Repository navigation
Open research children as Lift or Zoom cards inside the root document - #479
Merged
Merged
Conversation
MaggieAppleton
force-pushed
the
lift-zoom
branch
2 times, most recently
from
October 11, 2026 09:33
247c419 to
15d9584
Compare
MaggieAppleton
enabled auto-merge (rebase)
October 11, 2026 09:33
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Wrap the root's sheet in a document stack with a child slot. Lift covers
the sheet below its tab row, which becomes one "Back to {root}" control;
Zoom and compact show a stack bar with Back and the path. The top bar and
Chat stay live while a child is open, and the header drops its child crumb.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Host the child layer in the root's document stack instead of overlaying the whole workspace. Lift draws a titled card under the peeking tab row; Zoom drops the title under the stack bar. The surface toggle and presence no longer close a Lift child, and child motion has its own tokens. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A cancelled close animation no longer leaves the child stuck closing, and the card keys on the child route so loading its channel does not remount it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Opening a research child grows its card from the clicked card while the root recedes and dims behind it; closing lands it back on the card's settled slot. Every move settles in-flight values first, so a reopen or close mid-gesture reverses from the current frame. Keep the root's socket from reconnecting when a child opens: the host's metadata callback changes with the route, which remounted the root editor and lost its scroll and opener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ersal checks Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nted Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The window Escape handler treated any editable target as inside the child, so typing in root Chat and pressing Escape pulled focus into the child, and Escape on a Chat control closed it. Only targets in the document stack (or outside the root workspace) now blur into or close the child. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Switching off Document hid every sign of the open child. The stack bar now stays on Decisions and Build; its child crumb becomes a button there that switches back to Document with the child still open, and Back still returns to the root. The root view steps down beneath the bar so nothing is covered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Unmounting the return button at the start of the 250ms close dropped focus to the body before it was restored to the opener. The button now stays mounted and focusable but disabled through the close; `inert` is avoided because Chromium blurs a focused element that becomes inert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The child motion tokens split the --page-slide-* group; they now follow it. The theme test also checks --child-scrim-dur and --child-scrim-lag-dur are declared exactly once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed data flow:
- document-workspace-host.tsx: the parent RoomWorkspace is now rendered
directly inside the anchored-child-host div rather than through a `parent`
variable. workspaceProps still returns only document/session data and
callbacks, with no className or style key. Its parent presentation now
carries the child layer node, a finite "open" | "closing" childState, a
finite "lift" | "zoom" motion from readChildMotionStyle, path labels, and
the close callback. The exact case is replaced and its stale text removed;
the child workspaceProps case is unchanged.
- workspace.tsx: the reviewed dynamic class sites are unchanged. SurfaceToggle
still applies motionContract("feedback").className to its two spans and the
Chat aside still uses chatPresence.className. The new document stack, stack
bar, tab-row return and child slot use static classes; the motion style only
reaches the DOM as data-child-motion.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI builds the client once and runs the browser suite with E2E_SKIP_BUILD=1, so the VITE_CHILD_MOTION_TOGGLE=on that scripts/e2e.ts sets for its own build never reached CI. The Zoom spec then ran under Lift and found no stack bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A parent and its child share one route layer. When a child finished revealing just after the reader went back to the parent, its resolution landed on the current layer and its canonical path replaced the parent URL, reopening the child. Each resolution now names the route it answered, and only one that answered the current route is published. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewed data flow: - document-workspace-host.tsx: the only change adds requestKey, the DocumentRouteIdentity of the route the load answered, to the resolution passed to onReady. It is a route-identity string used for stale-response filtering in DocumentRouteSwap and never reaches a className, style, or JSX prop. Both reviewed workspaceProps call sites and their returned keys are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaggieAppleton
force-pushed
the
lift-zoom
branch
from
October 11, 2026 09:59
15d9584 to
4c25975
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changes
Opening a research child now replaces only the root's document panel. The root's Chat stays put, because a child no longer has a Chat of its own: there is one Chat per root document. The child is only a document, with no tabs, header or Chat. The Document, Decisions and Build tabs and presence belong to the root.
There are two motion styles, behind a dev toggle (
localStorage["chopin:dev:child-motion"] = "lift" | "zoom", default Lift), so we can choose after using both:Compact (phone) layouts use the stack bar.
Motion uses the Web Animations API from the clicked card's rectangle:
Opening from the sidebar grows from the panel's lower centre.
Also fixed: opening a child reconnected the root document's socket, which remounted the root editor and lost its scroll.
Stacked on #465 (merged). This is slice 1 of the nested-documents work.
Screenshots
Lift, child open
Lift open motion, frames 2–4
Zoom, child open
Zoom open motion, frames 2–4
Lift with the root expanded
Compact (390px)
Accessibility
inert. Tab never reaches it, and the root's Chat stays reachable.Testing
e2e/research-child-motion.e2e.tssamples real frames. It checks:research-child-surfaceandresearch-child-recoveryfor the new presentation, with keyboard sweep, focus and close-path coverage.bun run e2epassed before the final restack, except two unrelated flakes that passed on rerun. After the restack,bun run types, the design-contract check and the unit tests pass.Design-contract review
Renewed in "Renew design-contract review for Lift and Zoom" after a data-flow review, with the maintainer's approval:
workspaceProps(…)indocument-workspace-host.tsxpasses only data and callbacks.workspace.tsxsites are unchanged reviewed classes.🤖 Generated with Claude Code