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:
M.github/workflows/generate-manifest.yml | 12++++++------
Dgenerate_image_manifest.py | 59-----------------------------------------------------------
Agenerate_resource_manifest.py | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dimage-manifest.json | 29-----------------------------
Mindex.html | 55+++++++++++++++++++++++++++++++++++++++++--------------
Aresource-manifest.json | 34++++++++++++++++++++++++++++++++++
Aresources/fullscreen.svg | 40++++++++++++++++++++++++++++++++++++++++
Msw.js | 29++++++++++++++++++++---------
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()) );