Skip to content

Slice 18 — PWA + aspect-ratio setting (#19) - #39

Merged
cbaucom merged 6 commits into
mainfrom
feat/19-pwa-aspect-ratio
Jun 11, 2026
Merged

cbaucom merged 6 commits into
mainfrom
feat/19-pwa-aspect-ratio

Conversation

@cbaucom

@cbaucom cbaucom commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Closes #19

Approach

Aspect ratio (pure-module first): src/timeline-core/aspect.ts owns the decision — ASPECT_RATIOS (16:9 / 9:16 / 1:1) as the single source of truth, dimensionsForAspectRatio (1920×1080 / 1080×1920 / 1080×1080), DEFAULT_ASPECT_RATIO, and an isAspectRatio boundary guard. It is a project-level field in slideshow.json (per PRD: "aspect is a project setting"), not part of the settings cascade, so applying a theme never resets the canvas. PlayerPane lost its hardcoded COMP_WIDTH/COMP_HEIGHT and now takes compositionWidth/compositionHeight props; the composition itself was already dimension-parametric (percent-based Ken Burns, objectFit fit modes), which this slice proves.

PWA: public/manifest.webmanifest + generated PNG icons (scripts/generate-pwa-icons.mjs, rendered from favicon.svg) + a hand-rolled app-shell service worker (public/sw.js): network-first for navigations (cached shell as offline fallback), stale-while-revalidate for same-origin static assets. Media never touches the SW (blob: URLs) and cross-origin (Jamendo) is explicitly bypassed, so neither is ever cached. Registered in PROD builds only. Folder-permission persistence rides on the existing IndexedDB recent-projects + re-grant flow; installing makes Chromium persist the grant.

Acceptance criteria

  • Installable; folder reopens with ≤1 re-grant click — manifest (standalone, 192/512 icons incl. maskable) + active SW verified by smoke script; recent-projects re-grant flow unchanged, and installed PWAs get persistent folder permissions per Chromium policy.
  • 9:16 on an existing project plays portrait with cover/letterbox/blur-fill correct — smoke opens a project whose slideshow.json says 9:16, asserts the player is exactly 9:16, plays it, and cycles all three fit modes (screenshot below).
  • Offline → app shell loads; Jamendo gracefully unavailable — smoke sets the browser offline and reloads; shell renders from SW cache. Jamendo is never cached and its panel already surfaces fetch errors.

smoke screenshot

Smoke: pnpm build && pnpm preview, then node scripts/playwright-issue-19-smoke.mjs (PROD build required — SW registers in PROD only).

Code review (high effort): APPROVE, zero blocking findings

7 finder angles + verification. One confirmed bug was found and fixed in-branch: aspectRatio from slideshow.json was unvalidated, so a corrupted/hand-edited value (e.g. "4:3") reached dimensionsForAspectRatio's exhaustive switch and returned undefined, crashing the player — now guarded by isAspectRatio with fallback to the default. Notable refuted candidates: title-slide maxWidth: 1600 in portrait (a max-width wider than the container is inert; flex shrink-to-fit + word wrap), Player "remount" on dimension props (same element type/position; primitives), SW serving / for deep routes (single-route SPA, intended shell fallback).

Follow-ups discovered (not in this slice)

  • SW cache uses a static CACHE_NAME, so hashed assets from old deploys accumulate until manual eviction; consider build-stamped cache names or LRU trimming.
  • MockFileHandle/MockDirHandle/run() are now copy-pasted across three smoke scripts — extract a shared scripts/lib/ helper.
  • slidesToJson is up to six positional params; switch to an options object next time it grows.
  • RenderPlan doesn't carry canvas dimensions; the future export slice will need to thread them (today the Player gets them from the same slideshow.json source).

🤖 Generated with Claude Code

cbaucom and others added 6 commits June 11, 2026 13:49
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Service worker caches only the same-origin app shell (network-first
navigations, stale-while-revalidate assets). Media stays on blob: URLs
and Jamendo is cross-origin, so neither is ever cached. Registered in
PROD builds only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ASPECT_RATIOS is now the single source of truth for the union type and
the sidebar options; isAspectRatio guards untrusted file input so an
unsupported value falls back to the default instead of crashing the
player with undefined dimensions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cbaucom
cbaucom merged commit 767647d into main Jun 11, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Slice 18 — PWA + aspect-ratio setting

1 participant