Craft-style block menu: Copy Link, Open as note, Remind Me #24

Open
opened 2026-09-14 09:24:29 +00:00 by kayg · 7 comments
Owner

Request

Craft-style trailing block menu in the note editor (user spec 2026-09-14, with reference screenshots from Craft).

While the editor is not focused (or a block is hovered/selected), each block shows a trailing circled ⋯ button aligned to the block's first line at the right edge of the reading column. Clicking it opens a native NSMenu anchored to the button:

  1. Copy Link to Block — copies the durable block link (same payload as the existing sidebar "Copy Link"), icon link.
  2. Open — creates a new note whose title (and slugified filename, via the existing H1-derived filename rules) is the block's plain text, opens it in the editor, and leaves the source block unchanged. Icon arrow.right.circle or doc.badge.plus.
  3. Remind Me… — submenu: In 20 minutes, In 1 hour, In 3 hours, Tomorrow, Next week, separator, Custom…. Schedules a local notification for this block (see the reminders issue for the backend). Icon alarm.

Delete / Turn Into… are not requested now.

Acceptance

  • ⋯ appears on hover of the block and while the block is selected; hidden otherwise; never draws a focus ring; respects Reduced Motion (no fade when reduced); VoiceOver exposes it as a button "Block actions" with the block text as value.
  • Menu is a native NSMenu with SF Symbols; keyboard reachable (Control-Return on a selected block opens it).
  • Copy Link places the same content the sidebar Copy Link does.
  • Open creates the note atomically via the existing note-creation path, never duplicates on double-click, and returns focus to the new note's editor.
  • Remind Me items call the reminders seam; until that issue lands they are present but disabled with a tooltip.
  • Scroll-spam metric holds; launch metric unaffected.
  • Keep open until verified in a delivered build on the VM and accepted by the user.
## Request Craft-style trailing block menu in the note editor (user spec 2026-09-14, with reference screenshots from Craft). While the editor is not focused (or a block is hovered/selected), each block shows a trailing circled `⋯` button aligned to the block's first line at the right edge of the reading column. Clicking it opens a native `NSMenu` anchored to the button: 1. **Copy Link to Block** — copies the durable block link (same payload as the existing sidebar "Copy Link"), icon `link`. 2. **Open** — creates a new note whose title (and slugified filename, via the existing H1-derived filename rules) is the block's plain text, opens it in the editor, and leaves the source block unchanged. Icon `arrow.right.circle` or `doc.badge.plus`. 3. **Remind Me…** — submenu: In 20 minutes, In 1 hour, In 3 hours, Tomorrow, Next week, separator, Custom…. Schedules a local notification for this block (see the reminders issue for the backend). Icon `alarm`. Delete / Turn Into… are not requested now. ## Acceptance - `⋯` appears on hover of the block and while the block is selected; hidden otherwise; never draws a focus ring; respects Reduced Motion (no fade when reduced); VoiceOver exposes it as a button "Block actions" with the block text as value. - Menu is a native `NSMenu` with SF Symbols; keyboard reachable (Control-Return on a selected block opens it). - Copy Link places the same content the sidebar Copy Link does. - Open creates the note atomically via the existing note-creation path, never duplicates on double-click, and returns focus to the new note's editor. - Remind Me items call the reminders seam; until that issue lands they are present but disabled with a tooltip. - Scroll-spam metric holds; launch metric unaffected. - Keep open until verified in a delivered build on the VM and accepted by the user.
Author
Owner

Addendum from user (2026-09-14): once a block is selected, the editor's right-click context menu must offer the same items as the ⋯ menu (Copy Link to Block, Open, Remind Me…). Merge with any existing context-menu items rather than duplicating (e.g. one Copy Link entry only, one source of truth for the item list shared by both menus).

Addendum from user (2026-09-14): once a block is selected, the editor's right-click context menu must offer the same items as the `⋯` menu (Copy Link to Block, Open, Remind Me…). Merge with any existing context-menu items rather than duplicating (e.g. one Copy Link entry only, one source of truth for the item list shared by both menus).
Author
Owner

Chunks 3-4 on wave19/block-editor-craft (pushed): 55c60ff shared block actions menu (trailing ellipsis.circle NSButton, one item builder for ⋯ and context menu, Control-Return presenter, Remind Me… disabled submenu with tooltip, single Copy Link to Block), d453228, 7390f6d Open as standalone note (NoteIdentity.preparingNoteDraft + frontmatter merge + coordinatedCreateIfAbsent no-replace loop; setOpenedNotePath after durable write; no day/event mutation), 3e6b058, fd46365, 63a011b list/task/heading marker normalisation for the title, 11a870d hosted App test runner.

VM evidence (build 63a011b, Space "Calternal Native Break 86ccc9c", note "A0 Block Editor and Style QA"): selecting "populated bullet" shows the circled ⋯ at the right edge of the row; right-click menu contains Duplicate Block / Delete Block / Copy Link to Block / Open / Remind Me… (disabled) / Toggle Raw Markdown merged with the native text items, exactly one Copy Link entry. Open created notes/populated-bullet-8c0f4ee7.md (frontmatter title/slug/id, # populated bullet, empty body) and navigated to it; the source note's list is byte-identical. First attempt on the list block failed with "Start the note with a title…" — fixed in 63a011b (hosted test covers list and task blocks).

Hosted tests: testBlockActionsMenuMatchesContextMenuItems 1/1, EventAttachmentDropTests.testOpeningBlockAsNoteCreatesStandaloneNoteWithoutMutatingDay 1/1 on agents@mbp. Reviewer pass in progress; delivery to kayg@mbp gated on chunk 1 (#4) acceptance.

Chunks 3-4 on `wave19/block-editor-craft` (pushed): `55c60ff` shared block actions menu (trailing `ellipsis.circle` NSButton, one item builder for ⋯ and context menu, Control-Return presenter, Remind Me… disabled submenu with tooltip, single Copy Link to Block), `d453228`, `7390f6d` Open as standalone note (NoteIdentity.preparingNoteDraft + frontmatter merge + coordinatedCreateIfAbsent no-replace loop; setOpenedNotePath after durable write; no day/event mutation), `3e6b058`, `fd46365`, `63a011b` list/task/heading marker normalisation for the title, `11a870d` hosted App test runner. VM evidence (build `63a011b`, Space "Calternal Native Break 86ccc9c", note "A0 Block Editor and Style QA"): selecting "populated bullet" shows the circled ⋯ at the right edge of the row; right-click menu contains Duplicate Block / Delete Block / Copy Link to Block / Open / Remind Me… (disabled) / Toggle Raw Markdown merged with the native text items, exactly one Copy Link entry. Open created `notes/populated-bullet-8c0f4ee7.md` (frontmatter title/slug/id, `# populated bullet`, empty body) and navigated to it; the source note's list is byte-identical. First attempt on the list block failed with "Start the note with a title…" — fixed in `63a011b` (hosted test covers list and task blocks). Hosted tests: `testBlockActionsMenuMatchesContextMenuItems` 1/1, `EventAttachmentDropTests.testOpeningBlockAsNoteCreatesStandaloneNoteWithoutMutatingDay` 1/1 on agents@mbp. Reviewer pass in progress; delivery to kayg@mbp gated on chunk 1 (#4) acceptance.
Author
Owner

VM real-event verification (patched cua-driver), build 1763820 on wave19/block-editor-craft, note "A0 Block Editor and Style QA" in the Overnight Perf QA fixture Space:

  • Hover (#4): PASS — hovered row (bullet, checkbox) slides right ~18 pt with the six-dot handle in the vacated space and the ⋯ at the right edge; other rows untouched; leaving resets; H1 no longer rewraps (18 pt trailing reserve keeps line widths constant). Evidence .agents/evidence/block-editor/hover-grid.png (earlier grids). Note: the reading column is now 18 pt narrower everywhere; long titles wrap slightly earlier than on dev.
  • Open (#24): PASS — ⋯ → Open on "populated checkbox" created notes/populated-checkbox-0faa52c9.md and rewrote the source block to - [ ] [populated checkbox](<../notes/populated-checkbox-0faa52c9.md>) (checkbox preserved), then navigated to the new note.
  • Copy Link to Block (#24): FAIL — the block ID is minted and persisted (^fb62aaec appended to the source line) but the pasteboard is unchanged and no error surfaces; failure is downstream in Viewer→Shell.copyLink→CopyCoordinator. Under investigation.
  • Earlier report of ordered items rendering as "po / pula…" was caused by the verification script pressing Return in the editor (fixture note corrupted on disk, since restored), not by the app.

Verification script: Scripts/vm/block_editor_check.py (hover geometry per row, clipboard, note/source diff). Menu items are activated via hover + Return because synthetic clicks on NSMenu items do not activate them through cua-driver.

VM real-event verification (patched cua-driver), build `1763820` on `wave19/block-editor-craft`, note "A0 Block Editor and Style QA" in the Overnight Perf QA fixture Space: - Hover (#4): PASS — hovered row (bullet, checkbox) slides right ~18 pt with the six-dot handle in the vacated space and the ⋯ at the right edge; other rows untouched; leaving resets; H1 no longer rewraps (18 pt trailing reserve keeps line widths constant). Evidence `.agents/evidence/block-editor/hover-grid.png` (earlier grids). Note: the reading column is now 18 pt narrower everywhere; long titles wrap slightly earlier than on dev. - Open (#24): PASS — ⋯ → Open on "populated checkbox" created `notes/populated-checkbox-0faa52c9.md` and rewrote the source block to `- [ ] [populated checkbox](<../notes/populated-checkbox-0faa52c9.md>)` (checkbox preserved), then navigated to the new note. - Copy Link to Block (#24): FAIL — the block ID is minted and persisted (`^fb62aaec` appended to the source line) but the pasteboard is unchanged and no error surfaces; failure is downstream in Viewer→Shell.copyLink→CopyCoordinator. Under investigation. - Earlier report of ordered items rendering as "po / pula…" was caused by the verification script pressing Return in the editor (fixture note corrupted on disk, since restored), not by the app. Verification script: `Scripts/vm/block_editor_check.py` (hover geometry per row, clipboard, note/source diff). Menu items are activated via hover + Return because synthetic clicks on NSMenu items do not activate them through cua-driver.
Author
Owner

Status at end of session 2026-09-14 (paused on user request; further refinements under supervision tomorrow):

Branch wave19/block-editor-craft @ bc3ace0 (pushed). Final reviewer round (agent findings) fixed in 302d99a (old draw-time caret/hit-test offsets removed; Open revalidates the block after the await; scroll-snap flag; ID-less hover rebinding), ad73794 (explicit -synthetic mode gate; navigation-generation guard restored; Open idempotent on an existing generated link; VaultSession copyNoteBlock unique-owner validation without the resolver re-entry deadlock), 790eb00, bc3ace0.

VM real-event verification of bc3ace0 with Scripts/vm/block_editor_check.py (root checkout): hover 7/7 PASS (slide +36 px on hovered row, handle in vacated space, other rows untouched, reset on leave), Copy Link PASS (canonical calternal://…/block/ on the pasteboard, ~5 s latency — the copy awaits an index refresh; worth shortening), Open: on this run the app navigated to a stale index entry (a previously created-then-deleted "populated checkbox" note) instead of creating a new note — needs a look tomorrow (index entry for a deleted note file; Open should verify the file exists or create anew). Known follow-ups: reading column is 18 pt narrower everywhere (trailing reserve); ordered-list marker misalignment is pre-existing (#17); XCUITest scenarios in App/UITests/BlockEditorCraftUITests.swift still fail at launch/editor waits and are not part of the verification gate (VM script is).

No build delivered to kayg@mbp beyond 63a011bd earlier today; next delivery after tomorrow's supervised round.

Status at end of session 2026-09-14 (paused on user request; further refinements under supervision tomorrow): Branch `wave19/block-editor-craft` @ `bc3ace0` (pushed). Final reviewer round (agent findings) fixed in `302d99a` (old draw-time caret/hit-test offsets removed; Open revalidates the block after the await; scroll-snap flag; ID-less hover rebinding), `ad73794` (explicit `-synthetic` mode gate; navigation-generation guard restored; Open idempotent on an existing generated link; VaultSession copyNoteBlock unique-owner validation without the resolver re-entry deadlock), `790eb00`, `bc3ace0`. VM real-event verification of `bc3ace0` with `Scripts/vm/block_editor_check.py` (root checkout): hover 7/7 PASS (slide +36 px on hovered row, handle in vacated space, other rows untouched, reset on leave), Copy Link PASS (canonical calternal://…/block/<id> on the pasteboard, ~5 s latency — the copy awaits an index refresh; worth shortening), Open: on this run the app navigated to a stale index entry (a previously created-then-deleted "populated checkbox" note) instead of creating a new note — needs a look tomorrow (index entry for a deleted note file; Open should verify the file exists or create anew). Known follow-ups: reading column is 18 pt narrower everywhere (trailing reserve); ordered-list marker misalignment is pre-existing (#17); XCUITest scenarios in App/UITests/BlockEditorCraftUITests.swift still fail at launch/editor waits and are not part of the verification gate (VM script is). No build delivered to kayg@mbp beyond `63a011bd` earlier today; next delivery after tomorrow's supervised round.
Author
Owner

Status at end of session 2026-09-14 (paused on user request; further refinements under supervision tomorrow):

Branch wave19/block-editor-craft @ bc3ace0 (pushed). Final reviewer round (agent findings) fixed in 302d99a (old draw-time caret/hit-test offsets removed; Open revalidates the block after the await; scroll-snap flag; ID-less hover rebinding), ad73794 (explicit -synthetic mode gate; navigation-generation guard restored; Open idempotent on an existing generated link; VaultSession copyNoteBlock unique-owner validation without the resolver re-entry deadlock), 790eb00, bc3ace0.

VM real-event verification of bc3ace0 with Scripts/vm/block_editor_check.py (root checkout): hover 7/7 PASS (slide +36 px on hovered row, handle in vacated space, other rows untouched, reset on leave), Copy Link PASS (canonical calternal://…/block/ on the pasteboard, ~5 s latency — the copy awaits an index refresh; worth shortening), Open: on this run the app navigated to a stale index entry (a previously created-then-deleted "populated checkbox" note) instead of creating a new note — needs a look tomorrow (index entry for a deleted note file; Open should verify the file exists or create anew). Known follow-ups: reading column is 18 pt narrower everywhere (trailing reserve); ordered-list marker misalignment is pre-existing (#17); XCUITest scenarios in App/UITests/BlockEditorCraftUITests.swift still fail at launch/editor waits and are not part of the verification gate (VM script is).

No build delivered to kayg@mbp beyond 63a011bd earlier today; next delivery after tomorrow's supervised round.

Status at end of session 2026-09-14 (paused on user request; further refinements under supervision tomorrow): Branch `wave19/block-editor-craft` @ `bc3ace0` (pushed). Final reviewer round (agent findings) fixed in `302d99a` (old draw-time caret/hit-test offsets removed; Open revalidates the block after the await; scroll-snap flag; ID-less hover rebinding), `ad73794` (explicit `-synthetic` mode gate; navigation-generation guard restored; Open idempotent on an existing generated link; VaultSession copyNoteBlock unique-owner validation without the resolver re-entry deadlock), `790eb00`, `bc3ace0`. VM real-event verification of `bc3ace0` with `Scripts/vm/block_editor_check.py` (root checkout): hover 7/7 PASS (slide +36 px on hovered row, handle in vacated space, other rows untouched, reset on leave), Copy Link PASS (canonical calternal://…/block/<id> on the pasteboard, ~5 s latency — the copy awaits an index refresh; worth shortening), Open: on this run the app navigated to a stale index entry (a previously created-then-deleted "populated checkbox" note) instead of creating a new note — needs a look tomorrow (index entry for a deleted note file; Open should verify the file exists or create anew). Known follow-ups: reading column is 18 pt narrower everywhere (trailing reserve); ordered-list marker misalignment is pre-existing (#17); XCUITest scenarios in App/UITests/BlockEditorCraftUITests.swift still fail at launch/editor waits and are not part of the verification gate (VM script is). No build delivered to kayg@mbp beyond `63a011bd` earlier today; next delivery after tomorrow's supervised round.
Author
Owner

Worker BlockDrop owns worktree /home/kayg/Developer/calternal-apple/.worktrees/block-drop (branch wave19/block-drop). Starting implementation for the onto-block drop actions (make-note, swap, nest), portable per-Space setting, and mounted coverage. Next action: inspect existing editor/store writer seams, implement and verify scoped tests; parent Main owns VM lease/build sequencing.

Worker BlockDrop owns worktree /home/kayg/Developer/calternal-apple/.worktrees/block-drop (branch wave19/block-drop). Starting implementation for the onto-block drop actions (make-note, swap, nest), portable per-Space setting, and mounted coverage. Next action: inspect existing editor/store writer seams, implement and verify scoped tests; parent Main owns VM lease/build sequencing.
Author
Owner

DropFinish / wave19/block-drop / d3fa4e52: completed the block-drop verification gate work. Added mounted behavior coverage for make-note, swap, and nest; fixed nest source-separator removal, stable hovered-handle gutter placement, task-control discovery/first-line geometry, and responder-copy action. Focused onto-action tests and caret/handle regressions pass. The complete class reaches two independently confirmed existing image crashes tracked in #11; both were verified in desktop-test and recorded there. App build is currently blocked before packaging by unrelated VaultIndex.swift:2046 strict-concurrency region-isolation compilation error, so no VM deployment/evidence exists yet.

DropFinish / wave19/block-drop / d3fa4e52: completed the block-drop verification gate work. Added mounted behavior coverage for make-note, swap, and nest; fixed nest source-separator removal, stable hovered-handle gutter placement, task-control discovery/first-line geometry, and responder-copy action. Focused onto-action tests and caret/handle regressions pass. The complete class reaches two independently confirmed existing image crashes tracked in #11; both were verified in desktop-test and recorded there. App build is currently blocked before packaging by unrelated `VaultIndex.swift:2046` strict-concurrency region-isolation compilation error, so no VM deployment/evidence exists yet.
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-macos#24
No description provided.