# SLICE 14a — Compact move menu for editable Kanban cards > **State-saving rule:** update this file after every completed task and whenever > work pauses. Keep implementation, automated validation, production packaging, > and manual Joplin acceptance as separate status boundaries. ## Status **COMPLETE.** Phases 1–5 are complete. Focused and full automation passed, a fresh production JPL was built and inspected, and the user confirmed manual Joplin acceptance on 2026-07-31. ## Goal Reduce visual noise on editable Kanban cards by removing the permanently visible **Move to…** select. Keep the existing pointer-drag handle and place a compact disclosure arrow directly beneath it. Activating the arrow opens a temporary menu containing the available destination columns. The menu remains closed by default. All Slice 14 persistence, validation, canonical refresh, error handling, focus restoration, and accessibility behavior remains authoritative. ## Confirmed product contract - Remove the always-visible **Move to…** select from every editable card. - Keep the existing drag handle at the upper-right of each editable card. - Add a compact disclosure arrow directly beneath the drag handle. - Activating the arrow opens a temporary dropdown/menu below the arrow. - The menu lists the three fixed Kanban destinations: Backlog, In Progress, and Done. - The card's current column is unavailable and cannot submit a move. - The menu is closed by default and closes after selection or cancellation. - Pointer drag remains available and unchanged. - Keyboard movement remains fully available; the disclosure and menu must be operable without a pointer. - Use the existing constrained `moveKanbanCard` intent and main-process mutation pathway. This slice must not introduce new write capabilities. - Preserve pending/error feedback, canonical refresh, stale-response rejection, focus restoration, and live announcements. - Read-only boards continue to expose no drag or move controls. - Matrix, calendar, and Gantt rendering remain unchanged. ## Interaction model ### Closed state Each editable card shows a compact two-control stack in its upper-right corner: ```text ┌───────────────┐ │ Card title ↕ │ pointer drag handle │ ▾ │ move-menu disclosure └───────────────┘ ``` The disclosure arrow has an accessible name such as **Move “Card title” to…**. It is a real keyboard-focusable button. The pointer-only drag handle remains out of the tab order as established by Slice 14. ### Open state Activating the disclosure displays a menu immediately beneath it: ```text ▾ ┌─────────────┐ │ Backlog │ │ In Progress │ │ Done │ └─────────────┘ ``` - The current destination is disabled or omitted consistently; disabling it is preferred so all menus retain the same predictable destination order. - Arrow keys move among enabled menu items. - Enter or Space selects an item. - Escape closes the menu and returns focus to the disclosure. - Clicking outside closes the menu without submitting. - Selecting a destination closes the menu, submits one move, and enters the existing pending state. - Only one move menu should be open within a rendered view at a time. ## Accessibility and focus contract - The disclosure communicates expanded/collapsed state with `aria-expanded` and identifies its menu with `aria-controls`. - Use appropriate button/menu semantics and accessible destination names. - Opening moves focus predictably to the first enabled destination, or another documented deterministic item. - Closing without selection restores focus to the disclosure. - After a successful or rejected canonical refresh, restore focus to the moved card's disclosure when the card remains visible. - If sorting or pagination removes the card from the current batch, retain Slice 14's status-region focus fallback. - Pending, success, stale, and failure announcements continue through the existing live region. - Read-only cards gain no new focus stops. ## Layout and styling constraints - The closed control stack must occupy only the narrow upper-right card area and must not reserve a full row beneath every card title. - Card titles and detail text should regain the vertical space previously used by the visible select. - The open menu may overlay nearby content and must not resize every card or Kanban column. - The menu must remain legible with custom card foreground/background colours and Joplin light/dark themes. - Preserve card borders, hover detail, completed styling, glyphs, recurrence marks, pagination, and column sizing. - Avoid clipping the menu inside card, column, or notebook-view containers. ## Implementation plan ### Phase 1 — Menu markup and state - [x] Remove the always-visible `.gtd-kanban-move-select` control. - [x] Add an editable-only disclosure button beneath the drag handle. - [x] Render a closed-by-default destination menu with fixed destinations. - [x] Disable the current destination. - [x] Ensure only one menu per rendered view is open at a time. - [x] Close on selection, Escape, outside click, rerender, and cancellation. #### Phase 1 completion record - Replaced the permanent select with an editable-only disclosure button beneath the unchanged pointer drag handle. Its fixed Backlog, In Progress, and Done menu is hidden by default and disables the card's current destination. - Per-view `openMoveMenu` state closes an older menu before opening another. Selection, Escape, outside pointer action, pending submission, and canonical rerender all remove the open state and document-level listener. ### Phase 2 — Keyboard, focus, and announcements - [x] Add `aria-expanded`, `aria-controls`, accessible names, and menu semantics. - [x] Implement deterministic arrow-key navigation and Enter/Space selection. - [x] Restore focus to the disclosure after cancellation. - [x] Restore focus to the moved card's disclosure after canonical refresh. - [x] Preserve status fallback when the moved card is no longer visible. - [x] Preserve Slice 14 live announcements and pending-state behavior. #### Phase 2 completion record - The disclosure is a named button with `aria-expanded`, `aria-controls`, and `aria-haspopup`. The popup and destination buttons use menu/menuitem semantics. - Arrow Down opens and focuses the first enabled item. Arrow Up/Down, Home/End, native Enter/Space activation, Escape restoration, outside dismissal, and Tab closure are implemented deterministically. - Canonical refresh now restores focus to the disclosure rather than the removed select. Slice 14's status fallback, live regions, duplicate guard, and pending control disabling remain shared and unchanged. ### Phase 3 — Styling and regression - [x] Stack the disclosure beneath the drag handle without restoring a full-width control row. - [x] Position the open menu beneath the disclosure without resizing cards. - [x] Verify theme compatibility, custom colours, long titles, completed cards, hover details, grouped boards, scrolling, and paginated columns. - [x] Confirm pointer drag and normal card click/open behavior do not regress. - [x] Confirm read-only Kanban, matrix, calendar, and Gantt DOM remain unchanged. #### Phase 3 completion record - Added a narrow absolute-positioned control stack and overlay menu. The compact arrow consumes no full-width content row; the menu uses Joplin theme colours, overlays neighboring content, and leaves card/column widths unchanged. - Styling remains scoped to editable Kanban control classes. Shared card title, detail, completion, click, pointer drag, grouping, scrolling, pagination, read-only, matrix, calendar, and Gantt paths were not structurally changed. ### Phase 4 — Documentation, automation, and packaging - [x] Update README.md keyboard-control wording and any screenshots/examples that imply the select is permanently visible. - [x] Update SPEC.md interaction and accessibility details. - [x] Add the Slice 14a UI refinement to the unreleased CHANGELOG entry. - [x] Run focused protocol/mutation/handler and practical rendering-contract checks. - [x] Run the complete Jest suite, TypeScript, webview syntax, whitespace, and prohibited-reference audits. - [x] Build and inspect a fresh production JPL without publishing or versioning. #### Phase 4 completion record - README, SPEC, and the unreleased 2.0.0 CHANGELOG entry now describe the compact disclosure and closed-by-default keyboard menu. No screenshot in the repository depicted the removed select. - Added a practical source/CSS rendering-contract suite covering removal of the permanent select, disclosure/menu semantics, fixed destinations, current-state disabling, keyboard/outside dismissal markers, and overlay styling. - Focused validation passed: 4 suites, 46 tests. Full validation passed: 20 suites, 298 tests. TypeScript with `--skipLibCheck`, webview syntax, `git diff --check`, stale-select wording, and packaged-output prohibited-path/ planning-reference audits passed. - `npm run dist` produced and the archive inspection verified `publish/com.victorwiebe.joplin.plugin.gtd-calendar.jpl` (180,736 bytes, embedded manifest version 1.0.0, SHA-256 `80ec9c230ab2dad2a3fb11cb0c0390403a84e6655d31e10af58ba7a665305318`). The five expected files and compiled disclosure/menu/accessibility markers are present. Nothing was versioned, published, pushed, or committed. - No manual Joplin acceptance was performed; every Phase 5 item remains open. ### Phase 5 — Manual Joplin acceptance - [x] Editable cards no longer show an always-visible **Move to…** select. - [x] Every editable card shows the drag handle with a disclosure arrow directly beneath it. - [x] The menu is closed by default and opening one does not resize all cards. - [x] Pointer activation opens the correct card's menu beneath the disclosure. - [x] Keyboard activation, arrow navigation, Enter/Space selection, and Escape cancellation work. - [x] The current destination is unavailable and cannot submit. - [x] Outside click closes the menu without writing. - [x] Only one menu is open per rendered view. - [x] Selecting every valid destination persists the same exact note/to-do state accepted in Slice 14. - [x] Focus returns to the disclosure after cancellation and canonical refresh; the status fallback works when sorting/pagination hides the card. - [x] Pending/error states, live announcements, and duplicate-request prevention remain correct. - [x] Pointer drag, click-to-open, hover details, pagination, grouped boards, multiple blocks, long scrolling, custom colours, and completed styling do not regress. - [x] Read-only Kanban boards expose neither control; matrix, calendar, and Gantt remain unchanged. - [x] Record explicit user sign-off separately from automation. #### Phase 5 acceptance record - **PASSED — USER SIGN-OFF RECEIVED 2026-07-31.** After testing the inspected Slice 14a JPL, the user reported the compact move-menu result was perfect and asked that Slice 14a be marked complete and pushed. - This manual acceptance is recorded separately from Phase 4 automation and packaging. Slice 15 has not begun. ## Out of scope - Changing Slice 14 transition semantics or persistence rules. - Adding destinations, arbitrary tag editing, or free-form ordering. - Moving cards between notebook groups or blocks. - Matrix or Skeleton editing. - Changing card content, dates, note type, or notebook ownership. - Replacing pointer drag with the menu. - Publishing or bumping the plugin version. ## Acceptance criteria - Editable cards are visually quieter because the full-width move select is absent while the move menu remains discoverable beneath the drag handle. - Pointer and keyboard users can reach every valid destination through compact, accessible controls. - Slice 14's mutation safety, canonical refresh, errors, announcements, focus, sorting, pagination, grouping, and read-only defaults do not regress. - Automated validation, production packaging, and explicit manual Joplin acceptance are completed and recorded separately.