Inspector behind a switch; double-click zooms to fill; Space presents a board - #195
Jing-yilin wants to merge 13 commits into
Conversation
…indow The right inspector panel is gone, along with the in-board agent, the asset index and the SVG signatures that only it used. Double-clicking a board, an image, a video or any shape tldraw can't edit now zooms it to fill the canvas, instead of cropping or starting a text shape. A #<file> deep link selects the board and zooms to it, and the address follows the selection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deploying super-prototyping with
|
| Latest commit: |
af9a54b
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://603361c2.super-prototyping.pages.dev |
| Branch Preview URL: | https://fullscreen-zoom.super-prototyping.pages.dev |
…tor left The selection reaction also depended on the selected shape's record, so dragging a selected shape rewrote the tab bar and localStorage every frame. Remove readCanvasImage and --sp-warn, which only the inspector read, correct the double-click comment, and record the decision in docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n comes back Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…creen On a Mac the full screen moved the window to a display of its own and back, slowly. An overlay in the window's own document covers the tab bar and chat as well, and appears at once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2ad706ab22
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }; | ||
| const resized = () => setView({ w: top.innerWidth, h: top.innerHeight }); | ||
| const windows = top === window ? [window] : [window, top]; | ||
| for (const w of windows) w.addEventListener("keydown", onDown, true); |
There was a problem hiding this comment.
Keep presentation dismissible after focusing the board
When a presented board is clicked, focus moves into the nested srcDoc iframe, but keyboard events do not cross iframe boundaries, so neither listener installed here receives the subsequent Space or Escape. Because the overlay has no close button or click-to-dismiss handler, an interactive board can leave the user unable to exit presentation using the documented keys until they manually refocus the outer document; prevent the preview iframe from taking focus or provide a dismissal mechanism inside/over it.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in ffd038d: the presented frame takes no pointer, so it never gets the focus, and a click anywhere closes it. Checked in the browser: click on the board closes; Space then Esc still close.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The overlay becomes Present, shared by the canvas and the chat. A click beside what it shows closes it as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 541a44cbaf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const stopSelection = react("selection in the address", () => { | ||
| const id = editor.getOnlySelectedShapeId(); | ||
| const first = selected === undefined; | ||
| if (id === selected) return; | ||
| selected = id; | ||
| if (first || applying) return; | ||
| write(false); |
There was a problem hiding this comment.
Restore click selection for locked library shapes
When a user single-clicks a board or library image, this selection watcher never runs because those shapes are locked and tldraw reports their clicks as canvas clicks; the removed InspectorClicks bridge was the code that explicitly handled that case, while selectLockedShapes only restores marquee selection. Consequently an ordinary board click leaves the prior selection and URL unchanged, and Space cannot present the clicked board unless the user first double-clicks or marquee-selects it. Add a click bridge that programmatically selects the locked target.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not reproduced. With selectLockedShapes tldraw hits locked shapes on pointer down (getHitShapeOnCanvasPointerDown, Idle), and a board is a filled box. Checked in the browser: one click selects a board, the address moves to #02-log-in, and Space presents it.
…focus Clicking the board put the focus in its frame, where Space and Esc are not heard, so it could not be closed from the keyboard. The frame now takes no pointer, and any click closes it, the board included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ffd038d0f1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const typing = | ||
| e.target instanceof HTMLElement && | ||
| (e.target.isContentEditable || | ||
| e.target.tagName === "INPUT" || | ||
| e.target.tagName === "TEXTAREA"); | ||
| if (typing || editor.getEditingShapeId()) return; |
There was a problem hiding this comment.
Preserve Space activation on focused controls
When a board or image remains selected and keyboard focus moves to a non-text control in the canvas document, such as a button in CanvasStrip, this capture listener still interprets Space as Present because it exempts only editable elements, inputs, and textareas. Preventing the default and stopping propagation means the focused control is not activated and the presentation overlay unexpectedly opens instead; gate this shortcut on canvas/editor focus or exempt buttons, links, selects, and other interactive controls.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 9494c8e: Space is left to a focused input, button, link, select or role=button/menuitem/tab.
…e on a control is the control's Dropping the inspector took its in-board agent, and with it the outline a board drew round each element as the pointer crossed it. The selected board now loads a scripted copy over itself that does only that, fed the canvas's pointer in board px, as the inspected board was. Space no longer presents a board while a button, link or other control has the focus. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
It used to put the camera back where it was before the first one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… by default The switch sits right of the ground swatch. While it is on, the panel shows the one board or picture selected: layers, styles, tokens, assets and comments, as on main. The selected board runs the inspect agent in place of the plain hover outline, and a click on it, once selected, picks the element under the pointer. The panel follows the selection rather than keeping its own, so the address, Space to present and the double-click zoom need no change. Closing it clears the selection. The asset index, readCanvasImage and the cover crop of a pointed element are back with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e93f0b039
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /** What the inspector shows while it is on: the one board or picture selected. */ | ||
| const inspected = useValue( | ||
| "inspected", | ||
| () => asCanvasTarget(editor?.getOnlySelectedShape() ?? undefined), |
There was a problem hiding this comment.
Keep the board selected on the first inspector Escape
When an element has been picked by clicking inside the selected board, pressing Escape reaches tldraw while its editor is focused, so tldraw clears the board selection before InspectorPanel's window-level handler clears its local element selection. Because inspected is now derived exclusively from getOnlySelectedShape(), that immediately unmounts the panel and agent frame; the documented two-step behavior of clearing the picked element first and closing the inspector on the next Escape is therefore lost for the normal click-to-pick flow. Intercept that first Escape before the editor handles it, or keep the inspector target independent of the tldraw selection while an inner element is selected.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in af9a54b: the panel's Escape listener runs in the capture phase, ahead of tldraw, and swallows the Escape that clears a picked element (not while a menu is open). Checked in the browser: pick an element, Esc clears the pick with the board still selected and the panel open; Esc again closes it.
| /** What the inspector shows while it is on: the one board or picture selected. */ | ||
| const inspected = useValue( | ||
| "inspected", | ||
| () => asCanvasTarget(editor?.getOnlySelectedShape() ?? undefined), | ||
| [editor], |
There was a problem hiding this comment.
🔴 Locked boards cannot open inspector by click
Clicking a locked board leaves inspected empty, so the switched-on inspector never opens for that board. Tldraw treats locked-shape clicks as canvas clicks; only marquee selection selects them.
Learn more
The inspector now derives its target solely from tldraw's single selection. Library boards and images are locked, and ordinary clicks on them do not select them; the previous installInspectorClicks explicitly handled pointer-down and pointer-up over locked shapes. Marquee selection can select locked shapes because selectLockedShapes is enabled, but normal clicks cannot. As a result, turning on the inspector and clicking a board leaves the panel empty.
Example: Select nothing, turn on Inspector, then click a locked 01-home board. The board is not selected and no layers or tokens appear; dragging a marquee around it does select it and opens the panel.
Recommended fix: Install a click handler for locked board and image shapes that calls editor.select after a matching, non-dragging primary-button release. Preserve normal unlocked-shape selection and the existing locked-link click handler.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const src = | ||
| shape.type === "image" && shape.props.assetId | ||
| ? editor.getAsset(shape.props.assetId)?.props.src | ||
| : undefined; | ||
| return src ? <img alt="" src={src} /> : null; |
There was a problem hiding this comment.
🟡 Cropped pictures present their original image
When a library picture is cropped, Presented shows its full asset instead of the selected shape's crop. Space displays different artwork from the canvas.
Learn more
The canvas uses tldraw's image rendering and stores crop metadata in the selected shape's props.crop. The presentation instead renders the source asset URL directly in an <img>, discarding both crop coordinates and circular-crop information. An unlocked library picture can therefore appear cropped on the canvas but show its entire source when presented.
Example: Unlock a brand picture, crop it to its left half, and press Space with that picture selected. The presentation shows the entire source, including the hidden right half.
Recommended fix: Render the shape's crop and mask in Presented, using its shape dimensions and props.crop, or export the selected image shape at presentation resolution with tldraw's image renderer.
Was this helpful? React with 👍 or 👎 to provide feedback.
| const on = hit?.id === board && editor.getCurrentToolId() === "select"; | ||
| const at = () => | ||
| editor.getPointInShapeSpace(hit!, editor.inputs.getCurrentPagePoint()); | ||
| if (info.name === "pointer_move") send(on ? at() : { x: -1, y: -1 }, false); |
There was a problem hiding this comment.
🟡 Hover outline remains after canvas exit
Moving from a selected board directly into the chat leaves its sp:at outline visible. installBoardHover clears it only on another editor pointer move, which never arrives after pointer exit.
Learn more
The selected board's injected script keeps its latest hover rectangle until it receives another sp:at coordinate. A pointer move over empty canvas sends (-1, -1) and clears it, but leaving the editor immediately after hovering a board produces no editor pointer move. The outline remains on the board while the pointer is over the chat or strip.
Example: Select a board, hover one of its buttons until the blue outline appears, then move directly into the chat panel. The blue outline stays on that button.
Recommended fix: Subscribe to pointerleave on the editor container in installBoardHover and send (-1, -1) to the active board's frame; remove the listener in the returned cleanup.
Was this helpful? React with 👍 or 👎 to provide feedback.
| {page && ( | ||
| <button | ||
| type="button" | ||
| className="sp-canvas-tabs-inspector" | ||
| aria-pressed={inspectorOn} | ||
| aria-label="Inspector" | ||
| title={inspectorOn ? "Hide the inspector" : "Inspect the selected board"} | ||
| onClick={() => setInspectorOn(!inspectorOn)} | ||
| > | ||
| <Inspect /> | ||
| </button> |
There was a problem hiding this comment.
🔍 Add an Unreleased entry for these controls
The new inspector switch, zoom gesture, and presentation mode have no entry under ## Unreleased. Contributing rules require one for user-visible changes in the same PR.
Was this helpful? React with 👍 or 👎 to provide feedback.
| function shapeName(editor: Editor, shape: TLShape | undefined) { | ||
| const target = asCanvasTarget(shape); | ||
| if (target?.type === CANVAS_FILE_SHAPE_TYPE) | ||
| return readCanvasLibrary() | ||
| .flatMap((c) => c.files) | ||
| .find((c) => c.path === target.props.path)?.fileName; | ||
| if (target) return canvasImageRef(target.id)?.file; | ||
| return shape && personsShape(editor, shape) ? shape.id : undefined; | ||
| } |
There was a problem hiding this comment.
🔍 Inline the single-use URL helper
shapeName has one caller, write. Review rules ask for one-use helpers to be inlined.
Was this helpful? React with 👍 or 👎 to provide feedback.
…board tldraw took that Escape first and deselected the board, and the panel follows the selection, so it closed instead. The panel now listens in the capture phase, ahead of tldraw, and swallows the Escape that clears a pick. Not while a menu is open. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: af9a54b1ab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const show = (e: React.MouseEvent<HTMLAnchorElement>) => { | ||
| e.preventDefault(); |
There was a problem hiding this comment.
Preserve modified-click navigation for chat images
When a user Ctrl-clicks or Cmd-clicks either kind of chat screenshot, this handler unconditionally prevents the anchor's default action, so the existing target="_blank" no longer opens the image in a new tab and the presentation overlay opens instead. Only intercept an unmodified primary-button click; otherwise leave the anchor's normal navigation behavior intact.
Useful? React with 👍 / 👎.
What
InspectorPanel, the in-board agent, the asset index andsvgSignatureare back as they were on main.installDoubleClickZoomincanvasClicks.ts.?canvas=<slug>#<file>selects that board and zooms to fill. Selecting a shape replaces the address rather than pushing a history entry.docs/2026-09-26-inspector-switch.md.Checked
tsc -b,oxlintandbun run testpass (235 passed, 1 skipped).div.t.c, 393×17).assets/icons/chevron-back.svg.🤖 Generated with Claude Code