The quiet room: a slide editor with the chrome taken away - #1
Merged
Conversation
…nd axis Three related pieces, all optional and all off the same model. Sections (src/deck/sections.ts). A section is a run of consecutive top-level slides, named on the first of them with data-section="Anatomy"; an empty value starts an unnamed seam. Where a deck declares nothing, a `break` slide and a run of slides sharing one .kicker are read as sections, so existing decks show some structure without being edited. Real decks have almost none of this today, so naming has to be easy: the map does it in one click. The compass (src/ui/Compass.ts) is always on screen at the bottom-left: one dot per top-level slide, a seam between sections, a descender under any slide that has a vertical stack, a hollow dot for a hidden one. It costs the space of a page number and shows what a thumbnail rail cannot — which slides have depth. Click a dot to go there, the label to open the map. Quiet mode (Q, remembered) hides the toolbar, navigator, inspector and status bar and gives the slide the window; the compass and a four-glyph cluster stay, and holding Space brings everything back while held. Nothing is behind a hamburger — there is simply almost nothing. The map (M, or the toolbar button) lays the deck out the way reveal walks it: sections left to right, stacks downward with the arrow drawn in. It is the only view that edits deck structure — reorder, name a section, hide a slide, and drop one slide under another to nest it. Cards are the thumbnails the navigator already renders, lazily. Editor.nestSlide / unnestSlide are new: nothing could move a slide into or out of a vertical stack before. Nesting wraps a plain target into a stack, keeping the section name on the wrapper where it belongs; promoting the second-to-last sub-slide unwraps the stack again. Both are single undoable deck-scope edits. Tests: 9 unit tests for the sections model, 11 e2e for the compass, quiet mode, the map, section naming and nesting/promoting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
… on demand The slides either side (src/ui/Neighbours.ts). Quiet mode insets the stage and puts the previous and next slides in the gutters — dimmed, titled, click to go there. They are thumbnails positioned off the stage's own rendered slide box (Stage.canvasClientRect through the iframe transform), so the three line up as one filmstrip at any window size and never creep over the slide. Three glyphs, bottom left: the compass, the map, present. The four-glyph cluster is gone; on the right there is now one button that raises a small palette — text, heading, list, image, shape, note for AI — with everything else one step behind it. Its dot turns amber when the file is unsaved or a note is waiting, which is the only state worth keeping on screen. The map can now build a deck, not just rearrange one: "+ slide" and "+ below" on every column (and one at the end), each asking for the title first, because a slide called "Heading" is a slide you have to come back to. Clicking a card's title renames it — writing to the slide's own heading, so the map, compass and navigator all agree. Six more e2e tests, including one that asserts the neighbours line up with the rendered slide and never overlap it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
Opening a deck opened the panelled editor, so the quiet room was something you had to know to ask for. It is now what the editor is: `lectern:quiet` defaults on, `Q` brings the toolbar, navigator and inspector back, and the preference is still remembered either way. For the first three openings a toast says where they went — quiet mode hides things a newcomer has no reason to know are there. The e2e helper turns quiet off, so the existing specs keep testing the panelled layout deliberately rather than by accident; one new spec asserts the default. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
It lived inside .lec-center, so the map overlay painted straight over it and the glyphs vanished exactly when you were navigating. It now sits at the app root above the overlay, and stays put in every view — the map included. Presenting is a separate tab, so nothing there to hide from. Bigger glyphs (21px), and the group says what it is: compass, map, present, and notes for AI with a count on it when something is waiting. The page number moved to the bottom right next to the tools button, where a page number belongs; the section name stays with the compass, since that is where you are rather than how far along. The map glyph was a molecule. It is now two slides side by side with a third below one of them and the connectors drawn — reveal's → and ↓, which is what the map shows. Fixes the compass measuring a shrink-wrapped ancestor to decide its own width (it now measures the window), which is the same circular-measurement bug as before and cost the dots entirely once the bar moved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
Clicking a note always appended a new comment, so a request you had just written could never be corrected — only argued with. The deck in public/examples shows the cost: a note reading "on the following slide add answers to these questions" followed by "put this slide below instead", because the author could not go back and say what they meant. Now: a note with no reply in it is a draft, and a click opens what you wrote with the caret at the end. Once there is a <p data-by="ai"> the thread is a conversation and a click adds to it as before — rewriting a request the assistant has already acted on would leave the thread telling a lie. Emptying a comment still deletes it, so a stray line can be peeled off by clearing it and clicking again. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
Three fixes. The background is off-white by default and a click on it — anywhere on the canvas that is not the slide, an object or a piece of furniture — switches to the dark room and back. Colours were already tokens, so the dark theme is one block that redefines them; the light values are now the base, which also means no flash of dark before the preference is read. Two colours were not tokens (the canvas dot grid and the neighbour labels) and would have been invisible or unreadable on white; both now use tokens. The neighbours sit on the axis you would actually travel to reach them. A slide below in a vertical stack was being shown to the RIGHT, which teaches the wrong deck: in reveal, → is the next section and ↓ is the next sub-slide. Left and right are now the previous and next top-level slides; a stack's slides are above and below, cropped to a strip that clears the bar and shows the part worth seeing — the kicker and heading of what comes next. Both carry their arrow. Double-clicking the surround used to drop an AI note onto the edge of the slide from a click that was not on the slide at all; it now ignores points outside it, which is also what makes a single background click unambiguous. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
The neighbouring slides were parked half off-screen, so what showed beside the slide was a couple of hundred pixels of another slide's empty margin — a pale slab that reads as a panel of chrome rather than as the next slide. Each peek is now cropped to a sliver at the window edge showing the edge that faces you: the end of the slide before, the start of the one after, the foot of the one above, the head of the one below. The quiet room's inset drops from 13% to 7% now that the gutters only have to hold a sliver, so the slide itself is about 15% larger. The compass moves to the top-left corner, on its own rather than at the head of the bottom bar; the map, present and notes glyphs stay bottom left and the page number stays bottom right. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
The pale field around the slide was the deck's own page background: the stage iframe spans the whole canvas, and reveal paints its viewport, so the deck's paper colour was the editor's background wherever the slide was not. Worse, a slide with data-background-color flooded the entire canvas — reveal's background layer is viewport-sized (measured: 1032x800 for a 991x557 slide). Now the deck's page background is read once at load and moved onto reveal's background layer, which is pinned to the slide canvas; the page inside the iframe is painted the editor's own room colour instead, and follows it when you switch between the light and dark room. So the canvas is one flat colour and the deck's colour appears only where a slide is — including a slide's own background. Painted inside the iframe rather than made transparent: an iframe paints its own white base even when its document is transparent, which is what was still showing through after the first attempt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
The quiet room's inset goes from 7% to 11% with a couple of percent top and bottom, so the slide is about a tenth smaller and the peeks grow with the space left over: the side slivers go from 78px to 105px on a 1440-wide window. The lower peek was being squeezed to 51px between the slide and the bar, because it kept a gap sized for the horizontal gutter and reserved a wide margin for the bar. Vertical gaps are now sized to the vertical room, and each side reserves only what it needs — 56px below for the bar, 10px above — which gives the peek below 76px instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
The notes glyph goes: it duplicated a state the tools button already carries (its dot turns amber when a note is waiting or the file is unsaved) and notes are still one item inside the palette. The tools button takes its place, so the bottom left now reads as what it is — where you can go, then everything else — and the bottom right is just the page number. Glyphs go from 21px to 26px, palette items to 20px. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
Hiding the chrome costs a newcomer the labels, so seven tips buy them back. The rule is that a tip appears when what it describes has become relevant — you selected something, you landed in a stack, you flipped the lights by accident — rather than all at once on arrival, which is a tour, and a tour is read once and remembered never. Two open the deck, because nothing on screen would ever prompt them, and the first is the one that matters: a double-click on an empty part of the slide leaves a note for the assistant. It says "an empty part" because a double-click on text edits the text, and the first thing a person tries should not surprise them. The second says where the panels went, replacing the toast that used to. They wait for the opening toast to clear, appear one at a time, are shown once ever, and can be replayed or switched off from the tools menu. The panelled view gets none: everything there is already labelled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
Double-click only made a note on empty canvas, so on a slide that is mostly text there was nowhere to put one — the double-click landed on a paragraph and edited it instead. The gesture was fine; requiring empty space was the bug. Three routes now, all placing the note where you point: - Right-click anywhere on the slide, text or not. The canvas had no context menu at all (the interaction layer declared the hook and nothing implemented it), so this also gives the canvas the menu it was missing: note here, edit, duplicate, delete, paste. - N, which now drops the note at the pointer rather than at a default spot. - Alt-double-click, for the same gesture over text. A plain double-click still edits text and still makes a note on empty canvas, so nothing that worked before works differently. The first tip now names the right-click as the answer for a full slide, and AGENTS.md leads with it, since an assistant reading a deck should know where the notes come from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
Double-click made a note only where it found no text, which on a full slide is nowhere. Both gestures now leave a note wherever you point, and right-click no longer opens a menu on the way — it just does it. The cost is deliberate: a double-click no longer edits text. Text is edited by selecting it and pressing Enter, or by typing over it, and the tip that appears on your first selection says so. That made a latent bug matter: entering text without a caret selected the whole element, so the next keystroke wiped the paragraph — survivable when double-click placed a caret, dangerous now that Enter is the way in. Entering without a caret now puts the caret at the end; a fresh placeholder still selects, so typing over a new "Text" object still replaces it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
The loop with the person now opens with the note gesture, and text editing is click-then-Enter everywhere it is described. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr
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.
The editor with its chrome taken away: the slide gets the window, and the few things that stay are the ones that tell you where you are.
Fourteen commits, each self-contained. Not necessarily merge-ready — it changes two documented gestures (below), and those are calls to make rather than assume.
What it adds
The quiet room (the default view;
Qbrings the panels back, holdspacefor a look). The slide fills the window, with the neighbouring slides showing as slivers at the edges — on the axis you would actually travel to reach them, since in reveal→is the next section and↓is the next sub-slide. An editor that shows a sub-slide off to the right teaches the wrong deck.The compass (top left): one dot per slide, a seam between sections, a descender under any slide that has a vertical stack, a hollow dot for a backup slide. It costs the space of a page number and shows the one thing a thumbnail rail cannot — which slides have depth underneath them.
The map (
M): the deck laid out the way reveal walks it, sections left to right and stacks downward with the arrows drawn in. The only view that edits deck structure: add and title slides, reorder, name sections, drop one card under another to nest it.Sections (
src/deck/sections.ts): a run of consecutive top-level slides, named on the first withdata-section="Anatomy". Where a deck declares nothing, abreakslide and a run of slides sharing one.kickerare read as sections, so existing decks show some structure without being edited. Checked against the example decks first: their kickers are per-slide captions, so kicker-runs alone would have produced eighteen sections of one slide each — hence the explicit attribute.Light and dark rooms: off-white by default, and a click on the background switches. The deck's own colour now appears only on slides — it used to fill the whole canvas, and a slide with
data-background-colorflooded the entire editor (reveal's background layer is viewport-sized: measured 1032×800 for a 991×557 slide).Tips: seven, each shown once, appearing when what they describe becomes relevant rather than as a tour on arrival. The first is the one that matters — that a double-click leaves a note for the assistant.
Behaviour changes worth a decision
lectern:quietdefaults on; the e2e helper turns it off so the existing specs keep testing the panelled layout deliberately.⏎edits text. Double-click used to make a note only where it found no text, which on a full slide is nowhere. This is the change I would scrutinise hardest: it trades the most conventional gesture in the editor for the one the tool is actually about.New editing APIs
Editor.nestSlide/unnestSlide— nothing could move a slide into or out of a vertical stack before;moveSlidepermutes top-level,moveSubSlidepermutes within one stack, and the navigator told you to go and edit the HTML. Nesting wraps a plain target into a stack, keeping the section name on the wrapper; promoting the second-to-last sub-slide unwraps it again. Single undoable deck-scope edits.Editor.insertNoteAt— a note wherever you point, shared by all three routes.<p data-by="ai">the thread is a conversation and a click adds to it, because rewriting a request that was already acted on would leave the thread telling a lie.Testing
100 unit and 64 e2e, including: the sections model; the compass, map and quiet mode; that a peek is under 20% of a slide's width so it can never grow back into a panel; that the
↑neighbour is genuinely above the slide and the←one beside it; that the iframe's page colour equals the editor's room colour in both rooms; and that nesting round-trips through undo byte-for-byte.Not included
The naturalisation example deck's own content work (its three pending AI notes, and two new slides on French culture) is in the working tree, not on this branch — that file has unrelated uncommitted changes.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Hm2Kw1FWoWQoWcPZ6BDsGr