fix(studio): storyboard polish — a11y, preview, and edit-race fixes (#1544)

Batched non-blocking review nits from the storyboard stack (#1528–#1532):

- StoryboardGrid: responsive auto-fill grid instead of fixed-360 tiles
- FramePoster: reset failed state when the poster target changes (stale-error fix)
- StoryboardFrameTile: status-chip aria-label
- StoryboardSourceEditor: marked({async:false}); save() in-flight guard;
  immediate first preview paint; [&_img] prose; scoped link-hardening
  (rel=noopener noreferrer + target=_blank) in the sanitizer
- StoryboardLoaded: memoize sourceFiles on data.script.path/.exists, not the object ref
- StoryboardFrameFocus: applyEdit in-flight guard; aria-pressed on status buttons;
  ←/→/Esc keyboard navigation
- ViewModeContext: correct the popstate/replaceState doc-drift

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
James Russo
2026-06-17 16:05:12 -07:00
committed by GitHub
co-authored by Claude Opus 4.8
parent c8fd16f2d3
commit e662fcdea2
7 changed files with 64 additions and 14 deletions
@@ -53,7 +53,12 @@ export function useViewModeState(enabled: boolean): ViewModeValue {
enabled ? readViewModeFromUrl() : "timeline",
);
// Reflect back/forward navigation and agent-driven URL changes.
// Reflect genuine browser back/forward between history entries with a different
// `?view=`. Note: our own writes use `replaceState` (below), which does NOT fire
// `popstate`, so this listener never sees them — `setViewMode` updates state directly.
// An agent deep-links by doing a full navigation to `?view=storyboard` (picked up by
// the mount-time read); a scripted `pushState`/`replaceState` to `?view=` would not be
// reflected here, by design.
useEffect(() => {
if (!enabled) return;
const onPopState = () => setMode(readViewModeFromUrl());