Skip to content

UI Revamp v0 — Editor Surface References ​

Companion to ui-revamp-v0.md and second pass after ui-revamp-v0-refs.md. The first refs doc covered shell-level surfaces (3-pane, right-area panels, mention picker, bot-add flow, backend switcher). This one covers what happens inside the document — the surfaces the PRD §5 kill list flags ("the current floating editor menu") and the §6 component plan lists as new (SlashMenu, SelectionBubble).

Same rules as the first refs doc: this is a kit of references for the lock pass, not a design. The PRD constraints carry over — 14px base, dense, no global shadows, 1px borders, rounded-md ceiling.

1. Slash menu (PRD §6 SlashMenu) ​

Inserts a block at the caret when the user types /. Needs to cover headings, lists, code, task, callout, divider, table, image, embed, plus our extensions (AI prompt, bot mention, link-to-doc).

Borrow ​

  • Craft — block context popover (screen c59f0e28). Not strictly a slash menu, but the payload shape is exactly what we want: a single column of rows, each row = a small monochrome icon + an action label, with one mid-emphasis row ("Turn into Card") highlighted via a fill tint, dividers between semantic groups, and shortcut hints reserved for the right edge. No descriptions, no thumbnails — the row is the affordance. This is the density we need.
  • Craft — shortcuts cheatsheet (screen f6f77988). Useful as a taxonomy reference for slash items, not as visual lock: four columns (Style / Navigate / Organize / Task) with keyboard-shortcut chips. Tells us how Craft groups block-related actions; our slash menu's section order should mirror that mental model (Text → List → Embed → Action → Advanced), because users coming from Notion/Craft already know it.
  • Resend — left vertical block rail (screen 811edb3c). A 4-icon vertical strip (T, image, 2x2 grid, sparkle) floats just outside the content column. This is a complement to the slash menu, not a replacement — the rail is the "I don't remember the slash command" affordance and lives in the gutter. Same items as the slash menu, surfaced differently. Worth adopting as a peripheral option for mouse users; keyboard users hit /.
  • Search-as-you-type filtering. Across every editor we surveyed (Craft, Slab, Notion-likes), the slash menu uses a substring match on the row's label and alias list (e.g. /h1 matches "Heading 1"). Important: filter narrows the same flat list and re-groups; do not collapse sections to "best match" — the user types /co and needs to see both Code and Callout so they can disambiguate with one more keystroke.

Avoid ​

  • Notion-style descriptions under each item. Common in marketing screenshots — a two-line row with "Heading 1 / Big section heading". At 14px base this doubles the menu height for no information gain to repeat users. Use a single line with the label and a right-aligned shortcut hint instead.
  • Thumbnails / previews for every block type. Tome and Glorify lean on tile previews (Glorify 785ab890). They make sense for design-canvas tools where blocks are visual. For prose blocks (heading, list, code, callout) a thumbnail is wasted pixels. Reserve previews for the embed sub-menu only (image, video, link card).
  • A modal/centered command palette. Some apps (Luma 12ebb274, Deezer 7a916f34) center a search modal on dimmed canvas. Wrong for a slash menu — it has to anchor at the caret so the user's eye doesn't move. Popover, not modal.
  • Sticky "AI" tile at the top. It's tempting (and Anthropic-friendly) but biases every slash interaction toward the LLM. Our LLM lives in the right-area chat panel (§4.5); the slash menu's job is block insertion. AI actions belong in the selection bubble's "More" menu and as a regular slash row (/ask).

2. Selection bubble (PRD §6 SelectionBubble) ​

Floats above selected text. Replaces the "ugly current floating menu" called out in §5.

Borrow ​

  • Slab — single-row bubble (screen 912347ff). The closest reference to what we want. Top-anchored above the selection, single dark pill, contents in left-to-right order: Normal text ▾ (block-style dropdown) · B · i · U · S · </> (code) · link · highlight ▾ · comment · ⋯. The block-style dropdown is the trick that lets a single row handle both inline (B/i/U) and block (Heading / Quote / Bullet) actions without spawning a second row. Lock this structure. Approx 8 icons + 1 dropdown + 1 overflow = the maximum that stays readable at 14px.
  • Medium — minimal dual-purpose bubble (screen 22dce083). Worth noting for the arrow-down tail on the bubble (a small triangle pointing at the selection). Adds 4px of height for clear anchoring; we should keep this since at our density a borderless floating pill can read as detached. Medium's content is too sparse (B i | T T | " 1≡ | 💬) — they omit underline/strike entirely, which we cannot.
  • Resend — bottom selection bar (screen 811edb3c). Has the densest treatment of typography controls we found: Text ▾ </> B I U S </> AB ≣ ≣ ≣ ≣. Bottom-anchored rather than top-anchored, but the chip-style grouping (block-style chip on the left, divider, inline marks in the middle, divider, alignment on the right) is the right pattern for visually parsing many controls at a glance. Adopt the dividers.
  • Wandb — small inline popover near tool icon (screen 14943dab). Shows the link-insert pattern done well: clicking the link icon swaps the bubble's content area for a single text input with auto-paste + Enter to confirm, no second modal. That swap-in-place is what we want — not a second popover layered on top.

Avoid ​

  • Two-row bubbles by default. Notion classic exposes block-style as a separate top row. At our base size this becomes a 60px-tall floating shape, occluding the line above. Use Slab's single-row pattern with a dropdown for block-style.
  • A floating "More" button that opens a wide menu. It moves the eye twice (bubble → menu) and the menu often overlaps the bubble. Instead: ⋯ opens a small column anchored to the bubble's right edge, containing color, clear-formatting, AI-rewrite, comment-thread — the long tail of low-frequency actions. Keep the bubble itself stable; only the overflow moves.
  • Inline link previews inside the bubble. Some editors show the link target's OG image as soon as you paste — at our density it shoves the bubble to 2 rows. Show the parsed URL as plain monospace text in the bubble's link-input mode; previews belong on the inserted link block, not the editing chrome.

3. Block menus + drag handles (PRD §3 dense default) ​

The "row hover" affordance: on mouseenter of a block, show ⋮⋮ drag handle and + insert button in the left gutter outside the content column. Click the handle (or right-click anywhere on the block) → per-block menu.

Borrow ​

  • Craft — per-block context menu (screen c59f0e28). The reference. Action list, top to bottom: Add Content / Add Reaction / Add Comment / Turn into Card / Open Assistant / Remind Me ▸ / Insert Block Above / Insert Block Below / Copy Link to Block / Delete. Notable: Turn into Card is the only action with a colored fill — the destructive Delete is plain text at the bottom, which is correct (destructive is not the same as emphasis). The submenu marker ▸ on "Remind Me" gives us the pattern for nested actions (we'll use it for Turn into ▸ → list of block types, and Color ▸ → swatches).
  • Craft — right-side Insert/Format/Style/Info panel (same screen). A more permanent palette pinned to the right of the editor. Reject as a default surface (it competes with our right-area panel stack) but note the affordance: when a block is selected, the right-area can host a contextual Block panel (replacing the page-tree popover) for users who prefer a persistent inspector over hover affordances. Future v0.1.
  • Gutter ⋮⋮ +, not in-content. Across Notion-likes the drag handle never overlays the text — it lives in the left gutter, becomes visible on row hover, and the cursor stays on the text. We honor this: gutter width budget is ~24px, two stacked 16px icons, no labels.
  • Right-click parity. The same per-block menu opens on right-click anywhere in the row. This is critical for trackpad users who never hover-discover the handle. Lock both entry points.

Avoid ​

  • Persistent visible drag handles. Mailchimp (08af2456) and many email builders show a permanent drag chrome around every block. Looks busy and prevents the editor from feeling like prose. Hover-only.
  • Hamburger menus per block. Some editors use a ⋯ instead of ⋮⋮, which conflates "drag me" with "open menu". Use ⋮⋮ strictly for drag (cursor: grab) and + strictly for insert-below; right-click is the menu entry.
  • A "Turn into" picker that opens a modal. It interrupts flow. Use a flyout submenu off the per-block menu — same shape as the slash menu's block-type rows, so users learn one list.

4. Code blocks and embeds (PRD §6 — SlashMenu outputs) ​

Borrow ​

  • Anthropic Workbench — "Code for Claude API" modal (screen 983dbb9e). The cleanest in-doc code-block chrome we found. Top strip: Python ▾ on the left (the language picker), Copy Code + View Docs on the right. Code body has line numbers in a muted left column, syntax-highlighted body, no shadow, just a 1px border + slight bg shift. Lock this header layout for inline code blocks: <language-pill> left, <copy> right, nothing else on first render. The "Run" action — when we add it for executable blocks — joins the right cluster as a third button, not a separate row.
  • Linear developer docs (screen cbbd2bc8). Useful for the language pill styling: small monospace label inside a low-contrast pill, no chevron when there's only one language. Our pill always shows the chevron because every code block can be re-languaged.
  • Resend image-block chrome (screen eff09bc8). Image embed renders edge-to-edge inside the content column with a thin border, a centered hover handle (drag/resize), and a small caption row below the image (italic, muted). No floating action bar over the image itself. We adopt: embed chrome is the caption row + a hover-revealed ⋯ in the top-right corner of the embed, not a persistent toolbar.
  • Mercury attachment card (screen 018f4d0f). For file embeds: thin horizontal card with a left mime-type icon, filename + size middle, download icon on the right. One row, ~36px tall. This is what a non-previewable file (zip, dmg, binary) should render as inline. Distinct from image/video which gets full-bleed.

Synthesis: embed chrome rules ​

Embed typeChromeNotes
Code blockHeader strip: <lang ▾> left, <copy> right. Border + bg, no shadow.Run button added later for executable kinds.
Image / VideoFull-bleed inside content column. Caption row below. Hover-⋯ top-right for menu (alt text, replace, download, delete).No persistent toolbar on the asset itself.
File (non-previewable)Single-row card: icon · filename · size · download.Mime-detected from content (PRD §4.9).
Link card (URL embed)Card: site favicon, title, 1-line description, URL slug. Border, no shadow.If unfurl fails, fall back to plain underlined link, not an error card.
Failed embedInline 1-row warning row, retry action on the right. Not a centered modal, not a toast.Editing the URL stays in place.

Avoid ​

  • Lightbox / modal preview for inline images. Multiple references do this (Clipchamp 3370d540, Rox bf8cf417) — fine for a gallery, wrong inside a document. Click on an inline image edits its properties; nothing should fullscreen.
  • A floating language picker inside the code body. Some references float it over the code top-right (overlapping code on long lines). Anchor it to the header strip.
  • Copy buttons that show a green check toast. A 600ms inline label swap ("Copy" → "Copied") inside the same button is denser and faster.
  • Run-output panels stacked under every code block by default. Run is opt-in per block; the output area appears only after a run, collapses by default after the user reads it.

5. Pattern locks (recommendations for ADR-0005) ​

PRD refPattern locked from refsReference
§6 SlashMenuSingle-column popover anchored at caret. Rows = monochrome icon + label + right-aligned shortcut hint. Sections in fixed order (Text · List · Embed · Action · Advanced). Substring filter across labels + aliases. No descriptions, no thumbnails.Craft c59f0e28 (row shape), Craft f6f77988 (taxonomy), Resend 811edb3c (peripheral rail)
§6 SelectionBubbleSingle-row dark pill above selection. Order: <block-style ▾> · B · i · U · S · </> · link · highlight · comment · ⋯. Dividers between block / inline / action clusters. Link icon swaps the row into a single-input link mode in place.Slab 912347ff (lock), Medium 22dce083 (tail), Resend 811edb3c (dividers), Wandb 14943dab (link mode)
§3 row hoverLeft-gutter ⋮⋮ drag handle + + insert-below, both 16px, gutter ~24px wide, hover-only. Right-click anywhere in the row opens the per-block menu. Drag and menu have distinct icons.Craft c59f0e28
Per-block menuSingle column. Order: Add Content · Comment · Turn into ▸ · Color ▸ · Copy Link to Block · Insert Above · Insert Below · Duplicate · Delete. Submenus open as flyouts (▸), not modals.Craft c59f0e28
Code block chromeTop strip: <language ▾> left, <copy> right (Run later joins right cluster). Line numbers in muted left column. 1px border, no shadow. Inline "Copied" label swap, no toast.Anthropic Workbench 983dbb9e, Linear cbbd2bc8
Image / video embedFull-bleed inside content column, caption row below, hover-revealed ⋯ top-right for properties. No persistent toolbar over the asset.Resend eff09bc8
File embed (non-previewable)Single-row card: mime-icon · filename · size · download. ~36px tall.Mercury 018f4d0f
Link card embedCard: favicon · title · 1-line description · URL. Border, no shadow. Fall back to plain link if unfurl fails.(cross-product convention)
Failed embedInline 1-row warning, retry on the right, edit URL in place. No modal, no toast.(no good single ref; constraint follows from §3 density rules)

6. Open follow-up ​

  • Tables. Refero coverage of in-doc tables (not data-grid tools) was thin. We need to lock: column-resize affordance, per-cell context menu, header-row toggle, sortable vs static, and how tables collapse when the content column is narrow. Punt to a small follow-up pass; ship v0 with read-only-ish tables (insert / type / delete row, no column resize) and revisit.
  • Math / equation block. Slab and Craft both support it as a slash item but neither shows the editor UI clearly in the refs we found. KaTeX-shaped input is the de-facto baseline; the only design decision is whether the rendered block sits inline (like code) or full-bleed (like an image). Tentatively: inline-block treatment, same chrome as code, language pill replaced by LaTeX static label.
  • AI inline transform ("rewrite this paragraph"). The PRD keeps AI in the right-area chat, but the selection-bubble overflow has an AI action. We didn't lock how the streaming result lands back in the editor — diff-overlay in place? Insert-below-with-accept-reject? Defer to a dedicated AI-edit refs pass; do not ship in the first cut of SelectionBubble.
  • Drag-to-reorder visuals. All refs hide the drop indicator until the drag is in flight; nobody shows a polished "dropping here" treatment we can copy. Plan: a 2px accent rule between blocks during drag, no spacer animation. Confirm in Storybook against the actual block heights before locking.
  • Mobile / touch. Out of scope for v0 (no touch target in the product yet), but hover-only handles are a known accessibility risk on touch when we get there. Note for the next revamp.

Next pass after this lands: revise PRD §6 to reference the locked patterns by name; add a Storybook scaffold task per component (SlashMenu, SelectionBubble, plus the unbranded BlockMenu + RowHoverHandles that fall out of §3); then promote the §5 table above into ADR-0005 alongside the first refs doc's locks.