zudo-text

検索したい単語を入力

いつでも検索バーを開ける

Context menus

Right-clicking (or long-pressing, or pressing the keyboard's Context Menu key) on an item across the app opens the same kind of menu: a small floating panel with actions, separators, destructive rows, and — where an item has several sub-actions — a nested submenu. Every one of these menus is the same shared component under the hood, so they all open, close, and respond to the keyboard identically no matter which surface you're on.

Where menus appear

SurfaceWhat you get
Editor pane — the CodeMirror text areaCut/Copy/Paste, Select All; AI command…, Send selection to AI Assistant (only where AI is available); Edit table…/Edit sheet…, Insert component…, Attach file… (classified at the click point, not the cursor); Format Markdown; Copy file path (submenu: Full path, Relative path, Filename). Every mutating row is hidden in a read-only frame — only Copy, Select All, Send-to-assistant, and Copy file path survive
Preview pane — the rendered MarkdownVaries by what's under the pointer: an image (Enlarge image, Save image as…, Copy image filename), an asset chip (Download file), a link (Copy link URL), a code block (Copy code), a Mermaid diagram (Copy source), a blockquote (Copy text), or a spreadsheet block (Edit this table/sheet…, Copy block as Markdown). Any other block instead offers Edit this section in editor + Copy block as Markdown. A separator, then the pane-level rows always follow: Find in preview…, Toggle table of contents, Preview theme (submenu), Print…, Copy document (without frontmatter)
Kanban board — cardOpen card, Move to (submenu of the board's other columns), Archive
Kanban board — card's edit (✏️) buttonThe identical card menu, anchored to the button instead of the click point
Kanban board — calendar / now-view card chipSame card menu as the board's column view (Open card, Move to, Archive)
Kanban board — column headerRename column, Add card, Collapse/Expand column (label flips with state)
Kanban board — collapsed column tileExpand column — the tile's only row; rename/add-card stay on the expanded header
Todo board — item rowMark done/Mark not done, Rename, Set notification…/Clear notification, Move up/Move down (reorders within its Todo/Done section), Delete
Mind map — node (map view) or outline rowAdd child, Add sibling, Add sibling above, Rename, Move up/Move down, Collapse/Expand, Delete node — the same menu on both views, since both drive the same tree
Settings → Devices — device rowRename, Remove all overrides for this device
External File Editor — embedded tree row (file)Open, Open with default app, Copy path, Delete
External File Editor — embedded tree row (directory)Collapse/Expand, Make root, Reveal in Finder, Copy path
Inbox — note grid / timeline cardOpen, Copy content, Copy path, Edit tags, Delete
Archives — note cardSame menu as an Inbox note card — Archives is a Note Tray pointed at archives/, not a separate surface
Pile View — cardSwitch to note, Archive (non-empty, archivable cards only), Copy content, Copy path, Edit tags — no Delete row (see below)
Summarized Note List — cardOpen, Copy content, Copy path, Delete — Delete is omitted for an empty slot
Note Tray toolbar — pillA numbered pill: Open, Copy content, Copy path, Note history when supported, Find similar notes on desktop, Move to <directory> (submenu), Delete. A named-file pill: Open and Copy path, plus Note history when supported and Find similar notes on desktop. An empty or not-yet-listed pill: Open only. Mutating rows are hidden in a read-only frame
Tags page — doc rowOpen, Copy content, Copy path, Edit tags, Move to inbox, Delete
Tags page — tag rowCopy tag name — the row's only item
Toolbar — pin button / pin overflow rowOpen, Edit pin…, Hide from header, Move left/Move right, Reveal target in Finder/Copy target path (path-backed pins only), Remove pin, Manage pins…. An overflow row (in the "…" dropdown, once too many pins to fit inline) opens the identical menu
Toolbar — temp-frameset buttonSwitch to, Save as pin…, Restore previous pin, Discard — the implicit "current unsaved view" button (before it's registered as its own entry) omits Switch to and Discard
Frame header (right-click the title bar)Collapse/Zoom/Pop out frame (hidden with a single frame), Frame settings, Empty frame, Split right/left/top/bottom, Swap frames, Equalize layout, Close frame
Empty-frame bodySame menu as the frame header — right-click activates the frame first, then opens it; the nav's own search box, "Load file…" button, and provider rows keep their normal click behavior instead of being intercepted
Assets manager dialog — thumbnailOpen, Open with default app (desktop only), Download, Copy path (desktop only), Delete
External File Editor — tabClose, Close others, Close all, Copy path, Reveal in Finder, Open with default app, Edit/Preview (previewable tab, only shown for the active tab), Tab bar position (submenu)

Doc Cloud deliberately adds no provider-specific context menu to its outline or page tabs. Remote operations with concurrency or dirty-state consequences stay as explicit, labelled outline/tab/toolbar/dialog actions (including Publish), where loading, blockers, and Accepted-vs-live state can remain visible. Its CodeMirror surface still follows the standard editor-menu read-only rules.

The External File Editor rows above describe its embedded directory tree. There is no standalone Directory View frame or cross-frame menu hand-off.

Every one of these opens the same way: right-click the item, or press the keyboard's Context Menu key (or Shift+F10) while it's focused. Both a real right-click and the keyboard invocation dispatch the same underlying contextmenu DOM event — the component tells them apart and, for the keyboard case (no real pointer position), anchors the menu to the focused element's bottom-left corner instead of trying to use (0, 0).

Pile View has no Delete row by design. Pile View's undo stack only covers Archive, and the backend's delete call has trash-vs-permanent semantics that haven't been verified for this cross-directory overview surface — so a permanent, non-undoable per-card Delete isn't offered there. Archive (undoable) is the only removal flow.

On a touch device, holding your finger down on an item works the same way in most mobile browsers — a long press fires a native contextmenu event, same as a right-click. There are two exceptions, both because something else already claims the hold gesture:

  • The kanban board: dragging a card claims a ~250ms hold for drag-and-drop, so kanban cards keep their explicit ✏️ ("Edit card") button as the touch entry point instead of relying on long-press.

  • Note Tray toolbar pills: reordering a pill is a plain HTML5 drag on the whole pill (no separate handle to drag from), so it claims the hold the same way. Unlike kanban, there's no button substitute yet — in v1, the pill menu is desktop (right-click) and keyboard (Context Menu key / Shift+F10) only.

The editor and preview panes are a third, different exception: on a mobile viewport or iOS, neither pane intercepts contextmenu at all, so a long-press falls through to the platform's native text-selection behavior instead of opening the app's menu. This isn't the nativeContextMenu setting below — it's an unconditional platform gate, on every device in that category, regardless of the setting.

Keyboard access

Once a menu is open, it's fully keyboard-navigable — only the currently highlighted row is in the tab order (a "roving tabindex"), so Tab doesn't cycle through every item; it exits the menu instead.

KeyAction
↑ / ↓Move the highlight to the previous / next enabled row (wraps at the ends)
Home / EndJump to the first / last enabled row
→ (on a submenu row)Open the submenu and move the highlight into it
← (inside a submenu)Close that submenu and return the highlight to its parent row
Enter / SpaceActivate the highlighted row (or open it, if it's a submenu)
EscapeClose one level — the open submenu first, then the whole menu
TabClose the whole menu without moving focus elsewhere

A menu opened from inside a dialog (for example, the Assets manager) behaves the same way, with one deliberate exception: Escape there closes only the context menu, never the dialog underneath it. If you want to close the dialog too, press Escape again (or use its own close button) once the menu is gone.

Right-clicking somewhere else while a menu is open relocates it instead of requiring a second dismiss-then-reopen; clicking or scrolling anywhere outside the menu, resizing the window, or the window losing focus all close it.

On mobile

Below the app's mobile breakpoint, every context menu renders as a bottom sheet instead of a floating panel anchored to the click point — the same action list, just presented as a full-width slide-up sheet with a back button for its own header. A submenu becomes a "drill-in" page: tapping it slides in a new sheet page (its rows, with a back button in place of the usual chevron), rather than trying to nest a flyout inside a sheet. Roving keyboard focus, Escape/back-one-level, and Enter/Space activation all work the same way inside the sheet for anyone navigating with a keyboard or switch device.

Architecture note

The menu is one component: ContextMenu + its wiring hook useContextMenu, in @takazudo/ui-components. A surface builds a plain array of item descriptions — { id, label, onSelect } rows, { type: "separator" }, { type: "submenu", items: [...] }, and so on — and hands them to ContextMenu; the component owns everything else: positioning (flip/clamp near viewport edges), portaling to document.body so no ancestor overflow: hidden or dialog stacking context can clip it, focus management, and the desktop/mobile split described above. This is why every surface in the table above behaves identically — they're all the same component with a different item list.

Positioning uses a dedicated z-index tier, --z-context-menu (68), placed deliberately between --z-modal-stacked (65) and --z-tooltip (80): above every dialog, including one stacked on top of another, because several menus open from inside a dialog (Assets manager, Settings → Devices); below tooltips, so a tooltip left showing from a hover just before a right-click doesn't render on top of the menu (the menu dismisses any visible tooltip when it opens, rather than trying to out-rank it).

The editor.nativeContextMenu escape hatch. The editor and preview panes now show the app's own menu by default (the rows in the table above), but some native WebView behavior — macOS Look Up, Services, Speech, spellcheck's own suggestions UI, "Paste and Match Style" — has no app-side equivalent. Settings → Editor → Native Context Menu (editor.nativeContextMenu, off by default) restores the native menu in both panes, at the cost of every row in the tables above. It's a single on/off switch for both panes together, not a per-item toggle, and it applies only there — every other surface in this doc keeps the app's ContextMenu.

The preview pane also keeps the native menu whenever it's rendering as a slide deck (Settings → General → Use Slides applied to a doc with slide frontmatter): a slide's rendered output has no flat Markdown DOM or data-source-line anchors for the element-aware builder to classify, so there's nothing to build a menu from.

Description panel

Action items may supply description, whenUseful, and manualPath. On desktop, hovering an annotated action for 400 ms shows its title, explanation, use case, and shortcut beside the menu. Submenu rows do not show descriptions. Leaving or relocating the menu clears pending descriptions. The panel shares the menu's viewport positioning, theme tokens, and dismissal containment.

Settings → General → Context menu descriptions selects hover (default), always (hover and keyboard navigation), manual-only (hover on actions with a manual page), or off. On mobile, descriptions appear as a muted second line unless the policy is off; manual-only limits them to actions with a manual page. The sheet has no Manual link.

The optional Manual button opens the corresponding end-user manual page without activating the action. Pointer users can move directly into the panel; keyboard focus remains in the menu and the description text is referenced by aria-describedby. The decorative panel and its button are outside the tab order. Manual pages remain available through the app's Help surfaces.

ContextMenu accepts descriptionPolicy and onOpenManual overrides. App-wide integration uses setContextMenuDescriptionPolicy and setContextMenuOpenManual; updates notify mounted menus without mirroring props into local state.

Note-specific actions: the pill label is Note history. Find similar notes searches from the right-clicked non-empty note, even when another tray is active, and restores focus to the triggering pill. This explicit note request reads persisted content; the existing no-detail command still uses the active editor buffer. It remains read-only and is omitted on mobile.