Drawing blocks render inline in the editor with a full-screen button #1103

Open
opened 2026-10-05 06:06:46 +00:00 by kayg · 32 comments
Owner

Owner decision (2026-10-05)

"Excalidraw should simply render in-line in the block editor as a block with a full-screen icon."

A Drawing block renders the Excalidraw scene inline in the block editor, at the block's width with a sensible height (resizable by dragging its bottom edge). It has a full-screen button that expands it to a full-screen editor with the same scene, closed with Done or Escape, with no separate Cancel/Done sheet for editing an existing drawing. Edits save through the existing Canvas persistence (no loss on reload, tab close or rename). Inline editing works on desktop and iPad, including Pencil. On a phone the inline block is read-only and full screen is the editing surface (DESIGN C10). Creating a new one (Draw action, /draw) inserts an empty Drawing block in place, focused for drawing. The inline block must not trap page scroll: scroll passes through unless the block is focused.

## Owner decision (2026-10-05) "Excalidraw should simply render in-line in the block editor as a block with a full-screen icon." A Drawing block renders the Excalidraw scene inline in the block editor, at the block's width with a sensible height (resizable by dragging its bottom edge). It has a full-screen button that expands it to a full-screen editor with the same scene, closed with Done or Escape, with no separate Cancel/Done sheet for editing an existing drawing. Edits save through the existing Canvas persistence (no loss on reload, tab close or rename). Inline editing works on desktop and iPad, including Pencil. On a phone the inline block is read-only and full screen is the editing surface (DESIGN C10). Creating a new one (Draw action, `/draw`) inserts an empty Drawing block in place, focused for drawing. The inline block must not trap page scroll: scroll passes through unless the block is focused.
Author
Owner

Starting #1101–#1103 on branch job/draw-1101, based on dev at df92d4da129bfead879d417aef6e0514fc56ee96 (production Canvas base). I will reproduce and fix #1101 first and commit it separately as the hotfix, then implement the Draw rename and inline Drawing block. No pushes, deploys, or merges.

Starting #1101–#1103 on branch `job/draw-1101`, based on `dev` at `df92d4da129bfead879d417aef6e0514fc56ee96` (production Canvas base). I will reproduce and fix #1101 first and commit it separately as the hotfix, then implement the Draw rename and inline Drawing block. No pushes, deploys, or merges.
Author
Owner

Finding: Notes currently open a separate SketchSheet from NoteEditorSurface.svelte, and the block embed plugin mounts CanvasPreview.svelte, which renders passive exported pixels and opens a Canvas route. There is no inline editable Drawing surface today. I will extend the existing Canvas renderer and event writer for the inline block.

Finding: Notes currently open a separate `SketchSheet` from `NoteEditorSurface.svelte`, and the block embed plugin mounts `CanvasPreview.svelte`, which renders passive exported pixels and opens a Canvas route. There is no inline editable Drawing surface today. I will extend the existing Canvas renderer and event writer for the inline block.
Author
Owner

Production finding (#1103): in canvas-draw-990.mjs, the 390 px Mac-emulated Chromium pass opened Draw in the Journal Composer, but the Drawing remained inline after render readiness. The Drawing could mount before the collaboration provider delivered edit permission, so the initial focus request was lost. I am gating the phone full-screen handoff on both renderer readiness and provider editability, then rerunning the production matrix.

Production finding (#1103): in `canvas-draw-990.mjs`, the 390 px Mac-emulated Chromium pass opened Draw in the Journal Composer, but the Drawing remained inline after render readiness. The Drawing could mount before the collaboration provider delivered edit permission, so the initial focus request was lost. I am gating the phone full-screen handoff on both renderer readiness and provider editability, then rerunning the production matrix.
Author
Owner

Production finding (#1103): the Journal Composer Drawing was connected and editable in Chromium. The production probe selected Rectangle and dragged on the Excalidraw canvas. The Drawing then held one pending Canvas event, but GET /api/v1/notes/<drawing-id> still returned an empty scene after 30 seconds. I am checking the rendered scene and server warnings before changing the shared Canvas writer.

Production finding (#1103): the Journal Composer Drawing was connected and editable in Chromium. The production probe selected Rectangle and dragged on the Excalidraw canvas. The Drawing then held one pending Canvas event, but GET `/api/v1/notes/<drawing-id>` still returned an empty scene after 30 seconds. I am checking the rendered scene and server warnings before changing the shared Canvas writer.
Author
Owner

Production finding (#1103): the writer re-entry fix is in the current production build, and the Mac-emulated matrix captured the Composer, slash menu, inline Drawing, and 390 px light full-screen states. The next 390 px dark Note stayed in the Drawing error state instead of becoming ready. The local server logged calternal_plugin_notes: Notes Index operation failed sqlite_code="5" pool_timeout=false during that interval; the browser probe saw no 5xx response. I classify this as a SLOW-only fixture failure under host load and am doing one more production pass after the 44 px touch-target update.

Production finding (#1103): the writer re-entry fix is in the current production build, and the Mac-emulated matrix captured the Composer, slash menu, inline Drawing, and 390 px light full-screen states. The next 390 px dark Note stayed in the Drawing error state instead of becoming ready. The local server logged `calternal_plugin_notes: Notes Index operation failed sqlite_code="5" pool_timeout=false` during that interval; the browser probe saw no 5xx response. I classify this as a SLOW-only fixture failure under host load and am doing one more production pass after the 44 px touch-target update.
Author
Owner

#1103 status: implementation is present in the worktree but is not committed. It replaces the Draw sheet with a shared-renderer Drawing block in Notes and the Journal Composer, makes phone embeds read-only with full-screen editing, keeps Canvas writes on the existing writer, adds resize persistence and Drawing actions, and adds production-flow and bench profiles.

The merged production build passed. The integrated pointer E2E is not green: selectEveryTool queries .App-toolbar [role="radio"], which finds zero controls in production and stops before pointer checks. The preceding attempt with labeled native radio inputs passed mouse selection and failed later on the keyboard assertion. The full Draw matrix E2E, bun run check, focused Vitest, and post-merge screenshot matrix were not completed. The job is beyond the four-hour timebox, so I am stopping here rather than iterating further.

READY FOR MERGE: no.

#1103 status: implementation is present in the worktree but is not committed. It replaces the Draw sheet with a shared-renderer Drawing block in Notes and the Journal Composer, makes phone embeds read-only with full-screen editing, keeps Canvas writes on the existing writer, adds resize persistence and Drawing actions, and adds production-flow and bench profiles. The merged production build passed. The integrated pointer E2E is not green: `selectEveryTool` queries `.App-toolbar [role="radio"]`, which finds zero controls in production and stops before pointer checks. The preceding attempt with labeled native radio inputs passed mouse selection and failed later on the keyboard assertion. The full Draw matrix E2E, `bun run check`, focused Vitest, and post-merge screenshot matrix were not completed. The job is beyond the four-hour timebox, so I am stopping here rather than iterating further. READY FOR MERGE: no.
Author
Owner

Decisions not specified in DESIGN: removing a Drawing from a Journal draft removes only the relationship and keeps its saved Canvas Note in Home; Undo restores the relationship. Full-screen editing moves the same Drawing block into the shared body overlay to clear parent scrims, then restores its inline position on Done or Escape.

Decisions not specified in DESIGN: removing a Drawing from a Journal draft removes only the relationship and keeps its saved Canvas Note in Home; Undo restores the relationship. Full-screen editing moves the same Drawing block into the shared body overlay to clear parent scrims, then restores its inline position on Done or Escape.
Author
Owner

Resuming #1103 on job/draw-1101 at 305fa27958. The worktree already includes origin/dev through merge parent 0eea1138b0. I am preserving the existing uncommitted Drawing block changes and will fix the toolbar selector and keyboard selection before running the requested UX flow, screenshots and gates.

Resuming #1103 on job/draw-1101 at 305fa279580760c1913134a5a0a576ea322f5a1c. The worktree already includes origin/dev through merge parent 0eea1138b03c5de3da6adf8eff1fbf2a998d5c76. I am preserving the existing uncommitted Drawing block changes and will fix the toolbar selector and keyboard selection before running the requested UX flow, screenshots and gates.
Author
Owner

Finding: the prior Playwright query used , but Excalidraw 0.18.1 renders each tool as a native (implicit radio role; no explicit role attribute). The selector therefore found zero tools. I changed the probe to use accessible radio names and native checked state, and added ArrowRight/ArrowLeft plus checks for keyboard tool selection.

Finding: the prior Playwright query used , but Excalidraw 0.18.1 renders each tool as a native (implicit radio role; no explicit role attribute). The selector therefore found zero tools. I changed the probe to use accessible radio names and native checked state, and added ArrowRight/ArrowLeft plus checks for keyboard tool selection.
Author
Owner

Finding: the prior Playwright query used .App-toolbar [role="radio"], but Excalidraw 0.18.1 renders each tool as a native input[type=radio][aria-label] (implicit radio role; no explicit role attribute). The selector therefore found zero tools. I changed the probe to use accessible radio names and native checked state, and added ArrowRight/ArrowLeft plus v checks for keyboard tool selection.

Finding: the prior Playwright query used `.App-toolbar [role="radio"]`, but Excalidraw 0.18.1 renders each tool as a native `input[type=radio][aria-label]` (implicit radio role; no explicit role attribute). The selector therefore found zero tools. I changed the probe to use accessible radio names and native checked state, and added ArrowRight/ArrowLeft plus `v` checks for keyboard tool selection.
Author
Owner

Finding: the Composer Drawing Undo control inherited .undo-btn's 24 px compact height and lacked the shared touch-hit target. This left removal Undo below the 44 px touch minimum. I added the shared target to both Drawing Undo actions and extended the production probe to check removal Undo and the touch targets.

Finding: the Composer Drawing Undo control inherited `.undo-btn`'s 24 px compact height and lacked the shared `touch-hit` target. This left removal Undo below the 44 px touch minimum. I added the shared target to both Drawing Undo actions and extended the production probe to check removal Undo and the touch targets.
Author
Owner

Finding: after selecting toolbar radios through their accessible names, production Chromium reproduces a keyboard defect. Focusing Selection and pressing ArrowRight checks Rectangle, but ArrowLeft leaves Rectangle checked and drops focus from the radio controls. The observed DOM contains native same-name tool radios. I am adding an explicit per-Canvas radio group with managed roving focus and will rerun the Chromium/WebKit probe.

Finding: after selecting toolbar radios through their accessible names, production Chromium reproduces a keyboard defect. Focusing Selection and pressing ArrowRight checks Rectangle, but ArrowLeft leaves Rectangle checked and drops focus from the radio controls. The observed DOM contains native same-name tool radios. I am adding an explicit per-Canvas radio group with managed roving focus and will rerun the Chromium/WebKit probe.
Author
Owner

#1103 finding: the screenshot flow expected Escape to dismiss the Composer immediately after Undo restored an inline Drawing. In the real production build focus remains inside Excalidraw, which consumes Escape for its editing behavior; the full-screen Escape-to-inline path is checked separately. The cleanup now clicks the Composer scrim and waits for the dialog to close, so the test exercises the actual close control without changing product behavior.

#1103 finding: the screenshot flow expected Escape to dismiss the Composer immediately after Undo restored an inline Drawing. In the real production build focus remains inside Excalidraw, which consumes Escape for its editing behavior; the full-screen Escape-to-inline path is checked separately. The cleanup now clicks the Composer scrim and waits for the dialog to close, so the test exercises the actual close control without changing product behavior.
Author
Owner

#1103 finding: in the 390 px production run, Undo after removing an inline Journal Drawing restored it in full-screen mode. The .drawing-block.is-fullscreen intercepted clicks intended for the Composer scrim, and the restore path set activeDrawId even though its comment says focus returns to Composer text. I am clearing that focus-on-mount request on Undo and adding a regression assertion that the Drawing returns inline.

#1103 finding: in the 390 px production run, Undo after removing an inline Journal Drawing restored it in full-screen mode. The `.drawing-block.is-fullscreen` intercepted clicks intended for the Composer scrim, and the restore path set `activeDrawId` even though its comment says focus returns to Composer text. I am clearing that focus-on-mount request on Undo and adding a regression assertion that the Drawing returns inline.
Author
Owner

#1103 test finding: the production screenshot matrix reused the same browser context for light and dark captures at each width. The first Composer flow persisted its draft, so the next capture added a second Drawing and the .last() locator followed the remaining block after removal. Composer screenshot captures now close their context after each case, keeping real local-storage drafts isolated.

#1103 test finding: the production screenshot matrix reused the same browser context for light and dark captures at each width. The first Composer flow persisted its draft, so the next capture added a second Drawing and the `.last()` locator followed the remaining block after removal. Composer screenshot captures now close their context after each case, keeping real local-storage drafts isolated.
Author
Owner

#1103 verification finding: during the 820 px production capture, the Notes editor stayed in “Connecting…” for more than 30 seconds. The server diagnostics showed Notes Index SQLite busy results and slow pool/checkpoint operations, with no 5xx response. I increased only the screenshot flow’s live-editor readiness timeout to 60 seconds so it can complete under this host load.

#1103 verification finding: during the 820 px production capture, the Notes editor stayed in “Connecting…” for more than 30 seconds. The server diagnostics showed Notes Index SQLite busy results and slow pool/checkpoint operations, with no 5xx response. I increased only the screenshot flow’s live-editor readiness timeout to 60 seconds so it can complete under this host load.
Author
Owner

#1103 verification finding: after many per-screenshot Note fixtures, the local SQLite-backed Notes index logged sqlite_code=5 busy failures and the next Drawing showed “This Drawing could not be opened.” The 820 px flow then had no ready block within 60 seconds. I reduced the production screenshot matrix to two reused real Notes (one for the slash menu and one for the inline/full-screen Drawing) and made embed captures wait for the durable Markdown write. No server 5xx occurred.

#1103 verification finding: after many per-screenshot Note fixtures, the local SQLite-backed Notes index logged `sqlite_code=5` busy failures and the next Drawing showed “This Drawing could not be opened.” The 820 px flow then had no ready block within 60 seconds. I reduced the production screenshot matrix to two reused real Notes (one for the slash menu and one for the inline/full-screen Drawing) and made embed captures wait for the durable Markdown write. No server 5xx occurred.
Author
Owner

#1103 verification finding: the production Note Canvas flow saved and reopened its Drawing after resize, but the next .drawing-block “Drawing actions” click did not expose a Copy link menu item within 30 seconds. The server reported only slow SQLite pool/checkpoint operations and no 5xx. I added menu DOM diagnostics and will isolate this flow from the screenshot matrix before deciding whether the app action or locator is at fault.

#1103 verification finding: the production Note Canvas flow saved and reopened its Drawing after resize, but the next `.drawing-block` “Drawing actions” click did not expose a `Copy link` menu item within 30 seconds. The server reported only slow SQLite pool/checkpoint operations and no 5xx. I added menu DOM diagnostics and will isolate this flow from the screenshot matrix before deciding whether the app action or locator is at fault.
Author
Owner

#1103 gate finding: after the required origin/dev merge, bun run check stopped at perf-lint --check. origin/dev itself has 21,977 entries in contracts/perf/exceptions.json but contracts/perf/ratchet.json still sets 21,964; per-rule ceilings are also over by one in 13 rules. Neither contract file differs from origin/dev in this job. I left the shared ratchet unchanged and am running the focused Vitest files separately.

#1103 gate finding: after the required `origin/dev` merge, `bun run check` stopped at `perf-lint --check`. `origin/dev` itself has 21,977 entries in `contracts/perf/exceptions.json` but `contracts/perf/ratchet.json` still sets 21,964; per-rule ceilings are also over by one in 13 rules. Neither contract file differs from `origin/dev` in this job. I left the shared ratchet unchanged and am running the focused Vitest files separately.
Author
Owner

Forgejo #1103 final report

Branch: job/draw-1101 (merged origin/dev once before final gates; no push or deploy).
Head: 912810cb1656e37827ea0bf6750c2fba62ce3caa.

Built

  • Inline Drawing blocks for Notes and the Journal Composer, with full-screen editing, Done/Escape return, a shared Canvas writer, per-user resize persistence, and phone read-only previews.
  • Toolbar tools use a roving-tabindex radio group. Drawing actions expose menu state and use the shared anchored menu.
  • The no-live-room Markdown fallback now creates the standard Canvas Note and inserts its stable embed reference with editor Undo.
  • Public Canvas load-error cleanup no longer calls the account-only keyboard helper.
  • Added the Drawing hot-path profile at bench/drawing-block-1103.mjs.

Changed files: apps/web/e2e/canvas-draw-990.mjs, apps/web/e2e/canvas-pointer-1101.mjs, apps/web/src/lib/canvas/CanvasReact.tsx, DrawingBlock.svelte, draw.ts, draw.test.ts, embeds.ts, embeds.test.ts, removed DrawSheet.svelte and draftProvider.ts/test, apps/web/src/lib/composer/Composer.svelte, its test and Drawing test stub, attachments.svelte.ts and its test, apps/web/src/lib/notes/NoteEditorSurface.svelte, NoteView.svelte, editorHost.ts, and bench/drawing-block-1103.mjs.

UX gaps closed

  • Mouse, touch, and keyboard tool selection; APG arrow-key movement and the Selection shortcut.
  • Done and Escape return the same Drawing inline. A saved Journal Drawing survives reload.
  • Note Drawing height resizes by keyboard and pointer and persists across reload.
  • Journal Copy link opens the same stable Canvas Note. Removing its Journal relationship keeps the Canvas in Home; Undo restores the relationship.
  • Captured 48 macOS-emulated production screenshots at 390, 820, and 1440 px in Paper White and Tokyo Night, including inline, slash-menu, full-screen, Journal Canvas, and 3× header/toolbar crops. Download the screenshot bundle. The individual files remain under ignored artifacts/canvas-draw-1103-final/.

UX gaps left

The full canvas-draw-990.mjs journey did not pass end to end. After reload in the Notes route, the Drawing actions trigger moved 46 px between pointer-down and pointer-up as the page scrolled; pointer-up landed on the Excalidraw toolbar, so Copy link did not open. A later run hit a Notes Index SQLITE_BUSY timeout while creating the slash embed. The Journal Copy link/removal path passes in canvas-pointer-1101.mjs. No 5xx response was observed. Re-run node e2e/canvas-draw-990.mjs from apps/web in the merge round to prove the Notes post-reload action and full journey.

Decisions not covered by DESIGN

  • Keep the internal composer.sketch shortcut ID for saved bindings; show Draw and /draw, with /sketch as a hidden alias.
  • Removing a Drawing from a Journal draft removes only its relationship. Keep the Canvas Note in Home; Undo restores the relationship.
  • Full-screen editing moves the same Drawing block into the shared body overlay and restores it inline on Done or Escape.
  • In the no-live-room fallback, create the Canvas Note first, then insert its stable Drawing reference into the Markdown editor. This keeps the fallback on the inline-block flow after removal of DrawSheet.

Verification

Production build:

✓ built in 1m 53s
  Wrote site to "build"
Compressed 887 static variants; saved 20887213 bytes.

bun run check stopped at the shared origin/dev perf-lint exception ratchet. Exact output:

$ ../../scripts/perf-lint --check && node scripts/check-user-storage.mjs && node scripts/check-glass-tokens.mjs && node scripts/check-type-tokens.mjs && node scripts/check-focus-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json
perf-lint: INVALID: exception ratchet: contract.blaze: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.blocked-network: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; contract.dom-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.model-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.row-identity: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.snapshot: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.profile: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.readiness: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; io.unresolved-call: 16191 exceptions exceed the ratchet ceiling 16190; fix the new violation instead; render.blaze-adapter: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_byte_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_row_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; total: ledger has 21977 exceptions; ratchet total is 21964

Direct Svelte diagnostics:

svelte-check found 0 errors and 4 warnings in 3 files

Focused Vitest:

 Test Files  4 passed (4)
      Tests  50 passed (50)
   Start at  17:58:32
   Duration  56.72s (transform 78%, tests 14%, import 5%, environment 3%, setup 1%)

Production Journal pointer/touch probe:

PASS chromium: title clearance, mouse/touch tools, Done, Escape, Copy link and removal
PASS webkit: title clearance, mouse/touch tools, Done, Escape and removal

git diff --check exited 0. No Rust files changed. The benchmark was not run because this is not a performance issue under the current verification policy. cargo clean completed: Removed 8626 files, 7.2GiB total.

Commits: e6e0a74c7, 58878d53a, ef67652e6, 34bfe78da, 912810cb1.

# Forgejo #1103 final report Branch: `job/draw-1101` (merged `origin/dev` once before final gates; no push or deploy). Head: `912810cb1656e37827ea0bf6750c2fba62ce3caa`. ## Built - Inline Drawing blocks for Notes and the Journal Composer, with full-screen editing, Done/Escape return, a shared Canvas writer, per-user resize persistence, and phone read-only previews. - Toolbar tools use a roving-tabindex radio group. Drawing actions expose menu state and use the shared anchored menu. - The no-live-room Markdown fallback now creates the standard Canvas Note and inserts its stable embed reference with editor Undo. - Public Canvas load-error cleanup no longer calls the account-only keyboard helper. - Added the Drawing hot-path profile at `bench/drawing-block-1103.mjs`. Changed files: `apps/web/e2e/canvas-draw-990.mjs`, `apps/web/e2e/canvas-pointer-1101.mjs`, `apps/web/src/lib/canvas/CanvasReact.tsx`, `DrawingBlock.svelte`, `draw.ts`, `draw.test.ts`, `embeds.ts`, `embeds.test.ts`, removed `DrawSheet.svelte` and `draftProvider.ts`/test, `apps/web/src/lib/composer/Composer.svelte`, its test and Drawing test stub, `attachments.svelte.ts` and its test, `apps/web/src/lib/notes/NoteEditorSurface.svelte`, `NoteView.svelte`, `editorHost.ts`, and `bench/drawing-block-1103.mjs`. ## UX gaps closed - Mouse, touch, and keyboard tool selection; APG arrow-key movement and the Selection shortcut. - Done and Escape return the same Drawing inline. A saved Journal Drawing survives reload. - Note Drawing height resizes by keyboard and pointer and persists across reload. - Journal Copy link opens the same stable Canvas Note. Removing its Journal relationship keeps the Canvas in Home; Undo restores the relationship. - Captured 48 macOS-emulated production screenshots at 390, 820, and 1440 px in Paper White and Tokyo Night, including inline, slash-menu, full-screen, Journal Canvas, and 3× header/toolbar crops. [Download the screenshot bundle](https://git.kayg.org/attachments/4a89ff56-f404-49f3-b962-de08d1de0b96). The individual files remain under ignored `artifacts/canvas-draw-1103-final/`. ## UX gaps left The full `canvas-draw-990.mjs` journey did not pass end to end. After reload in the Notes route, the Drawing actions trigger moved 46 px between pointer-down and pointer-up as the page scrolled; pointer-up landed on the Excalidraw toolbar, so Copy link did not open. A later run hit a Notes Index `SQLITE_BUSY` timeout while creating the slash embed. The Journal Copy link/removal path passes in `canvas-pointer-1101.mjs`. No 5xx response was observed. Re-run `node e2e/canvas-draw-990.mjs` from `apps/web` in the merge round to prove the Notes post-reload action and full journey. ## Decisions not covered by DESIGN - Keep the internal `composer.sketch` shortcut ID for saved bindings; show Draw and `/draw`, with `/sketch` as a hidden alias. - Removing a Drawing from a Journal draft removes only its relationship. Keep the Canvas Note in Home; Undo restores the relationship. - Full-screen editing moves the same Drawing block into the shared body overlay and restores it inline on Done or Escape. - In the no-live-room fallback, create the Canvas Note first, then insert its stable Drawing reference into the Markdown editor. This keeps the fallback on the inline-block flow after removal of DrawSheet. ## Verification Production build: ```text ✓ built in 1m 53s Wrote site to "build" Compressed 887 static variants; saved 20887213 bytes. ``` `bun run check` stopped at the shared `origin/dev` perf-lint exception ratchet. Exact output: ```text $ ../../scripts/perf-lint --check && node scripts/check-user-storage.mjs && node scripts/check-glass-tokens.mjs && node scripts/check-type-tokens.mjs && node scripts/check-focus-tokens.mjs && node scripts/check-motion-tokens.mjs && svelte-kit sync && svelte-check --tsconfig ./tsconfig.json perf-lint: INVALID: exception ratchet: contract.blaze: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.blocked-network: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; contract.dom-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.model-bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.row-identity: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; contract.snapshot: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.profile: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; coverage.readiness: 356 exceptions exceed the ratchet ceiling 355; fix the new violation instead; io.unresolved-call: 16191 exceptions exceed the ratchet ceiling 16190; fix the new violation instead; render.blaze-adapter: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.bound: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_byte_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; render.model_row_cap: 290 exceptions exceed the ratchet ceiling 289; fix the new violation instead; total: ledger has 21977 exceptions; ratchet total is 21964 ``` Direct Svelte diagnostics: ```text svelte-check found 0 errors and 4 warnings in 3 files ``` Focused Vitest: ```text Test Files 4 passed (4) Tests 50 passed (50) Start at 17:58:32 Duration 56.72s (transform 78%, tests 14%, import 5%, environment 3%, setup 1%) ``` Production Journal pointer/touch probe: ```text PASS chromium: title clearance, mouse/touch tools, Done, Escape, Copy link and removal PASS webkit: title clearance, mouse/touch tools, Done, Escape and removal ``` `git diff --check` exited 0. No Rust files changed. The benchmark was not run because this is not a performance issue under the current verification policy. `cargo clean` completed: `Removed 8626 files, 7.2GiB total`. Commits: `e6e0a74c7`, `58878d53a`, `ef67652e6`, `34bfe78da`, `912810cb1`.
Author
Owner

Orchestrator review of artifacts/canvas-draw-990 (head 912810cb1): send back.

  1. Leaked internals (820-light-note-drawing-inline.png): above the inline Drawing block the Note shows a link chip "Untitled canvas" with "%% # Excalidraw Data" under it. The embed reference must render ONLY as the Drawing block: no link chip, no file-format text, no Markdown comment markers. Check every surface that renders the embed line (Note editor, read preview, public share, search snippet, hover card, palette preview): none may show %% comments or "Excalidraw Data".
  2. Toolbar overflow: at 820 the drawing toolbar is clipped on the left (selection tool cut) and the right-side controls (lock, hand) are cut at the block edge. The inline block must fit its tools: wrap to two rows, switch to a compact tool set with a "More tools" overflow, or reduce to the essential tools inline and keep the full set in full screen. Nothing clipped at 390/820/1440.
  3. Header controls: "⤢Full screen" has no gap between icon and label; ⋯ and Full screen use different pill styles. Use the shared Pill/capsule with the standard icon–label gap; one capsule for ⋯ + Full screen, consistent with Note block actions (#1152).
  4. Empty bars: the undo/redo strip is a full-width empty panel (white band in light theme) plus an extra empty strip under the canvas. Inline: undo/redo sit as two small icon buttons in the toolbar row (or bottom-left floating pill), no full-width bar, no trailing empty strip; the block height is the drawing area plus the toolbar only.
  5. Retake the matrix (Journal composer, Note inline, full screen; 390/820/1440; light + dark) with 4x crops of toolbar edges. Geometry e2e: every tool button rect is inside the block rect; no element with text matching /%%|Excalidraw Data/ is visible.
    Merge origin/dev first (dev moved: batches 6–18). Commit; comment on #1103 with head SHA. Do not push.
Orchestrator review of artifacts/canvas-draw-990 (head 912810cb1): send back. 1. **Leaked internals** (820-light-note-drawing-inline.png): above the inline Drawing block the Note shows a link chip "Untitled canvas" with "%% # Excalidraw Data" under it. The embed reference must render ONLY as the Drawing block: no link chip, no file-format text, no Markdown comment markers. Check every surface that renders the embed line (Note editor, read preview, public share, search snippet, hover card, palette preview): none may show `%%` comments or "Excalidraw Data". 2. **Toolbar overflow**: at 820 the drawing toolbar is clipped on the left (selection tool cut) and the right-side controls (lock, hand) are cut at the block edge. The inline block must fit its tools: wrap to two rows, switch to a compact tool set with a "More tools" overflow, or reduce to the essential tools inline and keep the full set in full screen. Nothing clipped at 390/820/1440. 3. **Header controls**: "⤢Full screen" has no gap between icon and label; ⋯ and Full screen use different pill styles. Use the shared Pill/capsule with the standard icon–label gap; one capsule for ⋯ + Full screen, consistent with Note block actions (#1152). 4. **Empty bars**: the undo/redo strip is a full-width empty panel (white band in light theme) plus an extra empty strip under the canvas. Inline: undo/redo sit as two small icon buttons in the toolbar row (or bottom-left floating pill), no full-width bar, no trailing empty strip; the block height is the drawing area plus the toolbar only. 5. Retake the matrix (Journal composer, Note inline, full screen; 390/820/1440; light + dark) with 4x crops of toolbar edges. Geometry e2e: every tool button rect is inside the block rect; no element with text matching /%%|Excalidraw Data/ is visible. Merge origin/dev first (dev moved: batches 6–18). Commit; comment on #1103 with head SHA. Do not push.
Author
Owner

Started #1103 on job/draw-1101 at head 912810cb1656e37827ea0bf6750c2fba62ce3caa, based on merge base f2a34dd6e9372cdd2c77c7b41c32e2c9a7a096b2. I am tracing Drawing rendering, toolbar layout, and the existing geometry e2e before changing the block surfaces.

Started #1103 on `job/draw-1101` at head `912810cb1656e37827ea0bf6750c2fba62ce3caa`, based on merge base `f2a34dd6e9372cdd2c77c7b41c32e2c9a7a096b2`. I am tracing Drawing rendering, toolbar layout, and the existing geometry e2e before changing the block surfaces.
Author
Owner

Finding (reproduced from the existing production capture artifacts/canvas-draw-990/820-light-note-drawing-inline.png): the Canvas embed renders as a Untitled canvas chip with %% # Excalidraw Data beneath it; the inline tool row extends past the Drawing block; undo/redo sits in a full-width panel and an empty resize strip adds height below the canvas. I am addressing these in the Notes embed renderer and Drawing block layout.

Finding (reproduced from the existing production capture `artifacts/canvas-draw-990/820-light-note-drawing-inline.png`): the Canvas embed renders as a `Untitled canvas` chip with `%% # Excalidraw Data` beneath it; the inline tool row extends past the Drawing block; undo/redo sits in a full-width panel and an empty resize strip adds height below the canvas. I am addressing these in the Notes embed renderer and Drawing block layout.
Author
Owner

The post-merge production browser matrix found a remaining inline toolbar overflow at 820 px. assertDrawingLayout measured the Selection tool at x=307.5–351.5 while the Drawing block begins at x=327.8. I updated the toolbar flex levels to allow intrinsic tool rows to shrink and wrap within the block; rerunning the production build and matrix now.

The post-merge production browser matrix found a remaining inline toolbar overflow at 820 px. `assertDrawingLayout` measured the Selection tool at x=307.5–351.5 while the Drawing block begins at x=327.8. I updated the toolbar flex levels to allow intrinsic tool rows to shrink and wrap within the block; rerunning the production build and matrix now.
Author
Owner

Search and preview review found that Note snippets were rendered directly by splitSnippet and markdownSnippet, so text-only surfaces could show the Canvas reference or its %% # Excalidraw Data block. Commit 279d1f739 extends the shared Canvas text projection and applies it to search rows, hover previews and palette previews. Regression coverage checks both snippet render paths and preserves fenced code examples. Focused result: 4 files passed, 39 tests passed.

Search and preview review found that Note snippets were rendered directly by `splitSnippet` and `markdownSnippet`, so text-only surfaces could show the Canvas reference or its `%% # Excalidraw Data` block. Commit 279d1f739 extends the shared Canvas text projection and applies it to search rows, hover previews and palette previews. Regression coverage checks both snippet render paths and preserves fenced code examples. Focused result: 4 files passed, 39 tests passed.
Author
Owner

The Copy link regression check reaches the stable /n/<Canvas ID> route, but the standalone page stays in “Opening Canvas…” for 60 seconds. At timeout, the DOM reports data-canvas-state="loading" and data-canvas-collab="connected"; there are no page errors or pending requests, and the note API request was aborted. The 24 requested screenshot states and each toolbar geometry/source-text assertion completed before this check. I am recording the route stall as a remaining gap rather than changing unrelated Notes collaboration behavior in this issue.

Screenshot matrix: 95 production screenshots, including 4× toolbar edge crops

The Copy link regression check reaches the stable `/n/<Canvas ID>` route, but the standalone page stays in “Opening Canvas…” for 60 seconds. At timeout, the DOM reports `data-canvas-state="loading"` and `data-canvas-collab="connected"`; there are no page errors or pending requests, and the note API request was aborted. The 24 requested screenshot states and each toolbar geometry/source-text assertion completed before this check. I am recording the route stall as a remaining gap rather than changing unrelated Notes collaboration behavior in this issue. Screenshot matrix: [95 production screenshots, including 4× toolbar edge crops](https://git.kayg.org/attachments/5528e4c0-eb52-4667-aa5c-237892b6c462)
Author
Owner

draw3-1101 final report

Head: 835dd2e5490fee8867e70b8609b424a9dabba85c on job/draw-1101 (not pushed).

Built

  • Drawing embeds now render without the source Note chip, Markdown %% comments, or Excalidraw Data text in the Notes read/editor paths, Public links, and Search snippets used by rows and previews. Stored Markdown stays unchanged.
  • Inline Canvas tools wrap inside the Drawing block at narrow widths. Undo and redo are small toolbar buttons; no empty strip remains under the canvas. The ⋯ and Full screen actions share one capsule with the standard icon/label gap.
  • The production e2e matrix emulates macOS and covers Journal composer, Note inline, full screen, 390/820/1440 px, light/dark, and 4× toolbar edge crops. All 24 screenshot-state layout/source-text assertions passed.

Files: apps/web/src/lib/canvas/DrawingBlock.svelte, scene.ts and scene.test.ts; apps/web/src/lib/composer/Composer.svelte; apps/web/src/lib/files/PublicLinkPage.svelte; apps/web/src/lib/notes/readPreview.ts and its test; apps/web/src/lib/search/snippet.ts and search-units.test.ts; apps/web/e2e/canvas-draw-990.mjs; exact live perf-pin updates in contracts/perf/{adoption-1058,exceptions,ratchet}.json.

Screenshots: 95 production screenshots, including toolbar edge crops

Gates

cd apps/web && bun run check exited 0. Output:

perf-lint: PASS; 0 violations; 22119 scoped exceptions
svelte-check found 0 errors and 4 warnings in 3 files

cd apps/web && bun run build exited 0. Output:

Compressed 893 static variants; saved 20934741 bytes.

Focused regression command (bunx vitest run src/lib/canvas/scene.test.ts src/lib/notes/readPreview.test.ts src/lib/search/search-units.test.ts src/lib/notes/inlineLinks.test.ts --maxWorkers=2) passed:

Test Files  4 passed (4)
Tests  39 passed (39)

The required full bun run test -- --maxWorkers=2 exited 1. Summary:

Test Files  4 failed | 260 passed (264)
Tests  4 failed | 1822 passed (1826)
error: script "test" exited with code 1

Failures are outside the changed files: date-formatting.test.ts flags existing direct date formatting in SearchPreview.svelte; font-weight-token.test.ts flags ModeIcon.svelte:49 (700); agenda.svelte.test.ts got +5 instead of +3; audioPlayback.svelte.test.ts found a null Play control. I kept every expectation unchanged.

The production e2e command captured all 24 requested states and passed the geometry and forbidden-source-text assertions, then exited 1 on the additional stable Copy link open:

TimeoutError: waitFor: Timeout 60000ms exceeded.
- waiting for locator('[data-canvas-state="ready"]') to be visible

At timeout the copied /n/<Canvas ID> page remained “Opening Canvas…” with collaboration connected; no page errors or pending requests were reported. I recorded this route stall separately on the issue. No Rust source changed, so no Rust gates were run. cargo clean removed 8633 files (7.2 GiB); apps/web/.svelte-kit/output and apps/web/build were removed.

UX gaps closed

  • Canvas source markers are hidden from the text projections named in the issue.
  • Tool controls stay within the block and the empty undo/redo bars are gone across the requested viewport/theme matrix.
  • Full screen and overflow actions use the shared capsule style.

UX gaps left

  • Opening a copied stable Canvas link can remain in Loading; this needs a Notes/Canvas route investigation.
  • The four unrelated full-suite failures listed above remain.

Decisions

DESIGN does not define how Excalidraw source lines should appear in Markdown-derived previews. I kept source data intact and filtered standalone .excalidraw references, %% comments, and cropped Excalidraw Data snippets only at the render boundary. DESIGN also does not prescribe narrow inline tool overflow; I let the existing Excalidraw tool row wrap within the block, retaining the full tool set.

# draw3-1101 final report **Head:** `835dd2e5490fee8867e70b8609b424a9dabba85c` on `job/draw-1101` (not pushed). ## Built - Drawing embeds now render without the source Note chip, Markdown `%%` comments, or `Excalidraw Data` text in the Notes read/editor paths, Public links, and Search snippets used by rows and previews. Stored Markdown stays unchanged. - Inline Canvas tools wrap inside the Drawing block at narrow widths. Undo and redo are small toolbar buttons; no empty strip remains under the canvas. The `⋯` and Full screen actions share one capsule with the standard icon/label gap. - The production e2e matrix emulates macOS and covers Journal composer, Note inline, full screen, 390/820/1440 px, light/dark, and 4× toolbar edge crops. All 24 screenshot-state layout/source-text assertions passed. **Files:** `apps/web/src/lib/canvas/DrawingBlock.svelte`, `scene.ts` and `scene.test.ts`; `apps/web/src/lib/composer/Composer.svelte`; `apps/web/src/lib/files/PublicLinkPage.svelte`; `apps/web/src/lib/notes/readPreview.ts` and its test; `apps/web/src/lib/search/snippet.ts` and `search-units.test.ts`; `apps/web/e2e/canvas-draw-990.mjs`; exact live perf-pin updates in `contracts/perf/{adoption-1058,exceptions,ratchet}.json`. **Screenshots:** [95 production screenshots, including toolbar edge crops](https://git.kayg.org/attachments/5528e4c0-eb52-4667-aa5c-237892b6c462) ## Gates `cd apps/web && bun run check` exited 0. Output: ```text perf-lint: PASS; 0 violations; 22119 scoped exceptions svelte-check found 0 errors and 4 warnings in 3 files ``` `cd apps/web && bun run build` exited 0. Output: ```text Compressed 893 static variants; saved 20934741 bytes. ``` Focused regression command (`bunx vitest run src/lib/canvas/scene.test.ts src/lib/notes/readPreview.test.ts src/lib/search/search-units.test.ts src/lib/notes/inlineLinks.test.ts --maxWorkers=2`) passed: ```text Test Files 4 passed (4) Tests 39 passed (39) ``` The required full `bun run test -- --maxWorkers=2` exited 1. Summary: ```text Test Files 4 failed | 260 passed (264) Tests 4 failed | 1822 passed (1826) error: script "test" exited with code 1 ``` Failures are outside the changed files: `date-formatting.test.ts` flags existing direct date formatting in `SearchPreview.svelte`; `font-weight-token.test.ts` flags `ModeIcon.svelte:49 (700)`; `agenda.svelte.test.ts` got `+5` instead of `+3`; `audioPlayback.svelte.test.ts` found a null Play control. I kept every expectation unchanged. The production e2e command captured all 24 requested states and passed the geometry and forbidden-source-text assertions, then exited 1 on the additional stable Copy link open: ```text TimeoutError: waitFor: Timeout 60000ms exceeded. - waiting for locator('[data-canvas-state="ready"]') to be visible ``` At timeout the copied `/n/<Canvas ID>` page remained “Opening Canvas…” with collaboration connected; no page errors or pending requests were reported. I recorded this route stall separately on the issue. No Rust source changed, so no Rust gates were run. `cargo clean` removed 8633 files (7.2 GiB); `apps/web/.svelte-kit/output` and `apps/web/build` were removed. ## UX gaps closed - Canvas source markers are hidden from the text projections named in the issue. - Tool controls stay within the block and the empty undo/redo bars are gone across the requested viewport/theme matrix. - Full screen and overflow actions use the shared capsule style. ## UX gaps left - Opening a copied stable Canvas link can remain in Loading; this needs a Notes/Canvas route investigation. - The four unrelated full-suite failures listed above remain. ## Decisions DESIGN does not define how Excalidraw source lines should appear in Markdown-derived previews. I kept source data intact and filtered standalone `.excalidraw` references, `%%` comments, and cropped `Excalidraw Data` snippets only at the render boundary. DESIGN also does not prescribe narrow inline tool overflow; I let the existing Excalidraw tool row wrap within the block, retaining the full tool set.
Author
Owner

Orchestrator review of artifacts/canvas-draw-990 after draw3 (leak fixed, toolbar wraps, header capsule good). Two items from the last review remain:

  1. Undo/redo strip: still a full-width empty panel at the bottom of the inline block (white band in light, dark band in dark: 820-light-note-drawing-inline.png, 1440-dark-composer-drawing.png). This is the Excalidraw default footer. In the INLINE block, hide Excalidraw's built-in footer (UIOptions / renderFooter / CSS for the footer island, per the installed Excalidraw version's API — check node_modules docs/types) and put Undo and Redo as two small icon buttons at the END of the top toolbar row (same button style as the tools, with tooltips "Undo ⌘Z" / "Redo ⇧⌘Z"). Full screen keeps the full Excalidraw UI. No empty band anywhere; the block ends right below the drawing area.
  2. Right-side island (lock / hand) sits flush against and is clipped by the block's right edge at 820. Inline: drop it (lock and hand are available in full screen), or inset it by the block padding so nothing touches the edge.
    Merge origin/dev (green), bun run check + bun run test must pass on your branch. Retake the inline shots at 390/820/1440 light + dark with zooms of the bottom edge and right edge. Commit; comment on #1103 with head SHA. Do not push.
Orchestrator review of artifacts/canvas-draw-990 after draw3 (leak fixed, toolbar wraps, header capsule good). Two items from the last review remain: 1. **Undo/redo strip:** still a full-width empty panel at the bottom of the inline block (white band in light, dark band in dark: 820-light-note-drawing-inline.png, 1440-dark-composer-drawing.png). This is the Excalidraw default footer. In the INLINE block, hide Excalidraw's built-in footer (UIOptions / `renderFooter` / CSS for the footer island, per the installed Excalidraw version's API — check node_modules docs/types) and put Undo and Redo as two small icon buttons at the END of the top toolbar row (same button style as the tools, with tooltips "Undo ⌘Z" / "Redo ⇧⌘Z"). Full screen keeps the full Excalidraw UI. No empty band anywhere; the block ends right below the drawing area. 2. **Right-side island** (lock / hand) sits flush against and is clipped by the block's right edge at 820. Inline: drop it (lock and hand are available in full screen), or inset it by the block padding so nothing touches the edge. Merge origin/dev (green), `bun run check` + `bun run test` must pass on your branch. Retake the inline shots at 390/820/1440 light + dark with zooms of the bottom edge and right edge. Commit; comment on #1103 with head SHA. Do not push.
Author
Owner

Starting #1103 on job/draw-1101 at head 835dd2e5490fee8867e70b8609b424a9dabba85c, based on origin/dev at merge-base d57992dea77a5c031b1494fe9e5b608505b707e8. I am checking Excalidraw 0.18.1 UI APIs and will replace the inline footer strip with top-row history controls, then verify the block edges across the requested screenshot matrix.

Starting #1103 on `job/draw-1101` at head `835dd2e5490fee8867e70b8609b424a9dabba85c`, based on `origin/dev` at merge-base `d57992dea77a5c031b1494fe9e5b608505b707e8`. I am checking Excalidraw 0.18.1 UI APIs and will replace the inline footer strip with top-row history controls, then verify the block edges across the requested screenshot matrix.
Author
Owner

Finding: installed @excalidraw/excalidraw is 0.18.1. Its UIOptions type has no footer flag or renderFooter prop; it does expose renderTopRightUI, while CSS defines separate desktop layer-ui__wrapper__footer and touch App-bottom-bar trees. I am using that supported top-right slot for two controls that delegate to Excalidraw’s native Undo/Redo buttons, preserving the upstream disabled state and history path. Scoped inline CSS hides both native footer trees and the lock/hand island; full screen keeps the native controls.

Finding: installed `@excalidraw/excalidraw` is 0.18.1. Its `UIOptions` type has no footer flag or `renderFooter` prop; it does expose `renderTopRightUI`, while CSS defines separate desktop `layer-ui__wrapper__footer` and touch `App-bottom-bar` trees. I am using that supported top-right slot for two controls that delegate to Excalidraw’s native Undo/Redo buttons, preserving the upstream disabled state and history path. Scoped inline CSS hides both native footer trees and the lock/hand island; full screen keeps the native controls.
Author
Owner

Finding: git fetch origin && git merge origin/dev merged successfully at f24ab01c6. The merge had a contracts/perf/ratchet.json conflict. I regenerated that file from the merged contracts/perf/exceptions.json; it now records 22,108 total entries and exact per-rule counts, matching the merged ledger. No guard value was set above its live count.

Finding: `git fetch origin && git merge origin/dev` merged successfully at `f24ab01c6`. The merge had a `contracts/perf/ratchet.json` conflict. I regenerated that file from the merged `contracts/perf/exceptions.json`; it now records 22,108 total entries and exact per-rule counts, matching the merged ledger. No guard value was set above its live count.
Author
Owner

Finished

Issue #1103 is implemented on job/draw-1101. Merged origin/dev at f24ab01c6 before verification. Head: 8e0f46aa87768cde99889b738539bf77279d210d.

Commits:

  • 3cbbdd9b4 — move inline Undo and Redo into the top toolbar and add layout/interaction coverage.
  • 8e0f46aa8 — measure visible Excalidraw controls correctly across responsive layouts.

The inline Excalidraw footer and mobile lock/hand island are hidden. Undo and Redo appear at the end of the toolbar with warm shortcut tooltips and invoke Excalidraw's native actions. Full screen retains its native responsive chrome. A Mac-platform screenshot archive with 36 PNGs (390/820/1440 light and dark, plus bottom/right and toolbar/history crops) is attached: canvas-draw-990-inline-review.zip.

Gates

bun run check:

perf-lint: PASS; 0 violations; 22109 scoped exceptions
svelte-check found 0 errors and 4 warnings in 3 files

bun run test --maxWorkers=2:

Test Files 265 passed (265)
Tests 1831 passed (1831)

The production build passed: Compressed 895 static variants; saved 20942008 bytes. The local screenshot server build passed: Finished dev profile [unoptimized + debuginfo] target(s) in 27m 13s. Cleanup passed: Removed 8633 files, 7.2GiB total.

The focused production browser run completed all six inline screenshot/layout checks. A later full-flow attempt timed out waiting for the Note Live status after SQLite calls and connection acquisition took over 15 seconds under concurrent host load. It did not reach the later persisted Undo/Redo and touch interaction checks. This is a slow-only verification gap; no API or Rust behavior changed.

UX gaps closed

  • Removed the empty inline footer band in both themes.
  • Removed the inline lock/hand island that touched the right edge.
  • Added labeled, shortcut-aware, touch-sized inline Undo/Redo controls and checks for keyboard activation.
  • Captured the inline view and edge crops at all three required widths and both themes with macOS shortcut rendering.

UX gaps left

The full real-server interaction flow did not finish under host load, so persisted Undo/Redo and touch interactions still need the merge-round run.

Decisions

  • Installed Excalidraw is 0.18.1. Its UIOptions has no footer switch or renderFooter, so inline mode uses scoped CSS to hide both responsive footer trees and the supported renderTopRightUI slot to host controls that delegate to native Undo/Redo buttons.
  • Excalidraw's mobile full-screen layout omits the desktop .undo-redo-buttons island. The responsive check requires native tool radios on mobile and the history island on desktop; CSS does not hide full-screen chrome.

For the merge round

Run the focused browser flow against the shared server and prove the persisted pointer, keyboard, and touch Undo/Redo interactions complete:

source /mnt/hdd/targets/shared-server/CURRENT
cd apps/web
bun e2e/canvas-draw-990.mjs
## Finished Issue #1103 is implemented on `job/draw-1101`. Merged `origin/dev` at `f24ab01c6` before verification. Head: `8e0f46aa87768cde99889b738539bf77279d210d`. Commits: - `3cbbdd9b4` — move inline Undo and Redo into the top toolbar and add layout/interaction coverage. - `8e0f46aa8` — measure visible Excalidraw controls correctly across responsive layouts. The inline Excalidraw footer and mobile lock/hand island are hidden. Undo and Redo appear at the end of the toolbar with warm shortcut tooltips and invoke Excalidraw's native actions. Full screen retains its native responsive chrome. A Mac-platform screenshot archive with 36 PNGs (390/820/1440 light and dark, plus bottom/right and toolbar/history crops) is attached: [canvas-draw-990-inline-review.zip](https://git.kayg.org/attachments/eb2a18fe-3ee9-4b23-9e76-23c3e6358f23). ## Gates `bun run check`: ```text perf-lint: PASS; 0 violations; 22109 scoped exceptions svelte-check found 0 errors and 4 warnings in 3 files ``` `bun run test --maxWorkers=2`: ```text Test Files 265 passed (265) Tests 1831 passed (1831) ``` The production build passed: `Compressed 895 static variants; saved 20942008 bytes.` The local screenshot server build passed: `Finished `dev` profile [unoptimized + debuginfo] target(s) in 27m 13s`. Cleanup passed: `Removed 8633 files, 7.2GiB total`. The focused production browser run completed all six inline screenshot/layout checks. A later full-flow attempt timed out waiting for the Note `Live` status after SQLite calls and connection acquisition took over 15 seconds under concurrent host load. It did not reach the later persisted Undo/Redo and touch interaction checks. This is a slow-only verification gap; no API or Rust behavior changed. ## UX gaps closed - Removed the empty inline footer band in both themes. - Removed the inline lock/hand island that touched the right edge. - Added labeled, shortcut-aware, touch-sized inline Undo/Redo controls and checks for keyboard activation. - Captured the inline view and edge crops at all three required widths and both themes with macOS shortcut rendering. ## UX gaps left The full real-server interaction flow did not finish under host load, so persisted Undo/Redo and touch interactions still need the merge-round run. ## Decisions - Installed Excalidraw is 0.18.1. Its `UIOptions` has no footer switch or `renderFooter`, so inline mode uses scoped CSS to hide both responsive footer trees and the supported `renderTopRightUI` slot to host controls that delegate to native Undo/Redo buttons. - Excalidraw's mobile full-screen layout omits the desktop `.undo-redo-buttons` island. The responsive check requires native tool radios on mobile and the history island on desktop; CSS does not hide full-screen chrome. ## For the merge round Run the focused browser flow against the shared server and prove the persisted pointer, keyboard, and touch Undo/Redo interactions complete: ```sh source /mnt/hdd/targets/shared-server/CURRENT cd apps/web bun e2e/canvas-draw-990.mjs ```
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal#1103
No description provided.