commit c17babb3c3d19387997ec4facd2c71892e9d23ee
parent db8077ce080685293216ccd8a9a41bbd5c6e4234
Author: Hunter
Date: Wed, 16 Jul 2025 13:13:13 -0400
move fullscreen icon to right of preview pane; use SVG icon
Diffstat:
8 files changed, 211 insertions(+), 117 deletions(-)
diff --git a/.github/workflows/generate-manifest.yml b/.github/workflows/generate-manifest.yml
@@ -1,9 +1,9 @@
-name: Generate Image Manifest
+name: Generate Resource Manifest
on:
push:
branches: [ main ]
- paths: [ 'images/**' ] # Only run when images are added/changed
+ paths: [ 'images/**', 'resources/**' ] # Run when images or resources are added/changed
workflow_dispatch: # Allow manual trigger
jobs:
@@ -23,8 +23,8 @@ jobs:
with:
python-version: '3.x'
- - name: Generate image manifest
- run: python generate_image_manifest.py
+ - name: Generate resource manifest
+ run: python generate_resource_manifest.py
- name: Check for changes
id: verify-changed-files
@@ -40,6 +40,6 @@ jobs:
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
- git add image-manifest.json
- git commit -m "Auto-update image manifest [skip ci]"
+ git add resource-manifest.json
+ git commit -m "Auto-update resource manifest [skip ci]"
git push
\ No newline at end of file
diff --git a/generate_image_manifest.py b/generate_image_manifest.py
@@ -1,58 +0,0 @@
-#!/usr/bin/env python3
-"""
-Generate image manifest for PWA caching
-Scans the images/ directory and creates a JSON manifest file
-"""
-
-import os
-import json
-from pathlib import Path
-
-def generate_image_manifest():
- """Generate a JSON manifest of all images in the images/ directory"""
-
- # Define supported image extensions
- image_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp'}
-
- # Get the directory where this script is located
- script_dir = Path(__file__).parent
- images_dir = script_dir / 'images'
-
- # Check if images directory exists
- if not images_dir.exists():
- print(f"Images directory not found: {images_dir}")
- return
-
- # Collect all image files
- image_files = []
-
- for file_path in images_dir.iterdir():
- if file_path.is_file() and file_path.suffix.lower() in image_extensions:
- # Create relative path from web root (GitHub Pages subdirectory)
- relative_path = f"/web_workshop/images/{file_path.name}"
- image_files.append(relative_path)
-
- # Sort for consistent output
- image_files.sort()
-
- # Create manifest object
- manifest = {
- "images": image_files,
- "generated_at": "auto-generated by GitHub Actions",
- "total_images": len(image_files)
- }
-
- # Write manifest file
- manifest_path = script_dir / 'image-manifest.json'
- with open(manifest_path, 'w', encoding='utf-8') as f:
- json.dump(manifest, f, indent=2, sort_keys=True)
-
- print(f"Generated manifest with {len(image_files)} images:")
- for img in image_files:
- print(f" - {img}")
-
- return manifest
-
-if __name__ == "__main__":
- manifest = generate_image_manifest()
- print(f"\nManifest saved to: image-manifest.json")
-\ No newline at end of file
diff --git a/generate_resource_manifest.py b/generate_resource_manifest.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+"""
+Generate resource manifest for PWA caching
+Scans the images/ and resources/ directories and creates a JSON manifest file
+"""
+
+import os
+import json
+from pathlib import Path
+
+def generate_resource_manifest():
+ """Generate a JSON manifest of all resources in the images/ and resources/ directories"""
+
+ # Define supported image extensions
+ image_extensions = {'.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.ico', '.bmp'}
+
+ # Get the directory where this script is located
+ script_dir = Path(__file__).parent
+ images_dir = script_dir / 'images'
+ resources_dir = script_dir / 'resources'
+
+ # Collect all image files
+ image_files = []
+ if images_dir.exists():
+ for file_path in images_dir.iterdir():
+ if file_path.is_file() and file_path.suffix.lower() in image_extensions:
+ # Create relative path from web root (GitHub Pages subdirectory)
+ relative_path = f"/web_workshop/images/{file_path.name}"
+ image_files.append(relative_path)
+
+ # Collect ALL resource files (no extension filtering)
+ resource_files = []
+ if resources_dir.exists():
+ for file_path in resources_dir.rglob('*'):
+ if file_path.is_file():
+ # Create relative path from web root (GitHub Pages subdirectory)
+ relative_path = f"/web_workshop/resources/{file_path.relative_to(resources_dir).as_posix()}"
+ resource_files.append(relative_path)
+
+ # Sort for consistent output
+ image_files.sort()
+ resource_files.sort()
+
+ # Create manifest object
+ manifest = {
+ "images": image_files,
+ "resources": resource_files,
+ "generated_at": "auto-generated by GitHub Actions",
+ "total_images": len(image_files),
+ "total_resources": len(resource_files),
+ "total_files": len(image_files) + len(resource_files)
+ }
+
+ # Write manifest file
+ manifest_path = script_dir / 'resource-manifest.json'
+ with open(manifest_path, 'w', encoding='utf-8') as f:
+ json.dump(manifest, f, indent=2, sort_keys=True)
+
+ print(f"Generated manifest with {len(image_files)} images and {len(resource_files)} resources:")
+ for img in image_files:
+ print(f" - {img}")
+ for res in resource_files:
+ print(f" - {res}")
+
+ return manifest
+
+if __name__ == "__main__":
+ manifest = generate_resource_manifest()
+ print(f"\nManifest saved to: resource-manifest.json")
+\ No newline at end of file
diff --git a/image-manifest.json b/image-manifest.json
@@ -1,28 +0,0 @@
-{
- "generated_at": "auto-generated by GitHub Actions",
- "images": [
- "/web_workshop/images/bigN.gif",
- "/web_workshop/images/bright_idea.gif",
- "/web_workshop/images/calamar.gif",
- "/web_workshop/images/cd.gif",
- "/web_workshop/images/email.gif",
- "/web_workshop/images/gaia.gif",
- "/web_workshop/images/hint.gif",
- "/web_workshop/images/mac.png",
- "/web_workshop/images/mario64.gif",
- "/web_workshop/images/pastel_square.png",
- "/web_workshop/images/pika_construction.gif",
- "/web_workshop/images/pink_clock.gif",
- "/web_workshop/images/rollerskate.png",
- "/web_workshop/images/scared_mouse.gif",
- "/web_workshop/images/smiley_dancing.gif",
- "/web_workshop/images/smiling_pizza.gif",
- "/web_workshop/images/spinninbrain.gif",
- "/web_workshop/images/splat.png",
- "/web_workshop/images/sun.png",
- "/web_workshop/images/underconstruction.gif",
- "/web_workshop/images/welcome.gif",
- "/web_workshop/images/windows.gif"
- ],
- "total_images": 22
-}
-\ No newline at end of file
diff --git a/index.html b/index.html
@@ -23,7 +23,6 @@
--editor-bg: #16181b;
}
-
* {
margin: 0;
padding: 0;
@@ -54,20 +53,38 @@
.fullscreen-toggle {
position: absolute;
top: 5px;
- left: 5px;
- z-index: 1000;
+ right: 5px;
+ z-index: 10000;
background: rgba(0, 0, 0, 0.2);
color: white;
border: none;
border-radius: 4px;
- padding: 5px 8px;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
cursor: pointer;
- font-size: 16px;
transition: background-color 0.2s;
+ box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
+ }
+
+ .fullscreen-toggle.has-scrollbar {
+ right: calc(5px + env(scrollbar-width, 15px));
}
.fullscreen-toggle:hover {
- background: rgba(0, 0, 0, 0.5);
+ background: rgba(0, 0, 0, 0.35);
+ }
+
+ .fullscreen-toggle img {
+ filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.3));
+ opacity: 0.8;
+ transition: opacity 0.2s;
+ }
+
+ .fullscreen-toggle:hover img {
+ opacity: 1;
}
.preview-pane.fullscreen {
@@ -80,11 +97,6 @@
background: white;
}
- .preview-pane.fullscreen .fullscreen-toggle {
- top: 20px;
- left: 20px;
- }
-
/* Hide editor when preview is fullscreen */
.editor-pane.hidden {
display: none;
@@ -171,7 +183,9 @@
</div>
<div class="preview-pane">
- <button class="fullscreen-toggle" id="fullscreenToggle" title="Toggle fullscreen">⛶</button>
+ <button class="fullscreen-toggle" id="fullscreenToggle" title="Toggle fullscreen">
+ <img src="resources/fullscreen.svg" width="20" height="20" alt="Fullscreen">
+ </button>
<iframe id="preview"></iframe>
</div>
@@ -192,12 +206,10 @@
if (isFullscreen) {
previewPane.classList.add('fullscreen');
editorPane.classList.add('hidden');
- toggleButton.textContent = '⛶';
toggleButton.title = 'Exit fullscreen';
} else {
previewPane.classList.remove('fullscreen');
editorPane.classList.remove('hidden');
- toggleButton.textContent = '⛶';
toggleButton.title = 'Toggle fullscreen';
}
}
@@ -207,6 +219,19 @@
toggleButton.addEventListener('click', toggleFullscreen);
});
+ function updateScrollbarDetection() {
+ const toggleButton = document.getElementById('fullscreenToggle');
+ try {
+ if (preview.contentDocument && preview.contentDocument.body) {
+ const body = preview.contentDocument.body;
+ const hasScrollbar = body.scrollHeight > preview.clientHeight;
+ toggleButton.classList.toggle('has-scrollbar', hasScrollbar);
+ }
+ } catch (e) {
+ // Ignore cross-origin errors
+ }
+ }
+
function updatePreview() {
const code = editorView.state.doc.toString();
@@ -244,6 +269,8 @@
} catch (e) {
// Ignore cross-origin errors
}
+ // Check for scrollbar after content is rendered
+ updateScrollbarDetection();
}, 10);
}
diff --git a/resource-manifest.json b/resource-manifest.json
@@ -0,0 +1,33 @@
+{
+ "generated_at": "auto-generated by GitHub Actions",
+ "images": [
+ "/web_workshop/images/bigN.gif",
+ "/web_workshop/images/bright_idea.gif",
+ "/web_workshop/images/calamar.gif",
+ "/web_workshop/images/cd.gif",
+ "/web_workshop/images/email.gif",
+ "/web_workshop/images/gaia.gif",
+ "/web_workshop/images/hint.gif",
+ "/web_workshop/images/mac.png",
+ "/web_workshop/images/mario64.gif",
+ "/web_workshop/images/pastel_square.png",
+ "/web_workshop/images/pika_construction.gif",
+ "/web_workshop/images/pink_clock.gif",
+ "/web_workshop/images/rollerskate.png",
+ "/web_workshop/images/scared_mouse.gif",
+ "/web_workshop/images/smiley_dancing.gif",
+ "/web_workshop/images/smiling_pizza.gif",
+ "/web_workshop/images/spinninbrain.gif",
+ "/web_workshop/images/splat.png",
+ "/web_workshop/images/sun.png",
+ "/web_workshop/images/underconstruction.gif",
+ "/web_workshop/images/welcome.gif",
+ "/web_workshop/images/windows.gif"
+ ],
+ "resources": [
+ "/web_workshop/resources/fullscreen.svg"
+ ],
+ "total_files": 23,
+ "total_images": 22,
+ "total_resources": 1
+}
+\ No newline at end of file
diff --git a/resources/fullscreen.svg b/resources/fullscreen.svg
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ width="14"
+ height="14"
+ viewBox="0 0 14 14"
+ version="1.1"
+ id="svg11"
+ sodipodi:docname="fullscreen.svg"
+ inkscape:version="1.2.1 (9c6d41e, 2022-07-14)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs15" />
+ <sodipodi:namedview
+ id="namedview13"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="0.25"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ showgrid="false"
+ inkscape:zoom="9.8333333"
+ inkscape:cx="-9.1525424"
+ inkscape:cy="7.0169492"
+ inkscape:window-width="1309"
+ inkscape:window-height="404"
+ inkscape:window-x="0"
+ inkscape:window-y="38"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg11" />
+ <path
+ d="M 7,14 H 5 v 5 h 5 V 17 H 7 Z M 5,10 H 7 V 7 h 3 V 5 H 5 Z m 12,7 h -3 v 2 h 5 V 14 H 17 Z M 14,5 v 2 h 3 v 3 h 2 V 5 Z"
+ id="path9"
+ style="fill:#ffffff"
+ transform="translate(-5,-5)" />
+</svg>
diff --git a/sw.js b/sw.js
@@ -1,4 +1,4 @@
-const CACHE_NAME = 'web-workshop-v7';
+const CACHE_NAME = 'web-workshop-v8';
const urlsToCache = [
'/web_workshop/',
'/web_workshop/index.html',
@@ -45,14 +45,15 @@ async function cacheDirectoryFiles(cache, directories) {
}
}
-// Function to cache all images using the generated manifest
-async function cacheImages(cache) {
+// Function to cache all resources using the generated manifest
+async function cacheResources(cache) {
try {
- const response = await fetch('/web_workshop/image-manifest.json');
+ const response = await fetch('/web_workshop/resource-manifest.json');
if (response.ok) {
const manifest = await response.json();
- console.log(`Caching ${manifest.total_images} images from manifest`);
+ console.log(`Caching ${manifest.total_images} images and ${manifest.total_resources} resources from manifest`);
+ // Cache images
for (const imagePath of manifest.images) {
try {
await cache.add(imagePath);
@@ -61,11 +62,21 @@ async function cacheImages(cache) {
console.log(`Failed to cache image ${imagePath}:`, e);
}
}
+
+ // Cache resources
+ for (const resourcePath of manifest.resources) {
+ try {
+ await cache.add(resourcePath);
+ console.log(`Cached resource: ${resourcePath}`);
+ } catch (e) {
+ console.log(`Failed to cache resource ${resourcePath}:`, e);
+ }
+ }
} else {
- console.log('No image manifest found, skipping image caching');
+ console.log('No resource manifest found, skipping resource caching');
}
} catch (e) {
- console.log('Failed to load image manifest:', e);
+ console.log('Failed to load resource manifest:', e);
}
}
@@ -88,8 +99,8 @@ self.addEventListener('install', event => {
}
}
- // Cache all images programmatically
- await cacheImages(cache);
+ // Cache all resources programmatically
+ await cacheResources(cache);
})
.then(() => self.skipWaiting())
);