Skip to content

Inspector behind a switch; double-click zooms to fill; Space presents a board - #195

Open
Jing-yilin wants to merge 13 commits into
mainfrom
fullscreen-zoom
Open

Jing-yilin wants to merge 13 commits into
mainfrom
fullscreen-zoom

Conversation

@Jing-yilin

@Jing-yilin Jing-yilin commented Sep 26, 2026 •

Copy link
Copy Markdown
Contributor

What

  • The inspector is behind a switch.
    • A button on the canvas strip, right of the background swatch, turns the inspector on and off. It is off by default.
    • While on, main's whole panel shows the one board or picture selected: layers, styles, tokens, assets and comments.
    • A click on a board that is already selected picks the element under the pointer.
    • The panel follows the selection rather than keeping its own, so closing it (× or Esc) clears the selection.
    • InspectorPanel, the in-board agent, the asset index and svgSignature are back as they were on main.
  • Hover outline. With the switch off, the selected board still outlines the element under the pointer.
  • A double-click zooms to fill. Double-clicking a board, an image, a video or any shape tldraw can't edit zooms it to fill the canvas. A second double-click on it shows the whole page. Notes and text still enter editing on a double-click. This lives in installDoubleClickZoom in canvasClicks.ts.
  • Space presents the selected board over the window. Space, Esc or any click closes it.
  • Deep links. ?canvas=<slug>#<file> selects that board and zooms to fill. Selecting a shape replaces the address rather than pushing a history entry.
  • The reasoning is in docs/2026-09-26-inspector-switch.md.

Checked

  • tsc -b, oxlint and bun run test pass (235 passed, 1 skipped).
  • In a browser on the dev server:
    • The switch opens the panel on the selected board.
    • Clicking the Log In button on the board picks it (div.t.c, 393×17).
    • The Assets tab names assets/icons/chevron-back.svg.
    • Turning the switch off closes the panel.

🤖 Generated with Claude Code

…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>
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Deploying super-prototyping with  Cloudflare Pages  Cloudflare Pages

Latest commit: af9a54b
Status: ✅  Deploy successful!
Preview URL: https://603361c2.super-prototyping.pages.dev
Branch Preview URL: https://fullscreen-zoom.super-prototyping.pages.dev

View logs

Jing-yilin and others added 4 commits September 26, 2026 14:11
…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>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Jing-yilin and others added 2 commits September 27, 2026 05:15
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>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment thread canvas/src/App.tsx
Comment on lines +1089 to +1095
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

Jing-yilin and others added 2 commits September 29, 2026 05:05
…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>
@Jing-yilin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment thread canvas/src/CanvasPresent.tsx Outdated
Comment on lines +25 to +30
const typing =
e.target instanceof HTMLElement &&
(e.target.isContentEditable ||
e.target.tagName === "INPUT" ||
e.target.tagName === "TEXTAREA");
if (typing || editor.getEditingShapeId()) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 9494c8e: Space is left to a focused input, button, link, select or role=button/menuitem/tab.

Jing-yilin and others added 3 commits September 29, 2026 05:14
…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>
@Jing-yilin Jing-yilin changed the title Drop the inspector; double-click zooms to fill Inspector behind a switch; double-click zooms to fill; Space presents a board Sep 29, 2026
@Jing-yilin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment thread canvas/src/App.tsx
/** What the inspector shows while it is on: the one board or picture selected. */
const inspected = useValue(
"inspected",
() => asCanvasTarget(editor?.getOnlySelectedShape() ?? undefined),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

@devin-ai-integration devin-ai-integration 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.

Devin Review found 5 potential issues.

Devin Review

Comment thread canvas/src/App.tsx
Comment on lines +1155 to +1159
/** What the inspector shows while it is on: the one board or picture selected. */
const inspected = useValue(
"inspected",
() => asCanvasTarget(editor?.getOnlySelectedShape() ?? undefined),
[editor],

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔴 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +132 to +136
const src =
shape.type === "image" && shape.props.assetId
? editor.getAsset(shape.props.assetId)?.props.src
: undefined;
return src ? <img alt="" src={src} /> : null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 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.

Devin Review


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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +218 to +228
{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>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment thread canvas/src/App.tsx
Comment on lines +977 to +985
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;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Inline the single-use URL helper

shapeName has one caller, write. Review rules ask for one-use helpers to be inlined.

Devin Review


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>
@Jing-yilin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment thread canvas/src/ChatPanel.tsx
Comment on lines +343 to +344
const show = (e: React.MouseEvent<HTMLAnchorElement>) => {
e.preventDefault();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

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