Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ Leafdown uses lightweight [Keep a Changelog](https://keepachangelog.com/en/1.1.0

### Fixed

- Open the editor context popup with `Shift+F10` or the `Menu` key and operate every command in it from the keyboard.
- Announce the editor context popup as a named toolbar instead of an unnamed dialog.
- Announce recent files and recent folders under their own headings in the `Open recent` menu.
- Disable a submenu instead of opening it empty when every command inside it is unavailable.
- Keep the window controls out of the keyboard tab order, matching native title bar buttons.
Expand Down
2 changes: 2 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ Leafdown's editor integration uses Milkdown Kit directly through a Leafdown-owne

Shortcut execution follows the layer that owns the interaction. The window-level application listener routes only application command IDs and reserved webview suppression. Leafdown's editor keymap routes semantic editor commands and projection-aware history while the editor has focus. Milkdown, ProseMirror, and the browser retain structural editing and native clipboard gesture ownership. The shared command metadata describes labels and displayed shortcuts across these surfaces; it is not itself a global executable shortcut registry.

Focus ownership follows the same layering. A pointer-opened context popup leaves focus with the editor, which still owns the selection the popup acts on; a keyboard-opened popup takes focus, having no other route in, and returns it to the editor on close rather than leaving it on the document body. ProseMirror keeps its selection across a blur, so restoring the editor's focus restores the selection with it, and the popup holds no selection state of its own.

Syntax highlighting uses bundled Shiki assets through Milkdown highlighting plugins. Raw Markdown HTML is preserved as text-like editor content instead of being rendered as browser DOM.

### Clipboard Ownership
Expand Down
4 changes: 2 additions & 2 deletions docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,11 @@ For diagnostic log format and ownership, see [Architecture](./architecture.md#ba

### Context Popup

The context popup is a contextual menu triggered by selection or right-click within the editor.
The context popup is a contextual menu triggered by selection, right-click, or `Shift+F10` and the `Menu` key within the editor.

- Right-click inside an existing selection keeps the selection.
- Right-click outside a selection uses the editor's normal pointer handling to place the caret at the clicked location; the popup does not perform a second coordinate-based caret move.
- `Escape`, typing, clicking outside, or scrolling the popup out of view closes it.
- `Escape`, typing, or clicking outside closes it, as does `Tab` while focus is inside it. Scrolling the popup out of view closes it only while focus is in the editor; a popup holding focus stays open.

#### Popup Command Groups

Expand Down
11 changes: 11 additions & 0 deletions docs/specification.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ These state axes compose. A document session, for example, can have a folder con
- **Closed:** no popup is visible.
- **Open from selection:** commands act on the selected text or blocks.
- **Open from right-click:** commands act on the editor selection established by the right-click.
- **Open from keyboard:** commands act on the caret or selection the request was made from, and the popup holds focus.

## Editor Model

Expand Down Expand Up @@ -151,6 +152,16 @@ The editor is a unified hybrid Markdown surface. Behavior is governed by renderi
- `Shift+Tab`: Moves focus to the cell to the left.
- `Enter`: Moves focus to the cell directly below. If pressed in the bottom row, inserts a new row below and focuses it.
- `ArrowDown` (in the bottom row of a table): Exits the table downwards and moves the caret to the block below (creating a new empty paragraph block if none exists).
- `Shift+F10` and the `Menu` key open the context popup around the caret or selection and move focus into it. A popup opened by right-click or by a mouse selection leaves focus in the editor.
- The popup is one command toolbar, and focus enters it on its first available command:
- `ArrowLeft` and `ArrowRight`: Move between commands in order, wrapping at either end.
- `ArrowUp` and `ArrowDown`: Move between rows at the nearest available column, wrapping at either end and skipping a row whose commands are all unavailable. On a submenu, `ArrowDown` opens it instead.
- `Home` and `End`: Move to the first or last available command.
- `Enter` and `Space`: Run the focused command, or open the focused submenu.
- `Escape`: Closes the popup, or an open submenu first, returning focus to the command that opened it.
- `Tab`: Closes the popup as well, rather than moving to another control.
- Closing a popup that holds focus returns focus to the editor with its selection intact, whichever path closed it.
- A scroll closes the popup while focus is in the editor. While focus is inside it the popup stays open and may drift from the text it anchors to.
- Structural editing and native text gestures retain their normal editor behavior. Leafdown commands provide the same semantic operations across menus, keyboard shortcuts, and the context popup.
- The app intercepts and disables default webview reload and navigation shortcuts, including `Mod+R` and `Mod+Shift+R`, to prevent accidental state resets.

Expand Down
12 changes: 12 additions & 0 deletions src/components/ui/Toolbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Toolbar as ToolbarPrimitive } from "radix-ui";
import type { ComponentProps } from "react";

function Toolbar({ ...props }: ComponentProps<typeof ToolbarPrimitive.Root>) {
return <ToolbarPrimitive.Root data-slot="toolbar" {...props} />;
}

function ToolbarButton({ ...props }: ComponentProps<typeof ToolbarPrimitive.Button>) {
return <ToolbarPrimitive.Button data-slot="toolbar-button" {...props} />;
}

export { Toolbar, ToolbarButton };
Loading