Editor on touch: swipe to select blocks and a block style sheet (needs owner decision, DESIGN §19 OPEN) #92

Open
opened 2026-09-25 14:02:06 +00:00 by kayg · 0 comments
Owner

Status: needs an owner decision before a job builds it

DESIGN §19 marks the block-editor chrome and gestures as OPEN, and DESIGN §35 lists "the per-screen essentials" as open. Do not build this until the owner confirms the scope below for calternal (the decisions were made for calternal.js). This issue keeps the context so nothing is lost.

Problem

On a phone, a calternal note has no way to select whole blocks and no formatting controls. The only tools are Markdown input rules and the slash menu (packages/editor/src/slash.ts). You cannot turn three paragraphs into a list, indent a list item, or make a block a heading without typing syntax. apps/web/src/lib/notes/NoteEditorSurface.svelte states that the calternal.js block-selection controller, touch grammar and format toolbars were not ported.

Reproduce: open any note at 390x844 with touch emulation. Try to select two blocks and make them a bulleted list, or to indent a list item: there is no control for it.

Decisions made in calternal.js (to confirm for calternal)

  • Swipe to select. A horizontal swipe on a block selects it; another swipe selects the next block, one at a time. Selected rows show a filled dot in the right gutter, unselected rows an empty circle. (Craft on iOS is the reference.)
  • Block sheet. While blocks are selected, a bottom sheet shows in place of the keyboard, with the current style highlighted:
    • row 1, block type: Heading, Body, More...;
    • row 2, block kind: Task, Toggle, Bulleted list, Numbered list, Outdent, Indent;
    • row 3, decoration: Quote accent, Callout, More...;
    • Reset and Done.
      It applies to every selected block at once.
  • The block actions for one block were Style, Tag, Link, Duplicate, Delete; for several blocks Style, Tag, Duplicate, Delete. Reordering stays the long-press drag (#91), not a button.
  • A formatting bar above the keyboard while typing must sit flush on the keyboard (no double safe-area inset) and show an overflow cue.

How it must fit calternal's rules

  • DESIGN §34: no stacked toolbars, and details open in the inspector popover on a fine pointer or a bottom sheet on touch. Build the block sheet on the shared packages/ui/src/components/Inspector.svelte / FloatingSurface sheet presentation. Do not add a second sheet primitive and never a right-hand column.
  • DESIGN §35: essentials only. Keep only controls that the Markdown dialect can store (DESIGN §9: the schema is limited to what round-trips through Markdown). Drop Craft ideas with no Markdown form (block colour, "Page" cards) unless the owner decides otherwise.
  • Size tokens from packages/ui/src/tokens.css (--hit 44 px on a coarse pointer); the layout sweep checks touch targets.
  • Reuse the existing pieces: packages/editor/src/blockSelection.ts, blockSelectDecoration.ts, listIndent.ts (the shared indent/outdent command), formatCommands.ts (format command descriptors), apps/web/src/lib/gestures.ts (swipe with axis lock, never hijack vertical scroll), apps/web/src/lib/selection.ts (native selection guard), apps/web/src/lib/actions/rovingToolbar.ts.
  • Block properties do not exist in calternal (no reminders or metadata on blocks); note properties stay in the Obsidian-style properties panel (packages/ui/src/components/notes/NoteProperties.svelte, DESIGN §19). This sheet is for block style only.
  • Every selected block keeps a Copy link action (deep links rule, DESIGN §33, /n/<id>#^<block-id>).

Acceptance criteria (after the decision)

  • 390x844 touch emulation: swipe selects blocks one by one; a vertical scroll never selects; the sheet applies each style to all selected blocks; changes reach the .md file and a second client.
  • Keyboard and screen reader: the sheet is a labelled dialog with a focus trap, Esc closes it and focus returns; the current style is exposed (aria-pressed or radio state); swipe has an equivalent non-gesture path.
  • Reduced motion: no sheet spring, a fade only.
  • Screenshots from a production build at 390x844 and 1280x900, light and dark.
  • Gates: bun run check, bun run test in apps/web and packages/editor, bun run test:e2e:layout, quoted verbatim.

Carried over from kayg/calternal.js#127
Carried over from kayg/calternal.js#169 (block part only)

## Status: needs an owner decision before a job builds it DESIGN §19 marks the block-editor chrome and gestures as **OPEN**, and DESIGN §35 lists "the per-screen essentials" as open. Do not build this until the owner confirms the scope below for calternal (the decisions were made for calternal.js). This issue keeps the context so nothing is lost. ## Problem On a phone, a calternal note has no way to select whole blocks and no formatting controls. The only tools are Markdown input rules and the slash menu (`packages/editor/src/slash.ts`). You cannot turn three paragraphs into a list, indent a list item, or make a block a heading without typing syntax. `apps/web/src/lib/notes/NoteEditorSurface.svelte` states that the calternal.js block-selection controller, touch grammar and format toolbars were not ported. Reproduce: open any note at 390x844 with touch emulation. Try to select two blocks and make them a bulleted list, or to indent a list item: there is no control for it. ## Decisions made in calternal.js (to confirm for calternal) - **Swipe to select.** A horizontal swipe on a block selects it; another swipe selects the next block, one at a time. Selected rows show a filled dot in the right gutter, unselected rows an empty circle. (Craft on iOS is the reference.) - **Block sheet.** While blocks are selected, a bottom sheet shows in place of the keyboard, with the current style highlighted: - row 1, block type: Heading, Body, More...; - row 2, block kind: Task, Toggle, Bulleted list, Numbered list, Outdent, Indent; - row 3, decoration: Quote accent, Callout, More...; - Reset and Done. It applies to every selected block at once. - The block actions for one block were Style, Tag, Link, Duplicate, Delete; for several blocks Style, Tag, Duplicate, Delete. Reordering stays the long-press drag (#91), not a button. - A formatting bar above the keyboard while typing must sit flush on the keyboard (no double safe-area inset) and show an overflow cue. ## How it must fit calternal's rules - DESIGN §34: no stacked toolbars, and details open in the inspector popover on a fine pointer or a **bottom sheet on touch**. Build the block sheet on the shared `packages/ui/src/components/Inspector.svelte` / `FloatingSurface` sheet presentation. Do not add a second sheet primitive and never a right-hand column. - DESIGN §35: essentials only. Keep only controls that the Markdown dialect can store (DESIGN §9: the schema is limited to what round-trips through Markdown). Drop Craft ideas with no Markdown form (block colour, "Page" cards) unless the owner decides otherwise. - Size tokens from `packages/ui/src/tokens.css` (`--hit` 44 px on a coarse pointer); the layout sweep checks touch targets. - Reuse the existing pieces: `packages/editor/src/blockSelection.ts`, `blockSelectDecoration.ts`, `listIndent.ts` (the shared indent/outdent command), `formatCommands.ts` (format command descriptors), `apps/web/src/lib/gestures.ts` (swipe with axis lock, never hijack vertical scroll), `apps/web/src/lib/selection.ts` (native selection guard), `apps/web/src/lib/actions/rovingToolbar.ts`. - Block properties do not exist in calternal (no reminders or metadata on blocks); note properties stay in the Obsidian-style properties panel (`packages/ui/src/components/notes/NoteProperties.svelte`, DESIGN §19). This sheet is for block style only. - Every selected block keeps a Copy link action (deep links rule, DESIGN §33, `/n/<id>#^<block-id>`). ## Acceptance criteria (after the decision) - 390x844 touch emulation: swipe selects blocks one by one; a vertical scroll never selects; the sheet applies each style to all selected blocks; changes reach the `.md` file and a second client. - Keyboard and screen reader: the sheet is a labelled dialog with a focus trap, Esc closes it and focus returns; the current style is exposed (`aria-pressed` or radio state); swipe has an equivalent non-gesture path. - Reduced motion: no sheet spring, a fade only. - Screenshots from a production build at 390x844 and 1280x900, light and dark. - Gates: `bun run check`, `bun run test` in `apps/web` and `packages/editor`, `bun run test:e2e:layout`, quoted verbatim. Carried over from kayg/calternal.js#127 Carried over from kayg/calternal.js#169 (block part only)
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#92
No description provided.