Skip to content

animation pipeline phase D: 3 cross-window composed scenes - #39

Open
borght-dev wants to merge 3 commits into
animation-pipeline-Cfrom
animation-pipeline-D
Open

borght-dev wants to merge 3 commits into
animation-pipeline-Cfrom
animation-pipeline-D

Conversation

@borght-dev

Copy link
Copy Markdown
Owner

Summary

Phase D — three cross-window animations attempting to show what happens when the user clicks an action in one window that, in production, opens a separate Tauri window. Storybook can't open separate OS windows, so the second window mounts in the same canvas and slides in from the right via CSS when the trigger fires.

Depends on #38 (Phase C). Base animation-pipeline-C; auto-rebases as the chain merges.

What's in this PR

Story File Slug Result
Anim_OpenPrDetailWindow App.hero.stories.tsx cross-pr-detail-open.gif partial — sidebar renders correctly with PR list, but slide-in didn't fire on PR card click
Anim_OpenWorkItemDetailWindow components/work-items/CrossWindow.stories.tsx cross-work-item-detail-open.gif partial — same slide-in issue
Anim_OpenSqlWindowHotkey App.hero.stories.tsx cross-sql-hotkey.gif works — slide-in fires, SQL window appears

Plus new shared infrastructure in main-window-data.tsx:

  • SlideInCompositionFrame — generic two-column frame with the right column transformable.
  • CrossWindowShell — wraps SlideInCompositionFrame, exposes window.__BORGDOCK_SLIDE_OPEN__() so a story play function or invoke handler can trigger the slide.

Bug fixed in the chain

App.tsx re-fires loadSettings() on mount, which invoke('load_settings') — the static SETTINGS_BASELINE mock return overwrote the decorator's seed and the App rendered the SetupWizard. Static screenshots dodged this race; animations didn't. Fix: cross-window stories now seed load_settings as () => useSettingsStore.getState().settings so post-load matches pre-load.

This bug was latent in Hero_ReadmeMain too but timed out before the wizard could appear.

Known follow-ups

The slide-in animation for cross-pr-detail-open and cross-work-item-detail-open doesn't reliably trigger when the user clicks the trigger element (PR card / work item row). Production uses invoke('open_pr_detail_window') and invoke('open_workitem_detail_window'); the mock intercepts these but doesn't always fire the registered slide callback in time. The cross-sql-hotkey works because the play function calls window.__BORGDOCK_SLIDE_OPEN__() directly.

Two options for fixing:

  1. Update the play functions for the broken two to call the global directly (matching SQL).
  2. Investigate why invoke('open_pr_detail_window') doesn't reach the mock's slideOpenInvokeResponse callback.

The static composition shots already show both windows side-by-side (Hero_ReadmeMain), so the visual story is conveyed there. These animations would add motion polish.

Capture run

capture-screenshots: 32 written, 0 errors  (16 static + 16 animated)

Phase E note

Per the design spec, Phase E (global hotkey toggle) was tagged "may be punted." Given the genuine difficulty of cross-window animations, I lean toward closing the rollout here at 16 animations and treating Phase E as a future addition only if a clean approach surfaces. Up to you whether to merge this and stop, or push for E.

🤖 Generated with Claude Code

KoenvanderBorghtGomocha and others added 3 commits May 9, 2026 00:17
Adds the three cross-window animations from the spec — sidebar PR card
click → PR Detail window opens, work item palette → Work Item Detail
opens, sidebar → SQL hotkey opens SQL window. Each composes two window
apps in one canvas. The "second window opens" gesture is faked with a
CSS slide-in driven by the existing mock invoke layer: the production
invoke('open_*_window') call is intercepted by the story decorator,
which flips a state flag that animates the second window's transform
from translateX(105%) to translateX(0).

Adds SlideInCompositionFrame helper to main-window-data.tsx for the
shared composition shell.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
App.tsx fires loadSettings() in a useEffect on mount, which calls
invoke('load_settings'). The mock returned the static SETTINGS_BASELINE
(setupComplete=false) and overwrote the decorator's seeded settings,
causing App to render the SetupWizard instead of the PRs section.

Static screenshots dodged this race — animations couldn't.

Fix: invokeResponses.load_settings = () => useSettingsStore
.getState().settings for the cross-window stories — returns whatever
the seed currently has in the store, so post-load matches pre-load.

cross-pr-detail-open.gif now shows the real PR list (was wizard).
The slide-in still doesn't trigger reliably — tracked as follow-up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… animation

Per-feedback iteration on the canary cross-window animation. Three
changes addressed three separate bugs that combined to produce a
12-frame slideshow with no visible interaction:

1. The play function failed silently. `findByText(/storybook phase 12/i)`
   matched multiple elements (PR title AND branch label under the card),
   throwing TestingLibraryElementError before the click ever fired.
   Switched to `canvasElement.querySelectorAll('[data-pr-card]')` and
   indexed to position 1 (PR #42 — second in PRS_CANONICAL). The card
   title text appears in tooltip / aria nodes too, so text queries are
   inherently unreliable for this list.

2. Frame rate was capped at ~2 fps. Each `page.screenshot()` at
   1600×1000 with deviceScaleFactor=2 was effectively a 6.4-megapixel
   capture and took 200-500 ms. The script now creates a separate
   Playwright context with deviceScaleFactor=1 for the animation pass,
   roughly doubling the captured frame count for the large composed
   scenes and bringing simple stories (file palette etc.) up to the
   declared 12 fps target.

3. Cursor was invisible. Playwright's userEvent fires DOM events but
   never paints a cursor. Added `.storybook/anim-cursor.ts` with
   `installCursor` / `moveCursorTo` / `clickRipple` / `hoverAt`
   helpers — the cursor is a position:fixed SVG arrow with a 600 ms
   transition and a click-ripple effect, so the GIF reads as a real
   user interacting with the UI.

Plus a backdrop change: `SlideInCompositionFrame` now renders on a
soft purple radial gradient instead of solid #1a1a1a, with shadowed
rounded corners on each window. Reads as a desktop screenshot
rather than two windows floating on a black void.

The result for cross-pr-detail-open: 61 frames at 12 fps target,
rate ~5 fps actual, 623 KB, cursor visibly moves to PR #42, click
ripples, slide-in fires, both windows visible side-by-side. Other
animations also re-rendered with the new DSF=1 context (frame
counts doubled or tripled — most are now closer to declared fps).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

2 participants