Skip to content

feat(runner): DEV-2027 show window-maximize in play, full mode without the editor - #117

Merged
demtario merged 2 commits into
feat/DEV-2027-redesignfrom
feat/DEV-2027-full-mode-play
Aug 5, 2026
Merged

feat(runner): DEV-2027 show window-maximize in play, full mode without the editor#117
demtario merged 2 commits into
feat/DEV-2027-redesignfrom
feat/DEV-2027-full-mode-play

Conversation

@demtario

@demtario demtario commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Why

window-maximize was gated on savedId, so it never appeared in play — no docs example, no starter, no anonymous visitor. It was not among ADR-0027's twelve recorded deviations, so it reads as a bug and has been re-filed as one more than once.

The frames disagree with the gate. 72:15697's preview bar draws book + github + window-maximize, and repoUrl is play-only (App.tsx), so by the app's own mapping that is a play-mode bar ending in maximize. 65:20432 "Mode Full" is drawn over Drag to scroll - Standard example — a docs example, which is always play.

Neither the frame names nor the URL text settle it, and both are easy to cite by mistake: "before login" is not "no id" (savedId is set in share too, so a signed-out visitor on /share/:id already got the button), and the placeholder …/share/14z4151i1l appears identically in a starter frame and a docs frame. The play-only icons are the evidence.

One question for design, non-blocking: both preview bars are auto-named Frame 48112801, so "one bar component reused across frames" is a live alternative reading. The plain reading is taken here because the icon is drawn in two independent play frames — but a line of confirmation would retire the question instead of leaving it for whoever reads the ADR next.

What

The button now renders in every mode, and full mode means the view without the editor. Its subject still differs by mode, which is the part that does depart from the frames:

  • edit / share — unchanged. fullModeId still routes to FullMode, which iframes the prebuilt /d/:id/ in a new tab. That is what the frames show and what the share dialog hands out, and it stays the cheap path: no bundler, no container, nothing to boot. It therefore shows the last build, not unsaved edits.
  • play — new. There is no artifact there, only the live preview already running, so EditorShell takes a fullMode flag: the sidebar, editor column and splitter are hidden, FullBar replaces PreviewBar, the top bar keeps theme toggle + Download (65:20458), and the example pill goes static per 65:21391's hidden chevron.

Hidden, not unmounted, and the distinction is the point of a toggle. The editor side sits under one display: contents wrapper that flips to display: nonecontents because the sidebar, the editor column and the splitter are grid items of s.body, which a real wrapper box would collapse into a single track. Unmounting was the first cut, and it discarded everything DEV-2169 keeps every tab mounted for: per-tab undo history, scroll offset and caret. display: none does leave CodeMirror with no layout, so props.fullMode joins the caret effect's deps for its requestMeasure() — a call that was already there as a precaution and is load-bearing now.

Entering it in play is history.replaceState plus state, not a navigation. window.open there would boot a second runtime, and for Tier 2 that is a second container session per click against a pool that already exhausts; in place, the session, its container and every unsaved edit survive the toggle. The URL stays shareable, and the deep link works because play state lives in the URL (?docs= / ?example= / ?v=) — moving it anywhere else breaks ?mode=full.

FullBar.onRefresh becomes optional, mirroring PreviewBar: in play it re-runs the live preview rather than re-keying an iframe.

Recorded as ADR-0027 §13, including the question it leaves for design — whether full mode over a saved demo should show the live workspace too. Answering "yes" collapses the two behaviours into one, at the cost of the share dialog's full-window link becoming a cold boot for the recipient.

Tests

e2e/full-mode.spec.ts is new. Nothing in e2e/ touched this button or the route before, which is why a control missing from an entire mode shipped unnoticed. Five tests: the button is visible in play; the toggle hides the editor side, shows FullBar, adds mode=full and does not re-mount the preview iframe (asserted with a DOM stamp, which a navigation would also wipe); minimize restores the editor and drops the param; a pasted ?mode=full link boots straight into full mode with a working way back; and the editor survives a trip through full mode, asserted by typing, toggling, returning and expecting Cmd+Z to still revert.

Watched every one fail first — the first four with element(s) not found on the maximize control, the undo one with the marker still present because the remounted view had an empty history.

Full suite: 59 passed, 0 failed, 90 skipped (the E2E_LIVE / starter-matrix ones). Unit: 77 pass. pnpm -r run typecheck clean across all four projects.

Two notes for whoever runs this locally:

  • The anonymous guards in authed-actions.spec.ts and sidebar-crud.spec.ts fail against any build that picks up apps/authoring/.env.local, which sets VITE_DEV_USER. Build with VITE_DEV_USER= or those two specs prove nothing. Not related to this change; it cost me one confused run.
  • EditorShell.tsx:415 contains a literal U+0000 inside a React key template, so git, grep and rtk grep all treat the file as binary — reading this PR's diff for that file needs --text, and a plain grep for a symbol in it silently returns nothing. Left alone here as unrelated, but it is worth its own one-line commit.

Verified visually at 1728×900 against 65:20432: pill + theme toggle + Download, FullBar with the italic Live preview placeholder and minimize at the right edge, full-width preview, status bar intact. The preview was mid-boot both before and after the toggle — which is itself evidence the toggle changed no runtime state.

🤖 Generated with Claude Code

…t the editor

The button was gated on `savedId`, so it never appeared in `play` — no docs
example, no starter, no anonymous visitor. The frames disagree: `72:15697`'s
preview bar draws book + github + window-maximize and `repoUrl` is play-only,
so that is a play-mode bar ending in maximize; `65:20432` "Mode Full" is drawn
over `Drag to scroll - Standard example`, a docs example, which is always
`play`.

Full mode is now the view without the editor rather than a separate route,
though its subject still differs by mode. `edit`/`share` are untouched:
`fullModeId` routes to `FullMode`, which iframes the prebuilt `/d/:id/` in a
new tab and stays the cheap path. `play` has no artifact, only the live
preview already running, so `EditorShell` takes a `fullMode` flag — sidebar,
editor column and splitter unmount, `FullBar` replaces `PreviewBar`, the top
bar keeps theme toggle + Download (`65:20458`), and the example pill goes
static per `65:21391`'s hidden chevron.

Entering it in `play` is `replaceState` plus state, not a navigation.
`window.open` there would boot a second runtime, and for Tier 2 a second
container session per click against a pool that already exhausts; in place,
the session, its container and every unsaved edit survive the toggle. The
deep link works because play state lives in the URL (`?docs=` / `?example=` /
`?v=`).

`FullBar.onRefresh` becomes optional, mirroring `PreviewBar` — in `play` it
re-runs the live preview instead of re-keying an iframe.

Nothing in `e2e/` covered this button or the route, which is why a control
missing from an entire mode shipped unnoticed; `e2e/full-mode.spec.ts` now
does, including that the toggle does not re-mount the preview iframe.

Recorded as ADR-0027 §13, with the one question it leaves for design: whether
full mode over a saved demo should show the live workspace too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3ec67fa. Configure here.

Comment thread runner/packages/editor-shell/src/EditorShell.tsx Outdated
… of unmounting it

Bugbot on #117 reported the visible half: `EditorStatusBar` kept its
pre-toggle `Ln, Col` after minimize, because the caret effect depends only on
`active` and `openPaths` and so never re-ran. The cause is bigger than the
readout — unmounting the panes discarded everything DEV-2169 keeps every tab
mounted for. Per-tab undo history, scroll offset and caret all went with them,
and coming back from full mode is meant to be a toggle.

The editor side now sits under one `display: contents` wrapper that flips to
`display: none`. `contents` because the sidebar, the editor column and the
splitter are grid items of `s.body`, which a real box would collapse into a
single track; `none` then takes the subtree out of layout, so the full-mode
form of `s.body` can stay a single track.

`display: none` does leave CodeMirror with no layout, so `props.fullMode` joins
the caret effect's deps for its `requestMeasure()`. That call was there as a
precaution against a `visibility: hidden` pane and is now load-bearing. The
readout itself needs no reset: nothing re-mounts and nothing moves the caret.

The two spec assertions that read `toHaveCount(0)` for the splitter and the
editor pane were asserting the unmount, which would have forbidden this fix;
they now assert invisibility. New test covers the cause rather than the
symptom — a stale readout and a correct one are the same number, so it types,
takes a trip through full mode and expects undo to still revert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@demtario
demtario merged commit b0fd795 into feat/DEV-2027-redesign Aug 5, 2026
3 checks passed
@demtario
demtario deleted the feat/DEV-2027-full-mode-play branch August 5, 2026 11:45
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.

1 participant