@estiva-app/ui 0.69.0 → 0.69.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AttachmentCard.d.ts.map +1 -1
- package/dist/LinkChips.d.ts.map +1 -1
- package/dist/editor.js +2 -0
- package/dist/editor.js.map +2 -2
- package/dist/index.js +9 -1
- package/dist/index.js.map +2 -2
- package/package.json +1 -1
- package/registry.json +1 -1
- package/src/AttachmentCard.tsx +8 -2
- package/src/LinkChips.mdx +2 -1
- package/src/LinkChips.test.tsx +17 -0
- package/src/LinkChips.ts +4 -0
package/dist/editor.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/SelectionToolbar.tsx", "../src/PastedImages.ts", "../src/CodeBlockColours.ts", "../src/codeColours.ts", "../src/suggestionPopup.tsx", "../src/EditorBlockHandle.tsx", "../src/EditorTableControls.tsx", "../src/looks.ts", "../src/ComposerTriggers.tsx", "../src/BlockNodes.tsx", "../src/ReferenceMenu.tsx", "../src/LinkChips.ts", "../src/ReferenceCards.ts"],
|
|
4
|
-
"sourcesContent": ["import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\nimport { IconBold, IconCode, IconItalic, IconLink, IconUnderline } from '@tabler/icons-react'\nimport { Form } from './Form'\nimport { Popover } from './Popover'\nimport { TextInput } from './TextInput'\nimport { Toolbar, ToolbarButton } from './Toolbar'\nimport { shortcutLabel } from './shortcuts'\n\n/**\n * The selection, kept visible while something else has the focus \u2014 Peek's\n * `src/extensions/keptSelection.ts` (PEE-19), moved into the package with the\n * `SelectionToolbar` that needs it (UIG-31, Katerina's ruling 29 September).\n *\n * The toolbar's address field takes the focus, and with it the document's\n * one selection, so the words being linked lost their highlight the moment\n * the field opened (measured 2026-09-07). Linear keeps it, and Katerina asked\n * for the same. The editor's own selection never moved \u2014 ProseMirror keeps it\n * while unfocused \u2014 so this only *draws* it: an inline decoration over the\n * range, in `bg-selected` \u2014 the token Peek's composer paints `::selection` in,\n * mapped through edits and cleared when the field closes.\n *\n * A decoration rather than a second selection, because a document has one\n * and the field needs it. Editor-internal by design (the editor owns its own\n * drawing); nothing here floats, focuses or handles a key.\n */\n\nexport interface KeptRange {\n from: number\n to: number\n}\n\n/** The plugin's key: read what is being kept, or dispatch a change to it. */\nexport const keptSelectionKey = new PluginKey<KeptRange | null>('keptSelection')\n\n/** The extension an editor registers so a selection can stay drawn while a field has the focus. */\nexport const KeptSelection = Extension.create({\n name: 'keptSelection',\n\n addProseMirrorPlugins() {\n return [\n new Plugin<KeptRange | null>({\n key: keptSelectionKey,\n state: {\n init: () => null,\n apply(tr, previous) {\n const meta = tr.getMeta(keptSelectionKey) as KeptRange | null | undefined\n if (meta !== undefined) return meta\n if (!previous) return null\n // Follow the words through an edit, so applying the link to them\n // (or typing before them) does not leave the highlight behind.\n return { from: tr.mapping.map(previous.from), to: tr.mapping.map(previous.to) }\n },\n },\n props: {\n decorations(state) {\n const range = keptSelectionKey.getState(state)\n if (!range || range.from === range.to) return null\n return DecorationSet.create(state.doc, [\n Decoration.inline(range.from, range.to, { class: 'kept-selection bg-bg-selected' }),\n ])\n },\n },\n }),\n ]\n },\n})\n\n/** Draw the selection as it is now, until `releaseSelection`. */\nexport function keepSelection(editor: Editor) {\n const { from, to } = editor.state.selection\n editor.view.dispatch(editor.state.tr.setMeta(keptSelectionKey, { from, to }))\n}\n\n/** Stop drawing it. Nothing kept, nothing dispatched. */\nexport function releaseSelection(editor: Editor) {\n if (editor.isDestroyed || !keptSelectionKey.getState(editor.state)) return\n editor.view.dispatch(editor.state.tr.setMeta(keptSelectionKey, null))\n}\n\n/** What is being drawn, or null. */\nexport function keptSelection(editor: Editor): KeptRange | null {\n return keptSelectionKey.getState(editor.state) ?? null\n}\n\n/** A mark the strip can toggle \u2014 the suite's inline vocabulary (SPEC \u00A713.1) less link and reference. */\nexport type MarkId = 'bold' | 'italic' | 'underline' | 'code'\n\nconst MARKS: Record<MarkId, { label: string; keys: string }> = {\n bold: { label: 'Bold', keys: 'Mod-B' },\n italic: { label: 'Italic', keys: 'Mod-I' },\n underline: { label: 'Underline', keys: 'Mod-U' },\n code: { label: 'Code', keys: 'Mod-E' },\n}\n\n/**\n * Toggle one mark over the current selection. The core `toggleMark`, which\n * each mark's own `toggleBold` and the like wrap: the package names no Tiptap\n * extension, only the marks the suite's format has (SPEC \u00A713.1).\n */\nfunction toggleMark(editor: Editor, id: MarkId): boolean {\n return editor.chain().focus().toggleMark(id).run()\n}\n\n/** The address the selection sits in, or '' \u2014 what the link field opens with. */\nexport function currentLink(editor: Editor): string {\n const href: unknown = editor.getAttributes('link').href\n return typeof href === 'string' ? href : ''\n}\n\n/**\n * What somebody typed into the link field \u2192 an `href`, or null when it cannot\n * be one. A bare host (`google.com`) gets `https://`. A scheme is kept only when\n * SPEC \u00A713.1 lets a reader follow it (http, https, mailto); `javascript:` and\n * the like are refused here. `localhost:5173` is a host and a port: a scheme is\n * never followed by a digit.\n */\nexport function normalizeHref(raw: string): string | null {\n const s = raw.trim()\n if (!s || /\\s/.test(s)) return null\n if (/^(https?:|mailto:)/i.test(s)) return s\n if (/^[a-z][a-z0-9+.-]*:(?!\\d)/i.test(s)) return null\n return `https://${s}`\n}\n\n/**\n * Apply the link field to the selection. Empty removes the link; an address\n * sets it, over the whole link when the caret sits inside one. `false` means\n * the text could not be an address, and the field should stay open.\n */\nexport function applyLink(editor: Editor, raw: string): boolean {\n const chain = editor.chain().focus().extendMarkRange('link')\n // The core commands the Link extension's `setLink` and `unsetLink` wrap, with\n // the same meta; `normalizeHref` has already refused what it would refuse.\n if (!raw.trim()) return chain.unsetMark('link', { extendEmptyMarkRange: true }).setMeta('preventAutolink', true).run()\n const href = normalizeHref(raw)\n if (!href) return false\n return chain.setMark('link', { href }).setMeta('preventAutolink', true).run()\n}\n\n/**\n * Bold / italic / underline / link over selected text \u2014 Peek's\n * `SelectionToolbar` (PEE-18, PEE-19), moved into the package at Katerina's\n * word (29 September, UIG-31) when Ship's description became its second user.\n * Unchanged but for two props: `marks`, which buttons the strip has (Peek's\n * three by default; Ship adds code), and `link`, whether it has the Link button\n * and its field (Peek's editor has a link mark; an editor without one leaves it\n * off). The editor must register `KeptSelection` when `link` is on.\n *\n * The marks apply to a run you have already written, which is why they are\n * not in the `/` menu: typing `/` replaces the selection, so by definition\n * there is nothing selected to apply them to.\n *\n * **The chrome is the shared `Popover`, anchored** (P26). It was a `Menu`, and\n * it never was one: a menu gives its contents roving focus and typeahead,\n * which would fight the link field this panel exists for. An anchored\n * `Popover` is the mode built for exactly this \u2014 it takes no focus, so the\n * caret stays in the text. It is laid out as a row instead of a column\n * through `className`, which `cn` merges last. The panel still portals out of\n * the composer, so no overflow clips it, and Floating UI clamps it on screen.\n *\n * **The anchor is the selection's own point** since 0.12.2. This toolbar is\n * *centred* over the selection (her design, PEE-19, after Linear), and the\n * package's `Popover` aligns left or right only \u2014 Base UI has `center`, the\n * package does not expose it (ADOPTION **B21**). So the placement arithmetic\n * and the hand-measured `SIZE` box stay for now, and the rect handed over is\n * positioned so the panel lands on the same pixel it always has: the\n * package's own 4px gap is subtracted from this file's 8px, and the rect's\n * left edge is the panel's. When `align=\"center\"` exists, `SIZE`, `GAP` and\n * `placement` all go and the anchor becomes the selection's own rect.\n *\n * **Two faces** (PEE-19 \u2014 Katerina, 2026-09-07, after Linear). `marks` is the\n * row of four buttons. `link` is one text field for the address: it opens from\n * the Link button, and opens by itself when the settled selection sits inside\n * a link, with that link's address in it. There is no open, embed or delete\n * button, by her call: the field is the whole control.\n *\n * **What applies the field** (her rule, same day): Enter, and clicking away \u2014\n * anywhere outside the toolbar, the editor included. Enter on an empty field\n * removes the link; clicking away with an empty field changes nothing, since\n * removing is deliberate and leaving is not. Escape drops what was typed and\n * puts the caret back. Once applied, the caret lands after the link, and\n * typing goes on as plain text \u2014 the mark is not inclusive (editorKit).\n *\n * While the field has focus the editor has not, so everything below that\n * hides the toolbar on blur or on an empty editor selection stands down in\n * `link` mode. The editor keeps its selection while unfocused; `focus()` in\n * the apply command puts it back.\n *\n * **Why it waits.** Showing on every selection change put the toolbar under the\n * pointer mid-drag, and made it blink on a plain click \u2014 ProseMirror restores\n * the previous selection when the editor takes focus and collapses it a tick\n * later, so there is a real, non-empty range in between. Both go away by\n * refusing to show while the pointer is down and re-reading the selection after\n * a short settle rather than trusting the event that woke us.\n */\n\nconst ICONS: Record<MarkId, typeof IconBold> = {\n bold: IconBold,\n italic: IconItalic,\n underline: IconUnderline,\n code: IconCode,\n}\n\ntype Mode = 'marks' | 'link'\n\n/** How long a selection must stand still before the toolbar appears. */\nconst SETTLE_MS = 250\nconst GAP = 8\n/** The gap the package's `Popover` adds of its own, subtracted above so the\n * panel keeps this file's 8px. Read from `Popover.tsx`, not guessed. */\nconst POPOVER_GAP = 4\n\nexport interface SelectionToolbarProps {\n /** The editor whose selection the strip follows. `null` while it loads. */\n editor: Editor | null\n /** Which marks the strip toggles, in order. Default bold, italic, underline. */\n marks?: readonly MarkId[]\n /** The Link button and its address field. Needs a link mark in the editor and `KeptSelection`. Default off. */\n link?: boolean\n /**\n * More of the strip, after the marks: the app's own `ToolbarButton`s for what\n * is selected \u2014 a chip's card, say. Each keeps the selection by preventing\n * its press's default, as the marks do. Nothing is added when it renders\n * nothing.\n */\n children?: ReactNode\n}\n\nexport function SelectionToolbar({ editor, marks = ['bold', 'italic', 'underline'], link = false, children }: SelectionToolbarProps) {\n /** The selection's top edge and horizontal centre; the face decides the rest. */\n const [anchor, setAnchor] = useState<{ top: number; centre: number } | null>(null)\n const [mode, setMode] = useState<Mode>('marks')\n const [url, setUrl] = useState('')\n // Re-render as marks turn on and off while the caret moves through the\n // selection, so the pressed states stay honest.\n const [, setTick] = useState(0)\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const pointerDown = useRef(false)\n const field = useRef<HTMLInputElement>(null)\n /** What the field holds while it is open; null while it is closed. The\n * listeners below are bound once, so they read this rather than state. */\n const draft = useRef<string | null>(null)\n // Read by the same listeners. Kept in an effect, declared before the focus\n // effect below so the ref already says `link` when the field's focus blurs\n // the editor.\n const modeRef = useRef<Mode>('marks')\n useEffect(() => {\n modeRef.current = mode\n }, [mode])\n // Read by `settle`, which is bound once: words already a link open the field only when there is one.\n const linkOn = useRef(link)\n linkOn.current = link\n\n const hide = useCallback(() => {\n clearTimeout(timer.current)\n draft.current = null\n if (editor) releaseSelection(editor)\n setAnchor(null)\n setMode('marks')\n }, [editor])\n\n /**\n * Leaving the field applies it (Enter or a click away). An address links the\n * words and the caret moves past them; an empty field on the way out is a\n * shrug, not a removal. Idempotent: the outside click reaches this twice,\n * once from the editor press and once from the Menu's own listener.\n */\n const commit = useCallback(() => {\n const text = draft.current\n if (text === null || !editor) return\n draft.current = null\n if (text.trim() && applyLink(editor, text)) {\n editor.commands.setTextSelection(editor.state.selection.to)\n }\n hide()\n }, [editor, hide])\n\n /** Read the selection as it is *now* and place the toolbar, or hide it. */\n const settle = useCallback(() => {\n if (!editor || editor.isDestroyed) return\n // Keeping the selection drawn is itself a transaction, which schedules\n // this; by then the field has the focus, and reading `hasFocus()` here\n // would close it.\n if (modeRef.current === 'link') return\n const { state, view } = editor\n const { empty, from, to } = state.selection\n if (empty || from === to || !view.hasFocus() || pointerDown.current) {\n setAnchor(null)\n setMode('marks')\n return\n }\n const start = view.coordsAtPos(from)\n const end = view.coordsAtPos(to)\n const centre = (Math.min(start.left, end.left) + Math.max(start.right, end.right)) / 2\n // Words that are already a link open on the field, with their address in it.\n const inLink = linkOn.current && editor.isActive('link')\n const address = inLink ? currentLink(editor) : ''\n draft.current = inLink ? address : null\n // The field is about to take the focus; the words stay highlighted.\n if (inLink) keepSelection(editor)\n setMode(inLink ? 'link' : 'marks')\n setUrl(address)\n setAnchor({ top: start.top, centre })\n }, [editor])\n\n useEffect(() => {\n if (!editor) return\n\n const schedule = () => {\n // The field owns the keyboard and the focus; the editor's own events\n // stand down until it closes.\n if (modeRef.current === 'link') return\n setTick((t) => t + 1)\n clearTimeout(timer.current)\n // A collapsed selection hides at once \u2014 waiting to hide is what made the\n // click blink. Only *showing* is deferred.\n if (editor.state.selection.empty) {\n setAnchor(null)\n return\n }\n timer.current = setTimeout(settle, SETTLE_MS)\n }\n\n // The field taking focus is the one blur that must not hide us.\n const onBlur = () => {\n if (modeRef.current !== 'link') hide()\n }\n\n // Only a press *inside the editor* starts a selection. A press on the\n // toolbar itself must not hide it \u2014 doing so unmounted the button before\n // its click could land, so nothing ever applied.\n const startsSelection = (e: MouseEvent) =>\n e.target instanceof Node && editor.view.dom.contains(e.target)\n\n const onPointerDown = (e: MouseEvent) => {\n if (!startsSelection(e)) return\n // A press in the editor while the field is open is a click away: apply\n // first, before the editor moves the caret under the press.\n commit()\n pointerDown.current = true\n hide()\n }\n const onPointerUp = () => {\n if (!pointerDown.current) return\n pointerDown.current = false\n schedule()\n }\n\n editor.on('selectionUpdate', schedule)\n editor.on('transaction', schedule)\n editor.on('blur', onBlur)\n // @estiva-escape(no-hand-rolled-behaviour): watches presses in the editor to follow a text selection; the Popover it opens in has no trigger to own that (Peek's UIG-8 escape, moved here with the part, UIG-31)\n document.addEventListener('mousedown', onPointerDown, true)\n document.addEventListener('mouseup', onPointerUp, true)\n\n return () => {\n clearTimeout(timer.current)\n editor.off('selectionUpdate', schedule)\n editor.off('transaction', schedule)\n editor.off('blur', onBlur)\n document.removeEventListener('mousedown', onPointerDown, true)\n document.removeEventListener('mouseup', onPointerUp, true)\n }\n }, [editor, settle, hide, commit])\n\n // The field takes focus once it is on screen. Not `autoFocus`, and not in\n // the same frame: the Menu renders once hidden to measure itself, and a\n // hidden input cannot take focus \u2014 measured 2026-09-07, the field opened\n // unfocused on a re-selected link. An address already in it is selected\n // whole, so typing replaces it.\n useEffect(() => {\n if (mode !== 'link' || !anchor) return\n const frame = requestAnimationFrame(() => {\n field.current?.focus()\n field.current?.select()\n })\n return () => cancelAnimationFrame(frame)\n }, [mode, anchor])\n\n if (!editor || !anchor) return null\n\n const openField = () => {\n const address = currentLink(editor)\n draft.current = address\n keepSelection(editor)\n setUrl(address)\n setMode('link')\n }\n /** Enter. Unlike a click away, an empty field here means remove. */\n const submit = () => {\n if (!url.trim()) {\n applyLink(editor, '')\n hide()\n editor.commands.focus()\n return\n }\n commit()\n }\n const discard = () => {\n hide()\n editor.commands.focus()\n }\n\n /* A zero-sized rect at the selection's own top edge and centre. The panel's\n width is no longer this file's business: `align=\"center\"` puts the\n panel's middle over this point (B21, package 0.12.2), so the measured\n `SIZE` table and the placement arithmetic that stood in for it are gone.\n The top is this file's GAP less the package's own 4, so the panel keeps\n the 8px it has always had. */\n const anchorRect = new DOMRect(anchor.centre, anchor.top - (GAP - POPOVER_GAP), 0, 0)\n\n return (\n <Popover\n anchor={anchorRect}\n open\n side=\"top\"\n align=\"center\"\n onOpenChange={(next) => {\n if (!next) commit()\n }}\n ariaLabel=\"Text formatting\"\n // The panel gives up its column; the strip inside draws the row. The 4px\n // around it is `contentClassName`'s: on `className` it was added to the\n // package's 8px from 0.12.6 on, and the toolbar grew 8px a side\n // (Katerina, 2026-09-13; migration docs D68, Finding 60). This is the\n // package's own `Popover/A toolbar` story, class for class.\n className=\"w-auto min-w-0\"\n contentClassName=\"p-1\"\n >\n {mode === 'link' ? (\n /* One field, so no strip: a toolbar of one control is a toolbar in\n name only, and `ToolbarInput` cannot take the ref this field is\n focused by (ADOPTION B25). */\n // The package Form (UIG-7): Enter applies the link. `free` and `flex`: one field, which keeps its own height.\n <Form onSubmit={submit} layout=\"free\" className=\"flex\">\n <TextInput\n ref={field}\n value={url}\n onChange={(e) => {\n setUrl(e.target.value)\n draft.current = e.target.value\n }}\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n // Before the Menu's own Escape, which would apply instead.\n e.stopPropagation()\n discard()\n }\n }}\n placeholder=\"Enter link URL\"\n aria-label=\"Link URL\"\n className=\"w-[264px]\"\n />\n </Form>\n ) : (\n /* Four marks and a link button: one Tab stop, \u2190 \u2192 along them (D29). */\n <Toolbar aria-label=\"Text formatting\" surface={false}>\n {marks.map((id) => {\n const Icon = ICONS[id]\n const active = editor.isActive(id)\n return (\n <ToolbarButton\n key={id}\n aria-label={MARKS[id].label}\n pressed={active}\n tooltip={MARKS[id].label}\n tooltipShortcut={shortcutLabel(MARKS[id].keys)}\n // Keep the selection: focus must not leave the editor on press.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => toggleMark(editor, id)}\n >\n <Icon size={16} stroke={1.5} />\n </ToolbarButton>\n )\n })}\n {link && (\n <ToolbarButton\n aria-label=\"Link\"\n tooltip=\"Link\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={openField}\n >\n <IconLink size={16} stroke={1.5} />\n </ToolbarButton>\n )}\n {children}\n </Toolbar>\n )}\n </Popover>\n )\n}\n", "import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\n/** As much of a `DataTransfer` as the paste reads. */\nexport interface ClipboardSource {\n files?: ArrayLike<File> | null\n getData(format: string): string\n}\n\n/** MIME type \u2192 the extension a pasted picture is named with. */\nconst IMAGE_EXTENSIONS: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/heic': 'heic',\n 'image/heif': 'heif',\n 'image/svg+xml': 'svg',\n}\n\n/** Names a browser invents for a clipboard picture. A made-up one is better. */\nconst GENERIC_STEMS = new Set(['image', 'blob', 'unknown', 'untitled', 'screenshot'])\n\n/*\n An unlisted type keeps its own subtype rather than becoming `png`, so a\n picture the app does not accept is refused by the app's own message instead\n of arriving disguised as one it does.\n*/\nfunction extensionFor(contentType: string): string {\n const type = contentType.toLowerCase()\n return IMAGE_EXTENSIONS[type] ?? type.split('/')[1]?.split('+')[0] ?? 'png'\n}\n\n/** `'Screenshot 2026-09-07 14-32-05.png'`: sortable, and readable on a file card. */\nexport function pastedImageName(contentType: string, at: Date = new Date(), index = 0): string {\n const pad = (n: number) => String(n).padStart(2, '0')\n const stamp =\n `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())} ` +\n `${pad(at.getHours())}-${pad(at.getMinutes())}-${pad(at.getSeconds())}`\n const nth = index > 0 ? ` (${index + 1})` : ''\n return `Screenshot ${stamp}${nth}.${extensionFor(contentType)}`\n}\n\n/** A real filename is kept; `image.png`, `blob` and '' are not names. */\nfunction hasUsableName(name: string): boolean {\n const dot = name.lastIndexOf('.')\n if (dot <= 0 || dot === name.length - 1) return false\n return !GENERIC_STEMS.has(name.slice(0, dot).toLowerCase())\n}\n\n/**\n * The pictures on the clipboard, named \u2014 `[]` when there is nothing to attach.\n *\n * **Text wins.** A copy from a spreadsheet or a document puts a rendered\n * picture of the selection beside the text; attaching the picture instead of\n * pasting the text is wrong every time. So a clipboard holding text is left to\n * the editor, and a screenshot, which holds none, lands here. Pictures only:\n * any other file comes through the app's own file picker.\n */\nexport function pastedImages(source: ClipboardSource | null | undefined, at: Date = new Date()): File[] {\n if (!source) return []\n if (source.getData('text/plain').trim() !== '') return []\n const images = Array.from(source.files ?? []).filter((f) => f.type.toLowerCase().startsWith('image/'))\n let generated = 0\n return images.map((file) =>\n hasUsableName(file.name)\n ? file\n : new File([file], pastedImageName(file.type, at, generated++), { type: file.type, lastModified: file.lastModified }),\n )\n}\n\nexport interface PastedImagesOptions {\n /**\n * The pictures a paste carried, named. Return `true` when the app took them\n * (an upload started), `false` to let the editor paste as it would have \u2014\n * for a box that cannot upload right now.\n */\n onImages: (files: File[]) => boolean\n}\n\n/**\n * Pasting a picture attaches it: a screenshot pasted into the editor reaches\n * the app as named files, the way the app's own file picker would hand them.\n * Text on the clipboard wins (see {@link pastedImages}).\n */\nexport const PastedImages = Extension.create<PastedImagesOptions>({\n name: 'pastedImages',\n addOptions() {\n return { onImages: () => false }\n },\n addProseMirrorPlugins() {\n const options = this.options\n return [\n new Plugin({\n key: new PluginKey('pastedImages'),\n props: {\n handlePaste: (_view, event) => {\n const files = pastedImages(event.clipboardData)\n return files.length > 0 && options.onImages(files)\n },\n },\n }),\n ]\n },\n})\n", "/** The editor's code block, coloured by language like RichText's (`@estiva-app/ui/editor`). */\nimport { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'\nimport { lowlight } from './codeColours'\n\n/*\n The editor library guesses a language for a block that names none, or names\n one it does not know, and colours it by the guess. RichText never guesses, so\n that block was coloured while written and plain once read. A block is\n coloured only by the language it names, while written and when read\n (Katerina, 9 October: never guess).\n*/\nconst namedOnly = {\n highlight: (language: string, value: string) => lowlight().highlight(language, value),\n highlightAuto: (value: string) => ({ type: 'root' as const, children: [{ type: 'text' as const, value }], data: { language: undefined, relevance: 0 } }),\n listLanguages: () => lowlight().listLanguages(),\n registered: (language: string) => lowlight().registered(language),\n}\n\n/**\n * A code block in the editor, with the colours RichText reads it in (Katerina,\n * 9 October): the same highlight.js languages, the same classes, and the\n * editor's root wears `richTextClassName`, which colours them.\n *\n * It replaces the starter kit's code block: `StarterKit.configure({ codeBlock: false })`,\n * then add `CodeBlockColours`. The block keeps its name, `codeBlock`, and its\n * `language` attribute, so nothing already written changes.\n */\nexport const CodeBlockColours = CodeBlockLowlight.configure({ lowlight: namedOnly })\n", "import { createElement, type ReactNode } from 'react'\nimport { common, createLowlight } from 'lowlight'\n\n/**\n * Colours inside a code block, by language (Katerina, 9 October).\n *\n * highlight.js reads the code and names each word (a keyword, a string, a\n * comment); RichText's class list gives each name its colour from the theme's\n * `syntax-*` tokens. The same instance colours the editor's code block\n * (`CodeBlockColours`), so what is written looks the same as what is read.\n *\n * `common` is highlight.js's 37 everyday languages. A language outside them, or\n * none, leaves the code in one colour.\n *\n * Made the first time code is coloured, not when the package loads: a call at\n * the top of the file is kept by every app's bundler, so an app that never draws\n * code carried highlight.js too (measured: an app importing only `Link` was\n * 55 KB gzipped).\n */\nlet instance: ReturnType<typeof createLowlight> | undefined\nexport function lowlight() {\n return (instance ??= createLowlight(common))\n}\n\ninterface HastNode {\n type: string\n value?: string\n properties?: { className?: string[] }\n children?: HastNode[]\n}\n\nfunction draw(node: HastNode, key: number): ReactNode {\n if (node.type === 'text') return node.value\n return createElement('span', { key, className: node.properties?.className?.join(' ') }, (node.children ?? []).map(draw))\n}\n\n/** The code, its words wrapped in highlight.js's classes; the text as it is when the language is unknown. */\nexport function codeColours(text: string, language?: string): ReactNode {\n if (!language || !lowlight().registered(language)) return text\n return (lowlight().highlight(language, text).children as HastNode[]).map(draw)\n}\n", "// @estiva-escape(component-has-a-page, component-has-a-story): draws nothing of its own \u2014 it opens a SuggestionMenu from a Tiptap suggestion plugin, and SuggestionMenu's page and stories show it (UIG-31)\nimport { forwardRef, useRef, useImperativeHandle } from 'react'\nimport { ReactRenderer } from '@tiptap/react'\nimport { exitSuggestion, type SuggestionKeyDownProps, type SuggestionOptions, type SuggestionProps } from '@tiptap/suggestion'\nimport { SuggestionMenu, type SuggestionMenuHandle, type SuggestionMenuRow, type SuggestionMenuSection } from './SuggestionMenu'\n\n/*\n * Is a type-ahead list open? \u2014 Peek's counter (`mention.tsx`), moved here with\n * the lists it counts (UIG-31).\n *\n * A counter rather than a boolean: two suggestion plugins can overlap for a\n * moment while one hands over to the other (`/` choosing \"Mention\" types `@`).\n * An editor asks before it acts on Enter or Escape, so a key that chose a row\n * does not also send the message or cancel the edit.\n *\n * A list counts while it shows rows, not while its trigger listens: since a\n * space stopped closing one (f586437a), \"meet @ 5pm\" leaves `@` listening\n * behind a hidden list to the end of the line, and that Enter must still send.\n *\n * And only while its editor has focus (71a4577b): a list left by a click ends,\n * or it would stay counted and swallow Escape in the box clicked into.\n */\nlet openCount = 0\nlet lastClose = 0\n\n/**\n * Open, or closed less than 100ms ago \u2014 what an Enter handler asks. Choosing a\n * row with Enter closes the list, and that same Enter must not also send.\n */\nexport function isSuggestionActive(): boolean {\n return openCount > 0 || Date.now() - lastClose < 100\n}\n\n/**\n * Open *now*, with no grace period \u2014 what an Escape handler asks. The first\n * Escape closes the list, the second is somebody's deliberate press.\n */\nexport function isSuggestionOpen(): boolean {\n return openCount > 0\n}\n\n/** How a list looks and what fills it \u2014 everything but the editor's own wiring. */\nexport interface SuggestionPopupOptions<T> {\n /** Names the list \u2014 \"Commands\", \"People\". */\n ariaLabel: string\n /** The box's width, e.g. `w-[300px]`. */\n width: string\n /** The cap before the rows scroll, e.g. `max-h-[400px]`. */\n maxHeight: string\n /** The plugin's items, and the typed query, \u2192 the sections to draw. One unlabeled section by default. */\n sections?: (items: T[], query: string) => readonly SuggestionMenuSection<T>[]\n itemKey: (item: T) => string\n row: (item: T) => SuggestionMenuRow\n /**\n * With `refresh`: called with a listener while the list is open; the app\n * calls it when its data has changed (a search landing), and the open list is\n * drawn again for the same query. Returns the unsubscribe, run when the list\n * closes however it closes.\n */\n subscribe?: (listener: () => void) => () => void\n /** The items for a query, as the plugin's `items` answers \u2014 asked again when `subscribe` fires. */\n refresh?: (query: string) => T[]\n}\n\ntype Props<T> = SuggestionProps<T> & { options: SuggestionPopupOptions<T> }\n\n// `forwardRef`, so Tiptap's `ReactRenderer` hands its `ref` through whichever\n// way it detects a component that takes one.\nconst Popup = forwardRef<SuggestionMenuHandle, Props<unknown>>(function Popup({ options, ...props }, handle) {\n const menu = useRef<SuggestionMenuHandle>(null)\n useImperativeHandle(handle, () => ({ onKeyDown: (event) => menu.current?.onKeyDown(event) ?? false }))\n const sections = options.sections ? options.sections(props.items, props.query) : [{ items: props.items }]\n return (\n <SuggestionMenu\n handle={menu}\n rect={props.clientRect?.() ?? null}\n editorElement={props.editor.view.dom}\n ariaLabel={options.ariaLabel}\n width={options.width}\n maxHeight={options.maxHeight}\n sections={sections}\n itemKey={options.itemKey}\n row={options.row}\n onSelect={(item) => props.command(item)}\n />\n )\n})\n\n/**\n * The `render` of a Tiptap suggestion plugin, drawn as a `SuggestionMenu` \u2014\n * the popup controller Peek wrote four times (`mention.tsx`, `slashCommands.tsx`),\n * once.\n *\n * Suggestion({ editor, char: '/', items, command,\n * render: suggestionPopup({ ariaLabel: 'Commands', width: 'w-[300px]',\n * maxHeight: 'max-h-[400px]', itemKey, row, sections }) })\n *\n * It mounts the list when the query starts and removes it when it ends: the\n * plugin owns the list's life. Escape closes it and is used up; \u2191, \u2193 and Enter\n * go to the list; every other key stays the editor's. The list portals itself\n * (the package `Popover`), so the mount point is only a mount point.\n */\nexport function suggestionPopup<T>(options: SuggestionPopupOptions<T>): NonNullable<SuggestionOptions<T>['render']> {\n return () => {\n let component: ReactRenderer<SuggestionMenuHandle, Props<T>> | null = null\n let container: HTMLDivElement | null = null\n let last: SuggestionProps<T> | null = null\n let unsubscribe: (() => void) | undefined\n let counted = false\n let unlisten: (() => void) | undefined\n const count = (shown: boolean) => {\n if (shown === counted) return\n counted = shown\n openCount = shown ? openCount + 1 : Math.max(0, openCount - 1)\n }\n // The plugin's state outlives a blur \u2014 no transaction runs \u2014 so leaving the\n // box ends the query here, as Escape does: coming back finds no list, and\n // Enter sends (Miky, 2026-10-06). Leaving the window is not leaving the box,\n // and a press in the list's own panel never takes focus (SuggestionMenu).\n const listen = (props: SuggestionProps<T>) => {\n const { view } = props.editor\n const dom = view.dom\n const onBlur = () => {\n // A window that loses focus leaves its active element where it was.\n if (dom.contains(document.activeElement) || !last) return\n const from = last.range.from\n // Not a pick: no grace for the other box's Enter.\n count(false)\n for (const plugin of view.state.plugins) {\n const state = plugin.getState(view.state) as { active?: boolean; range?: { from: number } } | undefined\n if (state?.active && state.range?.from === from && plugin.spec.key) exitSuggestion(view, plugin.spec.key)\n }\n }\n dom.addEventListener('blur', onBlur)\n unlisten = () => dom.removeEventListener('blur', onBlur)\n }\n const exit = () => {\n unsubscribe?.()\n unsubscribe = undefined\n unlisten?.()\n unlisten = undefined\n last = null\n // The grace is for the Enter that picked a row and closed the list, not\n // for a keystroke that only left it with no rows.\n if (counted) lastClose = Date.now()\n count(false)\n if (!component) return\n component.destroy()\n container?.remove()\n component = null\n container = null\n }\n return {\n onStart: (props) => {\n count(props.items.length > 0)\n component = new ReactRenderer<SuggestionMenuHandle, Props<T>>(Popup as never, { props: { ...props, options }, editor: props.editor })\n container = document.createElement('div')\n document.body.appendChild(container)\n container.appendChild(component.element)\n last = props\n listen(props)\n const { subscribe, refresh } = options\n if (subscribe && refresh) {\n // The plugin asks for items only when the query changes; an answer\n // landing later draws the open list again with the same props.\n unsubscribe = subscribe(() => {\n if (!last || !component) return\n last = { ...last, items: refresh(last.query) }\n count(last.items.length > 0)\n component.updateProps({ ...last, options })\n })\n }\n },\n onUpdate: (props) => {\n last = props\n count(props.items.length > 0)\n component?.updateProps({ ...props, options })\n },\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'Escape') {\n exit()\n return true\n }\n return component?.ref?.onKeyDown(event) ?? false\n },\n onExit: exit,\n }\n }\n}\n", "import { useMemo, type ReactNode } from 'react'\nimport type { Editor } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\nimport { IconCopy, IconTrash } from '@tabler/icons-react'\nimport { BlockHandle, blockAtY, type BlockHandleEditing, type HandleBlock } from './BlockHandle'\nimport { MenuItem, MenuSub } from './Menu'\n\n/** A row of the block menu's Turn into: what the block becomes. */\nexport interface BlockTurnInto {\n label: string\n /** Turns the block the caret is in. The handle puts the caret there first. */\n run: (editor: Editor) => void\n /** Whether the block already is this \u2014 drawn as the selected row. */\n active?: (node: ProseMirrorNode) => boolean\n}\n\nexport interface EditorBlockHandleProps {\n /** The editor. Not editable, or not there yet, and the handle reads: the caller's rows only. */\n editor: Editor | null\n /** Turn into's rows. Offered for a block of text \u2014 a paragraph, a heading, code. */\n turnInto?: readonly BlockTurnInto[]\n /** The caller's own rows, after the editing ones. */\n actions?: (block: HandleBlock) => ReactNode\n /** The `EditorContent`. */\n children: ReactNode\n className?: string\n}\n\n/** The editor's top-level blocks, where each starts and what it is. */\nfunction editorBlocks(view: EditorView): HandleBlock[] {\n const out: HandleBlock[] = []\n view.state.doc.forEach((node, pos) => {\n const element = view.nodeDOM(pos)\n if (element instanceof HTMLElement) out.push({ id: (node.attrs.blockId as string | null) || undefined, element, pos })\n })\n return out\n}\n\n/*\n Duplicate and Delete leave the editor focused, as Turn into and a drop do: the\n person is still in the field, so an editor that saves when they leave it saves\n these too \u2014 and one that loads someone else's edit while unfocused does not\n load it over them.\n*/\n\n/** A copy of the block straight after it, the caret in the copy. `BlockId` gives the copy a fresh id; the original keeps its own. */\nexport function duplicateBlock(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos)\n if (!node) return false\n editor.view.dispatch(editor.state.tr.insert(pos + node.nodeSize, node).scrollIntoView())\n editor.commands.focus(pos + node.nodeSize + 1)\n return true\n}\n\n/** Removes the block, the caret where it was. The last block of a document becomes an empty paragraph, since a document holds at least one. */\nexport function deleteBlock(editor: Editor, pos: number): boolean {\n const { state } = editor\n const node = state.doc.nodeAt(pos)\n if (!node) return false\n const tr = state.doc.childCount === 1 ? state.tr.replaceWith(pos, pos + node.nodeSize, state.schema.nodes.paragraph.create()) : state.tr.delete(pos, pos + node.nodeSize)\n editor.view.dispatch(tr)\n editor.commands.focus(Math.min(pos + 1, editor.state.doc.content.size))\n return true\n}\n\n/**\n * Moves the block at `from` to `to` (a position between top-level blocks), the\n * caret in it: what a drop in the gutter does, where the editor has no drop of\n * its own. The node moves whole, attributes and all, so it keeps its id, as\n * ProseMirror's drop keeps it.\n */\nexport function moveBlock(editor: Editor, from: number, to: number): boolean {\n const node = editor.state.doc.nodeAt(from)\n if (!node || (to >= from && to <= from + node.nodeSize)) return false\n const tr = editor.state.tr.delete(from, from + node.nodeSize)\n const at = tr.mapping.map(to)\n editor.view.dispatch(tr.insert(at, node).scrollIntoView())\n editor.commands.focus(at + 1)\n return true\n}\n\n/**\n * Starts the browser's own drag of the block, as ProseMirror starts one of a\n * selected node: the block selected, its slice on `view.dragging` with `move`,\n * and its HTML on the transfer. ProseMirror's drop then deletes the selection\n * and inserts the slice with its attributes \u2014 so the block arrives with the\n * id it left with, and every comment anchored to it still finds it.\n */\nexport function startBlockDrag(editor: Editor, pos: number, event: Pick<globalThis.DragEvent, 'dataTransfer'>, image?: Element) {\n editor.commands.setNodeSelection(pos)\n const { view } = editor\n const slice = view.state.selection.content()\n const { dom, text } = view.serializeForClipboard(slice)\n const transfer = event.dataTransfer\n if (transfer) {\n transfer.clearData()\n transfer.setData('text/html', dom.innerHTML)\n transfer.setData('text/plain', text)\n transfer.effectAllowed = 'copyMove'\n if (image) transfer.setDragImage(image, 0, 0)\n }\n view.dragging = { slice, move: true }\n}\n\n/**\n * The drop line a dragged block shows, in the package's accent instead of the\n * editor's default (the text's colour). Give it to the editor's StarterKit:\n * `StarterKit.configure({ dropcursor: blockDropCursor })`.\n */\nexport const blockDropCursor = { color: false, class: 'bg-accent-primary' } as const\n\nconst NOTHING_YET: BlockHandleEditing = { find: () => null, rows: () => null, startDrag: () => {}, drop: () => {}, endDrag: () => {}, onChange: () => () => {} }\n\n/**\n * `BlockHandle` in an editor: drag the handle to move the block, and the menu\n * gains Turn into, Duplicate and Delete before the caller's rows.\n */\nexport function EditorBlockHandle({ editor, turnInto = [], actions, children, className }: EditorBlockHandleProps) {\n const editing = useMemo<BlockHandleEditing>(() => {\n // Before the editor exists there is no block to find \u2014 not the editor's whole element read as one.\n if (!editor) return NOTHING_YET\n // Where the block being dragged starts.\n let dragged: number | undefined\n return {\n find: (y) => blockAtY(editorBlocks(editor.view), y),\n rows: (block) => {\n const pos = block.pos\n // Not editable: the caller's rows only, and no drag.\n if (!editor.isEditable) return null\n const node = pos === undefined ? null : editor.state.doc.nodeAt(pos)\n if (pos === undefined || !node) return null\n return (\n <>\n {node.isTextblock && turnInto.length > 0 && (\n <MenuSub label=\"Turn into\">\n {turnInto.map((row) => (\n <MenuItem\n key={row.label}\n label={row.label}\n selected={row.active?.(node) ?? false}\n onClick={() => {\n // Inside the block's text, so the row's command turns this block and no other.\n editor.chain().focus().setTextSelection(pos + 1).run()\n row.run(editor)\n }}\n />\n ))}\n </MenuSub>\n )}\n <MenuItem label=\"Duplicate\" leading={<IconCopy size={16} stroke={1.5} className=\"text-text-secondary\" />} onClick={() => duplicateBlock(editor, pos)} />\n <MenuItem label=\"Delete\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={() => deleteBlock(editor, pos)} />\n </>\n )\n },\n startDrag: (block, event) => {\n dragged = block.pos\n if (block.pos !== undefined) startBlockDrag(editor, block.pos, event, block.element)\n },\n drop: (target, after) => {\n const node = target.pos === undefined ? null : editor.state.doc.nodeAt(target.pos)\n if (dragged === undefined || target.pos === undefined || !node) return\n moveBlock(editor, dragged, after ? target.pos + node.nodeSize : target.pos)\n },\n // ProseMirror clears `dragging` on its own dragend, which a drag started outside its element never reaches.\n endDrag: () => setTimeout(() => (editor.view.dragging = null), 50),\n onChange: (changed) => {\n editor.on('update', changed)\n return () => {\n editor.off('update', changed)\n }\n },\n }\n }, [editor, turnInto])\n\n return (\n <BlockHandle editing={editing} actions={actions} className={className}>\n {children}\n </BlockHandle>\n )\n}\n", "import { useCallback, useEffect, useRef, useState, type MouseEvent, type ReactNode } from 'react'\nimport type { Editor } from '@tiptap/core'\nimport { TextSelection, type Command } from '@tiptap/pm/state'\nimport { addColumnAfter, addColumnBefore, addRowAfter, addRowBefore, deleteColumn, deleteRow, deleteTable, toggleHeader } from '@tiptap/pm/tables'\nimport { IconColumnInsertLeft, IconColumnInsertRight, IconGripHorizontal, IconGripVertical, IconPlus, IconRowInsertBottom, IconRowInsertTop, IconTrash } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\nimport { FLOATING_SURFACE_CLASSES } from './looks'\nimport { Menu, MenuItem, MenuSeparator } from './Menu'\n\nexport interface EditorTableControlsProps {\n /** The editor, with TableKit (`@tiptap/extension-table`) in its extensions. Not editable, or not there yet: no controls. */\n editor: Editor | null\n /** The `EditorContent`. */\n children: ReactNode\n className?: string\n}\n\n/** The cell the pointer is on, and the table and row it is in. */\ninterface Hovered {\n table: HTMLTableElement\n row: HTMLTableRowElement\n cell: HTMLTableCellElement\n}\n\n/** Where the controls go, measured from the frame: they depend on the document, not on a size of ours. */\ninterface Placed {\n tableBox: { top: number; left: number; width: number; height: number }\n rowBox: { top: number; height: number }\n columnBox: { left: number; width: number }\n}\n\n/*\n Every row runs the table command with the caret in a cell, as the command\n expects \u2014 the hovered cell's, or for a \u2795 bar the last row's or the last\n column's \u2014 and leaves the editor focused there, as the block menu does: the\n person is still in the field, so an editor that saves when they leave it\n saves this too.\n*/\n\n/** Puts the caret in `cell` and runs `command` there. */\nfunction inCell(editor: Editor, cell: HTMLTableCellElement, command: Command): boolean {\n const { view } = editor\n if (!view.dom.contains(cell)) return false\n const at = view.posAtDOM(cell, 0)\n view.dispatch(view.state.tr.setSelection(TextSelection.near(view.state.doc.resolve(at))))\n const done = command(view.state, view.dispatch, view)\n editor.commands.focus()\n return done\n}\n\n/** A row of header cells only: the table's header row. */\nfunction isHeaderRow(row: HTMLTableRowElement): boolean {\n return row.cells.length > 0 && Array.from(row.cells).every((c) => c.tagName === 'TH')\n}\n\n/**\n * The cell's row, or the whole table when it is its only one: a table holds at\n * least one row (prosemirror-tables refuses to delete it). Deleting the header\n * row makes the next row the header, so the table still opens with one.\n */\nexport function deleteTableRow(editor: Editor, cell: HTMLTableCellElement): boolean {\n const row = cell.closest('tr')\n const table = cell.closest('table')\n if (!row || !table || table.rows.length <= 1) return inCell(editor, cell, deleteTable)\n const header = isHeaderRow(row)\n return inCell(editor, cell, header ? (state, dispatch, view) => deleteRow(state, dispatch) && (!view || toggleHeader('row')(view.state, view.dispatch)) : deleteRow)\n}\n\n/** The same for a column: the last one deletes the table. */\nexport function deleteTableColumn(editor: Editor, cell: HTMLTableCellElement): boolean {\n const row = cell.closest('tr')\n return inCell(editor, cell, row && row.cells.length > 1 ? deleteColumn : deleteTable)\n}\n\n/** A row after the last one: the \u2795 under the table. */\nexport function appendTableRow(editor: Editor, table: HTMLTableElement): boolean {\n const last = table.rows[table.rows.length - 1]?.cells[0]\n return !!last && inCell(editor, last, addRowAfter)\n}\n\n/** A column after the last one: the \u2795 right of the table. */\nexport function appendTableColumn(editor: Editor, table: HTMLTableElement): boolean {\n const first = table.rows[0]\n const last = first?.cells[first.cells.length - 1]\n return !!last && inCell(editor, last, addColumnAfter)\n}\n\nfunction place(frame: HTMLElement, { table, row, cell }: Hovered): Placed {\n const at = frame.getBoundingClientRect()\n const t = table.getBoundingClientRect()\n const r = row.getBoundingClientRect()\n const c = cell.getBoundingClientRect()\n return {\n tableBox: { top: t.top - at.top, left: t.left - at.left, width: t.width, height: t.height },\n rowBox: { top: r.top - at.top, height: r.height },\n columnBox: { left: c.left - at.left, width: c.width },\n }\n}\n\nconst ICON = { size: 16, stroke: 1.5, className: 'text-text-secondary' } as const\n\n/**\n * A \u2795 press keeps the focus where it is, as the selection toolbar's buttons do:\n * the person never left the field, so an editor that saves when they leave it\n * does not save half-way, and History gets one line for the edit, not one per\n * press. (A handle's menu takes the focus, as every menu does.)\n */\nconst keepFocus = (e: MouseEvent) => e.preventDefault()\n\n/** What a handle and a \u2795 bar sit in: the elevated surface's border and fill, opaque over the table's lines, with no shadow at this size. */\nconst HANDLE_BOX = cn(FLOATING_SURFACE_CLASSES, 'shadow-none')\n\n/**\n * A table's own controls in an editor, as in Notion: point at a cell and a\n * handle sits on its row's left edge and on its column's top edge; press one\n * for Insert above and below (left and right), Delete, and Delete table. On\n * the last row a \u2795 bar under the table adds a row, and on the last column one\n * right of it adds a column. Tab in\n * the last cell adds a row too \u2014 that is TableKit's own.\n *\n * Deleting a table's only row or only column deletes the table, which cannot\n * be left with none.\n *\n * Built on the editor library's own table commands; the controls are ours.\n * Wrap the `EditorContent` in it, inside an `EditorBlockHandle` if there is\n * one: the block handle stays the table's, in the gutter.\n */\nexport function EditorTableControls({ editor, children, className }: EditorTableControlsProps) {\n const frame = useRef<HTMLDivElement>(null)\n // The cell pointed at and where its controls go, as one: the boxes mean nothing without their elements.\n const [hovered, setHovered] = useState<(Hovered & Placed) | null>(null)\n // A menu that is up keeps its row and column, wherever the pointer goes.\n const [menu, setMenu] = useState<'row' | 'column' | null>(null)\n const editable = !!editor && editor.isEditable\n\n const follow = useCallback(\n (target: EventTarget | null) => {\n if (!editor || !frame.current || !(target instanceof Element)) return\n // On a control of ours: keep what it was drawn for.\n if (target.closest('[data-table-control]')) return\n const cell = target.closest<HTMLTableCellElement>('td, th')\n const row = cell?.closest('tr')\n const table = row?.closest('table')\n if (!cell || !row || !table || !editor.view.dom.contains(table)) {\n setHovered(null)\n return\n }\n // Measured on every move, the same cell too: a panel resized or a cell's text wrapping moves the edges under a still pointer.\n const next = { table, row, cell, ...place(frame.current, { table, row, cell }) }\n setHovered((was) => (was && was.cell === cell && JSON.stringify([was.tableBox, was.rowBox, was.columnBox]) === JSON.stringify([next.tableBox, next.rowBox, next.columnBox]) ? was : next))\n },\n [editor],\n )\n\n /*\n Any change to the document moves everything under the controls, so they go\n until the pointer moves again: a row added or deleted here, and someone\n else's edit loaded into the field \u2014 which an app does without an `update`\n (`emitUpdate: false`), and which redraws the cells the controls point at.\n */\n useEffect(() => {\n if (!editor) return\n const changed = ({ transaction }: { transaction: { docChanged: boolean } }) => {\n if (transaction.docChanged && !menu) setHovered(null)\n }\n editor.on('transaction', changed)\n return () => {\n editor.off('transaction', changed)\n }\n }, [editor, menu])\n\n const shown = editable ? hovered : null\n const run = (act: () => boolean) => () => {\n if (editor) act()\n setMenu(null)\n setHovered(null)\n }\n\n return (\n <div\n ref={frame}\n className={cn('relative', className)}\n onMouseMove={(e) => {\n if (!menu) follow(e.target)\n }}\n onMouseLeave={() => {\n if (!menu) setHovered(null)\n }}\n >\n {children}\n {shown && editor && (\n <>\n {/* The row's handle, centred on the table's left edge: the box is ours, so the table's line does not show through it. */}\n <div data-table-control className={cn(HANDLE_BOX, 'absolute -translate-x-1/2 -translate-y-1/2')} style={{ left: shown.tableBox.left, top: shown.rowBox.top + shown.rowBox.height / 2 }}>\n <Menu\n open={menu === 'row'}\n onOpenChange={(open) => setMenu(open ? 'row' : null)}\n trigger={\n <IconButton aria-label=\"Row menu\" tooltip=\"Row\" className=\"h-6 w-4 p-0\">\n <IconGripVertical size={16} stroke={1.5} />\n </IconButton>\n }\n >\n {/* Nothing goes above a header row: it is the table's first row, and a header anywhere else is no header (RichText, the agent's text). */}\n {!isHeaderRow(shown.row) && <MenuItem label=\"Insert above\" leading={<IconRowInsertTop {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addRowBefore))} />}\n <MenuItem label=\"Insert below\" leading={<IconRowInsertBottom {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addRowAfter))} />\n <MenuSeparator />\n <MenuItem label=\"Delete\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => deleteTableRow(editor, shown.cell))} />\n <MenuItem label=\"Delete table\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => inCell(editor, shown.cell, deleteTable))} />\n </Menu>\n </div>\n {/* The column's handle, centred on the table's top edge. */}\n <div data-table-control className={cn(HANDLE_BOX, 'absolute -translate-x-1/2 -translate-y-1/2')} style={{ left: shown.columnBox.left + shown.columnBox.width / 2, top: shown.tableBox.top }}>\n <Menu\n open={menu === 'column'}\n onOpenChange={(open) => setMenu(open ? 'column' : null)}\n trigger={\n <IconButton aria-label=\"Column menu\" tooltip=\"Column\" className=\"h-4 w-6 p-0\">\n <IconGripHorizontal size={16} stroke={1.5} />\n </IconButton>\n }\n >\n <MenuItem label=\"Insert left\" leading={<IconColumnInsertLeft {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addColumnBefore))} />\n <MenuItem label=\"Insert right\" leading={<IconColumnInsertRight {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addColumnAfter))} />\n <MenuSeparator />\n <MenuItem label=\"Delete\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => deleteTableColumn(editor, shown.cell))} />\n <MenuItem label=\"Delete table\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => inCell(editor, shown.cell, deleteTable))} />\n </Menu>\n </div>\n {/*\n The \u2795 bars, flush with the table so the pointer reaches them from a\n cell without leaving it \u2014 the row's from the last row, the column's\n from the last column. Anywhere else the one under the table would\n lie over the top of the next block for nothing.\n */}\n {shown.row === shown.table.rows[shown.table.rows.length - 1] && (\n <div data-table-control className={cn(HANDLE_BOX, 'absolute flex h-4')} style={{ left: shown.tableBox.left, top: shown.tableBox.top + shown.tableBox.height, width: shown.tableBox.width }}>\n <IconButton aria-label=\"Add row\" tooltip=\"Add row\" className=\"h-full w-full p-0\" onMouseDown={keepFocus} onClick={run(() => appendTableRow(editor, shown.table))}>\n <IconPlus size={12} stroke={1.5} />\n </IconButton>\n </div>\n )}\n {shown.cell === shown.row.cells[shown.row.cells.length - 1] && (\n <div data-table-control className={cn(HANDLE_BOX, 'absolute flex w-4')} style={{ left: shown.tableBox.left + shown.tableBox.width, top: shown.tableBox.top, height: shown.tableBox.height }}>\n <IconButton aria-label=\"Add column\" tooltip=\"Add column\" className=\"h-full w-full p-0\" onMouseDown={keepFocus} onClick={run(() => appendTableColumn(editor, shown.table))}>\n <IconPlus size={12} stroke={1.5} />\n </IconButton>\n </div>\n )}\n </>\n )}\n </div>\n )\n}\n", "/**\n * Looks two or more parts share, written once (UIG-25).\n *\n * A look typed again by hand draws the same today and drifts the day one of\n * them changes: nothing fails, one screen just looks slightly off. So a look\n * more than one part wears lives here, and each part takes it from here. The\n * lint that finds a copied look reads a class list wherever it is written; a\n * constant of classes ends in `_CLASSES`, so it is read as one.\n *\n * Only looks \u2014 colour, text, border, corner, shadow. Where a part sits and how\n * much room it takes stay in the part.\n */\n\n/**\n * A field's box: TextInput and Textarea. The hairline strengthens on hover \u2014 of the box, or of anywhere in the `Field` around it, label included, so the field is one thing under the pointer (Katerina, 28 September) \u2014 and not while switched off: a label passes its hover to its box, switched off or not (FIELD_DISABLED_CLASSES holds it back). Focus outranks it, so a focused field keeps its colour under the pointer.\n */\nexport const FIELD_BOX_CLASSES = 'bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong focus:border-border-focus group-hover/field:focus:border-border-focus rounded-lg'\n\n/** What a field's words look like, typed or waiting: TextInput, Textarea and Select. */\nexport const FIELD_TEXT_CLASSES = 'text-text-primary placeholder:text-text-muted'\n\n/** A field that cannot be used: TextInput, Textarea and Select. */\nexport const FIELD_DISABLED_CLASSES = 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled disabled:border-border-field group-hover/field:disabled:border-border-field'\n\n/** The ring a focused field wears in Signal: TextInput and Textarea. */\nexport const FIELD_FOCUS_RING_CLASSES = 'signal:transition-shadow signal:focus:shadow-focus-ring'\n\n/** A field's two sizes, its padding and its words: TextInput, Textarea (default only) and Select. The small one is the small Select's trigger. */\nexport const FIELD_SIZE_CLASSES = {\n default: 'px-3 py-2 text-input-value',\n small: 'h-6 min-h-6 px-2 text-caption',\n} as const\n\n/** The box around an input that sits inside it, before its focus look: SearchInput and ChipInput. */\nexport const FIELD_SHELL_CLASSES = 'bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong rounded-lg transition-colors'\n\n/** A panel that floats over the page: Menu's panel and Tooltip. */\nexport const FLOATING_SURFACE_CLASSES = 'rounded-lg border border-border-default bg-bg-elevated shadow-lg'\n\n/** A count beside a label, in the muted colour: NavItem's number and SectionHeader's `count`. */\nexport const COUNT_TEXT_CLASSES = 'font-mono text-caption tabular-nums text-text-muted'\n\n/** A chip's words: Chip's label and Reaction's count. */\nexport const CHIP_TEXT_CLASSES = 'text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums'\n\n/** Small secondary words that turn mono under signal: Kbd's key, CommandPalette's footer and MembersPill's count. */\nexport const MONO_CAPTION_CLASSES = 'text-caption text-text-secondary signal:font-mono signal:text-small'\n\n/** Words typed straight into an input with no box of its own: SearchInput and ChipInput. */\nexport const BARE_INPUT_CLASSES = 'bg-transparent text-text-primary placeholder:text-text-muted outline-none'\n\n/** A sidebar row's shape and its words at rest, brightening under the pointer: NavItem, and SectionHeader's `row` (a folder among rows). */\n// 8px corners, a MenuItem row's (Katerina, 2 October).\nexport const SIDEBAR_ROW_CLASSES = 'rounded-lg transition-colors'\nexport const SIDEBAR_ROW_TEXT_CLASSES = 'text-text-secondary hover:text-text-primary'\n\n/** A quiet icon control: secondary, brightening on a hover fill \u2014 IconButton's `muted`. */\nexport const MUTED_CONTROL_CLASSES = 'text-text-secondary hover:bg-bg-hover hover:text-text-primary'\n\n/** A control that opens a menu: a hover fill, held while its menu is up \u2014 PersonTrigger's row, and a ContainerHeader title with a menu. */\nexport const MENU_TRIGGER_CLASSES = 'cursor-pointer rounded-md transition-colors hover:bg-bg-hover data-[popup-open]:bg-bg-hover'\n\n/**\n * A panel that slides open and shut on Base UI's Collapsible: Base UI measures\n * it and writes its height to a variable, `auto` again once the slide ends, and\n * the panel is 0 high on its opening and closing frames. None under\n * prefers-reduced-motion. CollapsibleSection's rows, and the rows under a NavItem.\n */\nexport const COLLAPSIBLE_PANEL_CLASSES = 'h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0'\n", "import type { ReactNode } from 'react'\nimport { Extension, type Editor } from '@tiptap/core'\nimport { PluginKey } from '@tiptap/pm/state'\nimport Mention, { type MentionOptions } from '@tiptap/extension-mention'\nimport Suggestion from '@tiptap/suggestion'\nimport { NodeViewWrapper, ReactNodeViewRenderer, type ReactNodeViewProps } from '@tiptap/react'\nimport { Avatar } from './Avatar'\nimport { EnterHint } from './Menu'\nimport { inlineChipClassName } from './InlineChip'\nimport type { SuggestionMenuRow } from './SuggestionMenu'\nimport { suggestionPopup, type SuggestionPopupOptions } from './suggestionPopup'\n\n/*\n * The four composer triggers every Estiva app shares \u2014 `@`, `!@`, `[` and `/`\n * (CON-26, Miky 2026-10-02). Peek built them first and they move here unchanged\n * (CON-27); an app passes in only its own data: its people, the things `[` can\n * reference, and the `/` rows it can perform beside the shared Format section.\n *\n * **What a pick writes is not here.** The text a chip becomes in the body and\n * the tags it earns are `@estiva-app/conversation`'s (`mentionText`,\n * `messageReference`, `urgentTagsFor`), so a client without React writes the\n * same bytes. A chip here carries what that needs: a person's `pubkey`, a\n * reference's `uri`.\n */\n\n// \u2500\u2500\u2500 @ and !@ \u2500\u2500\u2500\n\n/** A person `@` and `!@` offer. */\nexport interface MentionPerson {\n /** The app's own key for the person \u2014 the row's key and the chip's `id`. */\n id: string\n name: string\n /** The second line of the row: a role, a team. */\n description?: string\n /** Their Nostr key, when they have one \u2014 what the body is written with (SPEC \u00A713.1). */\n pubkey?: string | null\n /** The face at the row's start. Default: the package `Avatar` for the name. */\n avatar?: ReactNode\n}\n\n/** How many people the list shows. */\nexport const PEOPLE_LIMIT = 6\n\n/** The people whose name holds the query, the first {@link PEOPLE_LIMIT} of them. */\nexport function filterPeople(people: readonly MentionPerson[], query: string): MentionPerson[] {\n const q = query.toLowerCase()\n return people.filter((person) => q === '' || person.name.toLowerCase().includes(q)).slice(0, PEOPLE_LIMIT)\n}\n\n/**\n * The people list `@` and `!@` open: the face, the name, the second line, and\n * the Enter hint while highlighted \u2014 `MenuItem` at its tall size.\n */\nexport function peopleMenu(urgent: boolean): SuggestionPopupOptions<MentionPerson> {\n const section = urgent ? 'Urgent mention' : 'People'\n return {\n ariaLabel: section,\n // The people and the reference lists are the same box: 658px, capped at 360.\n width: 'w-[658px]',\n maxHeight: 'max-h-[360px]',\n sections: (people) => [{ label: section, items: people, className: 'px-3 py-1' }],\n itemKey: (person) => person.id,\n row: (person) => ({\n label: person.name,\n description: person.description,\n leading: person.avatar ?? <Avatar size={32} alt={person.name} />,\n hint: <EnterHint />,\n size: 'tall',\n }),\n }\n}\n\nexport interface PersonMentionOptions {\n /** Everyone the list can offer, asked each time the query changes. */\n people: () => readonly MentionPerson[]\n}\n\ntype Doc = { descendants: (cb: (node: { type: { name: string }; attrs: Record<string, unknown> }) => boolean | void) => void }\n\nfunction optionsOf<T>(editor: Editor, name: string): T | undefined {\n return editor.extensionManager.extensions.find((one) => one.name === name)?.options as T | undefined\n}\n\n/**\n * A list keeps listening across spaces, so a name or a title can be more than\n * one word (f586437a, Miky 2026-10-06). It stops at a new line, Escape, a pick\n * or the cursor leaving \u2014 Suggestion's own \u2014 and once the query holds `stop`:\n * `]` for `[`, so \"see [the notes] above\" ends as text. While nothing matches,\n * the list is hidden rather than closed, and comes back if the words match again.\n * A space straight after the key is prose (\"meet @ 5pm\", \"- [ ] task\"), not a\n * query: every two-word name holds a space.\n */\nconst listening = (stop: string) => ({\n allowSpaces: true,\n shouldShow: ({ query }: { query: string }) => !/^\\s/.test(query) && !query.includes(stop),\n})\n\n/** `@`'s list and pick, or `!@`'s, for the node `name` \u2014 the extension's own, so a renamed one still finds its people. */\nfunction personSuggestion(urgent: boolean, name: string) {\n return {\n ...(urgent ? { char: '!@' } : {}),\n // A second `@` starts another mention; the first one has ended.\n ...listening('@'),\n items: ({ query, editor }: { query: string; editor: Editor }) => filterPeople(optionsOf<PersonMentionOptions>(editor, name)?.people() ?? [], query),\n render: suggestionPopup(peopleMenu(urgent)),\n command: ({ editor, range, props: person }: { editor: Editor; range: { from: number; to: number }; props: MentionPerson }) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n { type: name, attrs: { id: person.id, label: person.name, pubkey: person.pubkey ?? null } },\n { type: 'text', text: ' ' },\n ])\n .run()\n },\n }\n}\n\n/*\n What a chip keeps through HTML \u2014 a copy and paste, inside an editor or\n between two. The chip's own `renderHTML` writes each as a `data-*` attribute\n and these read them back. Until CON-27 none was written, so a pasted chip\n came back with every value null and was sent as `@null`. What is read is\n checked: a crafted `data-pubkey` that is not a key, or a `data-uri` that is\n not a `nostr:` reference, is dropped rather than written into the body.\n*/\nconst HEX_KEY = /^[0-9a-f]{64}$/\nconst NOSTR_URI = /^nostr:[a-z0-9]+$/i\nconst fromData = (name: string, valid?: RegExp) => ({\n parseHTML: (element: HTMLElement) => {\n const value = element.getAttribute(`data-${name}`)\n return value !== null && (!valid || valid.test(value)) ? value : null\n },\n // The node's own `renderHTML` writes it.\n renderHTML: () => ({}),\n})\n\nconst personAttributes = () => ({\n id: { default: null, ...fromData('id') },\n label: { default: null, ...fromData('label') },\n /**\n * The person's Nostr key, when they have one \u2014 SPEC \u00A713.1. `id` is the app's\n * own and means nothing to another app; this is what the body is written\n * with, so a mention survives a rename.\n */\n pubkey: { default: null, ...fromData('pubkey', HEX_KEY) },\n})\n\n/** The `data-*` a person chip is written with, so a paste reads it back. */\nfunction personData(attrs: Record<string, unknown>): Record<string, string> {\n return {\n 'data-id': String(attrs.id ?? ''),\n 'data-label': String(attrs.label ?? ''),\n ...(typeof attrs.pubkey === 'string' && attrs.pubkey ? { 'data-pubkey': attrs.pubkey } : {}),\n }\n}\n\n/**\n * `@` \u2014 a person, as a chip that writes their key (SPEC \u00A713.1).\n *\n * PersonMention.configure({ people: () => directory })\n *\n * Its `pubkey` and `label` are what `mentionText` takes.\n */\nexport const PersonMention = Mention.extend<MentionOptions<MentionPerson, MentionPerson> & PersonMentionOptions>({\n name: 'mention',\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: {}, people: () => [], suggestion: personSuggestion(false, this.name) }\n },\n addAttributes: personAttributes,\n renderHTML({ node }) {\n // No node view, so this spec is what the editor draws: the one chip.\n return ['span', { 'data-mention': 'true', ...personData(node.attrs), class: inlineChipClassName('person', 'cursor-default') }, `@${node.attrs.label}`]\n },\n parseHTML() {\n return [{ tag: 'span[data-mention]' }]\n },\n})\n\n/**\n * `!@` \u2014 a person, urgently: the same chip in the urgent tone. Its text is an\n * ordinary mention; the urgency travels as a tag (`urgentTagsFor`), so hand the\n * write {@link urgentPubkeys}.\n */\nexport const UrgentPersonMention = Mention.extend<MentionOptions<MentionPerson, MentionPerson> & PersonMentionOptions>({\n name: 'urgentMention',\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: {}, people: () => [], suggestion: personSuggestion(true, this.name) }\n },\n addAttributes: personAttributes,\n renderHTML({ node }) {\n return [\n 'span',\n { 'data-urgent-mention': 'true', ...personData(node.attrs), class: inlineChipClassName('urgent', 'cursor-default') },\n `@${node.attrs.label}`,\n ]\n },\n parseHTML() {\n return [{ tag: 'span[data-urgent-mention]' }]\n },\n})\n\n/**\n * The keys the document's urgent mentions name \u2014 what the write turns into\n * `[\"urgent\", <pubkey>]` tags. A chip with no key has no tag to carry; its text\n * is the old `!@Name`.\n */\nexport function urgentPubkeys(editor: { state: { doc: Doc } }): string[] | undefined {\n const urgent: string[] = []\n editor.state.doc.descendants((node) => {\n if (node.type.name === 'urgentMention' && typeof node.attrs.pubkey === 'string' && node.attrs.pubkey) urgent.push(node.attrs.pubkey)\n })\n return urgent.length > 0 ? urgent : undefined\n}\n\n// \u2500\u2500\u2500 [ \u2500\u2500\u2500\n\n/** Something `[` offers: a reference drawn as who said it and how it opens. */\nexport interface CaptionedItem {\n /** The referenced thing's id \u2014 the row's key and the chip's `id`. */\n id: string\n /** Who, as the list shows them. */\n label: string\n /** The opening words, marks stripped: the caption the row and the chip wear. */\n snippet: string\n /** `nostr:\u2026`, exactly as the body will carry it (`messageReference`). */\n uri: string\n /** What the query is matched against beside `label`. Default: `snippet`. */\n search?: string\n /** The row's second line, e.g. where the thing sits. The chip does not wear it. */\n description?: string\n /**\n * This row's own 16px icon, in place of the list's `icon`: what the thing is,\n * when the list holds several kinds \u2014 a topic, a project, a message\n * (Katerina, 7 October). Absent, the list's icon.\n */\n icon?: ReactNode\n}\n\n/** One labelled group of `[`'s rows, in the order the list draws them. */\nexport interface ReferenceSection {\n label: string\n items: readonly CaptionedItem[]\n}\n\n/** How many references the list shows. */\nexport const REFERENCE_LIMIT = 6\n\n/**\n * The references whose label or text holds the query \u2014 the last\n * {@link REFERENCE_LIMIT}, in the order given: a thread is unbounded, and a\n * list that grew with it would be a hundred rows of prose.\n */\nexport function filterReferences(items: readonly CaptionedItem[], query: string): CaptionedItem[] {\n const q = query.toLowerCase()\n const shown = q ? items.filter((item) => (item.search ?? item.snippet).toLowerCase().includes(q) || item.label.toLowerCase().includes(q)) : [...items]\n return shown.slice(-REFERENCE_LIMIT)\n}\n\nconst captionOf = (label: string, snippet: string) => (snippet ? `${label}: ${snippet}` : label)\n\nexport interface CaptionedReferenceOptions {\n /** Drawn before the caption: a 14px icon. */\n icon: ReactNode\n /**\n * The chip's 14px icon from what it points at \u2014 its `uri` \u2014 when chips name\n * several kinds of thing; `undefined` from it, or no `iconFor`, draws `icon`.\n * The same answer the list's row gave (Katerina, 7 October).\n */\n iconFor?: (uri: string | null) => ReactNode | undefined\n}\n\n/**\n * A reference mid-sentence, drawn as `label: snippet` and written as its `uri`\n * verbatim (SPEC \u00A713.1). The caption is the snapshot it was picked with \u2014 a\n * caption, never parsed. Inserted by {@link ReferenceTrigger}, a paste or a\n * draft; it opens no list of its own.\n */\nexport const CaptionedReference = Mention.extend<MentionOptions & CaptionedReferenceOptions>({\n name: 'captionedReference',\n group: 'inline',\n inline: true,\n atom: true,\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: {}, icon: null, iconFor: undefined, suggestion: { char: '\\0', items: () => [] } }\n },\n addAttributes() {\n return {\n id: { default: null, ...fromData('id') },\n label: { default: null, ...fromData('label') },\n snippet: { default: '', parseHTML: (element: HTMLElement) => element.getAttribute('data-snippet') ?? '', renderHTML: () => ({}) },\n uri: { default: null, ...fromData('uri', NOSTR_URI) },\n }\n },\n renderHTML({ node }) {\n return [\n 'span',\n { [`data-${this.name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`]: 'true', 'data-id': String(node.attrs.id ?? ''), 'data-label': String(node.attrs.label ?? ''), 'data-snippet': String(node.attrs.snippet ?? ''), ...(node.attrs.uri ? { 'data-uri': String(node.attrs.uri) } : {}), class: inlineChipClassName('neutral', 'cursor-default') },\n captionOf(node.attrs.label, node.attrs.snippet),\n ]\n },\n parseHTML() {\n return [{ tag: `span[data-${this.name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}]` }]\n },\n addNodeView() {\n const { icon, iconFor } = this.options\n return ReactNodeViewRenderer(\n ({ node }: ReactNodeViewProps) => (\n <NodeViewWrapper as=\"span\" className={inlineChipClassName('neutral', 'max-w-[24ch] cursor-default')}>\n <span className=\"flex items-center justify-center w-4 h-4 shrink-0 text-text-secondary\">{iconFor?.(node.attrs.uri ?? null) ?? icon}</span>\n <span className=\"truncate\">{captionOf(node.attrs.label, node.attrs.snippet)}</span>\n </NodeViewWrapper>\n ),\n { as: 'span' },\n )\n },\n})\n\nexport interface ReferenceMenuOptions {\n /** Names the list, e.g. what can be referenced. */\n ariaLabel: string\n /** The section's heading. */\n sectionLabel: string\n /** At each row's start: a 16px icon \u2014 unless the row brings its own (`CaptionedItem.icon`), or `iconFor` answers. */\n icon: ReactNode\n /**\n * A row's 16px icon from what it points at \u2014 its `uri` \u2014 when the list holds\n * several kinds of thing; `undefined` from it draws `icon`. Give the chip the\n * same answer at 14px (`CaptionedReference`'s `iconFor`), so a pick keeps it.\n */\n iconFor?: (uri: string) => ReactNode | undefined\n}\n\n/** A row as the list holds it: which section it is drawn under, when the list has several. */\ntype ListedItem = CaptionedItem & { section?: string }\n\n/**\n * The list `[` opens: `label: snippet` rows, the same box as the people list.\n * One section headed `sectionLabel`, or \u2014 when the rows say which section they\n * are in \u2014 a heading for each, in the order the rows arrive.\n */\nexport function referenceMenu({ ariaLabel, sectionLabel, icon, iconFor }: ReferenceMenuOptions): SuggestionPopupOptions<CaptionedItem> {\n return {\n ariaLabel,\n width: 'w-[658px]',\n maxHeight: 'max-h-[360px]',\n sections: (items: ListedItem[]) => {\n const groups: { label: string; items: CaptionedItem[]; className: string }[] = []\n for (const item of items) {\n const label = item.section ?? sectionLabel\n const last = groups[groups.length - 1]\n if (last?.label === label) last.items.push(item)\n else groups.push({ label, items: [item], className: 'px-2' })\n }\n return groups\n },\n itemKey: (item) => `reference-${item.id}`,\n row: (item) => ({ leading: item.icon ?? iconFor?.(item.uri) ?? icon, label: captionOf(item.label, item.snippet), description: item.description, hint: <EnterHint /> }),\n }\n}\n\nexport interface ReferenceTriggerOptions extends ReferenceMenuOptions {\n /** Everything `[` can offer, asked each time the query changes. Narrowed by {@link filterReferences}. */\n items: () => readonly CaptionedItem[]\n /**\n * In place of `items`: the sections to draw for what was typed, already\n * narrowed and ordered by the app. Empty sections are not drawn.\n */\n sections?: (query: string) => readonly ReferenceSection[]\n /**\n * Called with a listener while the list is open; the app calls it when what\n * `sections` would answer has changed \u2014 a search landing \u2014 and the open list\n * is drawn again for the same query. Returns the unsubscribe.\n */\n subscribe?: (listener: () => void) => () => void\n /** The node a pick inserts. Default `captionedReference`. */\n nodeName: string\n}\n\nconst listed = (options: ReferenceTriggerOptions, query: string): ListedItem[] =>\n options.sections\n ? options.sections(query).flatMap((section) => section.items.map((item) => ({ ...item, section: section.label })))\n : filterReferences(options.items(), query)\n\n/**\n * `[` \u2014 reference something, inserted as a {@link CaptionedReference} chip.\n *\n * Its own extension rather than the chip's suggestion, so the chip's text (its\n * `renderText`) does not change with the key that summons the list.\n */\nexport const ReferenceTrigger = Extension.create<ReferenceTriggerOptions>({\n name: 'referenceTrigger',\n addOptions() {\n return { items: () => [], nodeName: 'captionedReference', ariaLabel: 'References', sectionLabel: 'References', icon: null }\n },\n addProseMirrorPlugins() {\n const options = this.options\n return [\n Suggestion<CaptionedItem>({\n editor: this.editor,\n char: '[',\n pluginKey: new PluginKey('referenceTrigger'),\n ...listening(']'),\n // As a mention's: only where the chip may go.\n allow: ({ state, range }) => {\n const type = state.schema.nodes[options.nodeName]\n return !!type && !!state.doc.resolve(range.from).parent.type.contentMatch.matchType(type)\n },\n items: ({ query }) => listed(options, query),\n render: suggestionPopup({ ...referenceMenu(options), subscribe: options.subscribe, refresh: (query) => listed(options, query) }),\n command: ({ editor, range, props: item }) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n { type: options.nodeName, attrs: { id: item.id, label: item.label, snippet: item.snippet, uri: item.uri } },\n { type: 'text', text: ' ' },\n ])\n .run()\n },\n }),\n ]\n },\n})\n\n// \u2500\u2500\u2500 / \u2500\u2500\u2500\n\n/** A row of the `/` list, and what choosing it does. */\nexport interface SlashCommand {\n key: string\n row: SuggestionMenuRow\n /** Matched beside the row's label: an id, a trigger, a description. */\n keywords?: readonly string[]\n /** Runs once the `/query` that summoned the list has been removed. */\n run: (editor: Editor) => void\n /**\n * The extensions (by name) the row needs: an editor without one of them is\n * not offered the row, so no row makes something the editor cannot hold.\n */\n needs?: readonly string[]\n}\n\n/** Whether an editor has every extension a row needs. */\nconst offers = (editor: Editor, command: SlashCommand) =>\n !command.needs || command.needs.every((name) => editor.extensionManager.extensions.some((extension) => extension.name === name))\n\nexport interface SlashSection {\n label: string\n commands: readonly SlashCommand[]\n}\n\nexport type FormatId = 'paragraph' | 'heading' | 'subheading' | 'quote' | 'bulletList' | 'orderedList' | 'code' | 'checklist' | 'note' | 'warning'\n\nexport interface FormatDef {\n id: FormatId\n label: string\n /** What you type to get it \u2014 the input rule the extension registers. Blank when there is none. */\n trigger?: string\n /** The trigger when the format makes a block instead \u2014 Code's fence. Blank when it is the same. */\n blockTrigger?: string\n}\n\n/**\n * The formats `/` offers, in order. The hint is what you would type, not a key\n * chord. Bold, italic and underline are absent: they act on text already\n * written, so they are the selection toolbar's.\n */\nexport const FORMATS: readonly FormatDef[] = [\n { id: 'paragraph', label: 'Text' },\n { id: 'heading', label: 'Heading', trigger: '#' },\n { id: 'subheading', label: 'Subheading', trigger: '##' },\n { id: 'quote', label: 'Quote', trigger: '>' },\n { id: 'code', label: 'Code', trigger: '`', blockTrigger: '```' },\n { id: 'bulletList', label: 'Bulleted list', trigger: '-' },\n { id: 'orderedList', label: 'Numbered list', trigger: '1.' },\n]\n\n/**\n * What only a document holds \u2014 a checklist item and a callout (RIC-19) \u2014 so\n * only a block editor's `/` offers them (`formatSection('block')`): a\n * message's text has no spelling for either. They need the `Checklist` and\n * `Callout` extensions. Note and Warning are the two callouts offered (Miky,\n * 2026-10-13); the other tones are read and kept, not offered.\n */\nconst DOCUMENT_FORMATS: readonly FormatDef[] = [\n { id: 'checklist', label: 'Checklist', trigger: '[]' },\n { id: 'note', label: 'Note' },\n { id: 'warning', label: 'Warning' },\n]\n\n/** Code as a mark inside the line, or as a block of its own. */\nexport type CodeFormat = 'inline' | 'block'\n\n/**\n * Apply one format to the current block. `set*` wherever Tiptap has one:\n * choosing Heading should make a heading, not turn one off. `false` means\n * nothing changed, not that anything failed.\n */\nexport function applyFormat(editor: Editor, id: FormatId, code: CodeFormat = 'inline'): boolean {\n const chain = editor.chain().focus()\n /*\n The node's own command, called by name: StarterKit's extensions declare\n them, and this package builds without StarterKit. Its own command, rather\n than the core one it wraps, because a list's runs with the app's options\n (`keepMarks`, `itemTypeName`). An editor without that node changes nothing.\n */\n const run = (command: string, ...args: unknown[]): boolean => {\n const named = (chain as unknown as Record<string, ((...a: unknown[]) => { run: () => boolean }) | undefined>)[command]\n return named ? named(...args).run() : false\n }\n switch (id) {\n case 'paragraph':\n return run('setParagraph')\n case 'heading':\n return run('setHeading', { level: 1 })\n case 'subheading':\n return run('setHeading', { level: 2 })\n case 'quote':\n return run('toggleBlockquote')\n case 'bulletList':\n return run('toggleBulletList')\n case 'orderedList':\n return run('toggleOrderedList')\n case 'code':\n return run(code === 'block' ? 'toggleCodeBlock' : 'toggleCode')\n case 'checklist': {\n // In a list, that item becomes one; elsewhere, the line becomes a list of one.\n if (!editor.isActive('listItem') && !run('toggleBulletList')) return false\n // An item already a checklist item keeps its tick.\n if (typeof editor.getAttributes('listItem').checked === 'boolean') return true\n return editor.chain().updateAttributes('listItem', { checked: false }).run()\n }\n case 'note':\n case 'warning': {\n if (!editor.isActive('blockquote') && !run('toggleBlockquote')) return false\n return editor.chain().updateAttributes('blockquote', { tone: id }).run()\n }\n default:\n return false\n }\n}\n\n/** Words a row is also found by, beyond its id. */\nconst KEYWORDS: Partial<Record<FormatId, string[]>> = {\n checklist: ['todo', 'task'],\n note: ['callout'],\n warning: ['callout'],\n}\n\n/** The extension a row's block needs: without it the row would make a plain bullet or a plain quote (Katerina, review of #170). */\nconst NEEDS: Partial<Record<FormatId, string[]>> = {\n checklist: ['checklist'],\n note: ['callout'],\n warning: ['callout'],\n}\n\n/**\n * The Format section every app's `/` opens with. A block editor's also offers\n * a checklist and the callouts \u2014 only where the editor has the `Checklist` and\n * `Callout` extensions (`SlashCommand.needs`).\n */\nexport function formatSection(code: CodeFormat = 'inline'): SlashSection {\n return {\n label: 'Format',\n commands: (code === 'block' ? [...FORMATS, ...DOCUMENT_FORMATS] : FORMATS).map((format) => ({\n key: `format-${format.id}`,\n row: { label: format.label, shortcut: (code === 'block' && format.blockTrigger) || format.trigger },\n keywords: [format.id, ...(KEYWORDS[format.id] ?? [])],\n ...(NEEDS[format.id] ? { needs: NEEDS[format.id] } : {}),\n run: (editor) => {\n applyFormat(editor, format.id, code)\n },\n })),\n }\n}\n\n/** A row that types another trigger, so its own list opens \u2014 `@` from `/`. */\nexport function typeTrigger(input: string): SlashCommand['run'] {\n // Deferred a frame: the delete of the `/query` has to land first, or the\n // trigger's plugin sees the character arrive inside a range still being removed.\n return (editor) => {\n requestAnimationFrame(() => editor.commands.insertContent(input))\n }\n}\n\nfunction matches(command: SlashCommand, q: string): boolean {\n return [command.row.label, ...(command.keywords ?? [])].some((text) => text.toLowerCase().includes(q))\n}\n\n/** The sections narrowed to a query \u2014 the text typed after the `/`. Empty sections stay. */\nexport function filterSlashSections(sections: readonly SlashSection[], query: string): SlashSection[] {\n const q = query.toLowerCase()\n return sections.map((section) => ({ ...section, commands: q ? section.commands.filter((command) => matches(command, q)) : section.commands }))\n}\n\nexport interface SlashCommandsOptions {\n /** The sections, in order: {@link formatSection} first, then the app's own. */\n sections: readonly SlashSection[]\n}\n\ntype SlashRow = SlashCommand & { section: number }\n\n/**\n * `/` \u2014 format the line or insert something, at the caret, mid-sentence too.\n * A `/` inside a word (`and/or`, a path) opens nothing: the plugin wants a\n * space or the line's start before it.\n */\nexport const SlashCommands = Extension.create<SlashCommandsOptions>({\n name: 'slashCommands',\n addOptions() {\n return { sections: [formatSection()] }\n },\n addProseMirrorPlugins() {\n const sections = this.options.sections\n return [\n Suggestion<SlashRow>({\n editor: this.editor,\n char: '/',\n startOfLine: false,\n pluginKey: new PluginKey('slashCommands'),\n items: ({ query, editor }) =>\n filterSlashSections(sections, query).flatMap((section, at) =>\n section.commands.filter((command) => offers(editor, command)).map((command) => ({ ...command, section: at })),\n ),\n command: ({ editor, range, props }) => {\n editor.chain().focus().deleteRange(range).run()\n props.run(editor)\n },\n render: suggestionPopup<SlashRow>({\n ariaLabel: 'Commands',\n width: 'w-[300px]',\n maxHeight: 'max-h-[400px]',\n sections: (rows) => sections.map((section, at) => ({ label: section.label, items: rows.filter((row) => row.section === at) })),\n itemKey: (row) => row.key,\n row: (row) => row.row,\n }),\n }),\n ]\n },\n})\n", "import type { ComponentType, MouseEvent } from 'react'\nimport { Extension, InputRule, Node, type Editor } from '@tiptap/core'\nimport { DOMSerializer, type Node as ProseMirrorNode, type Slice } from '@tiptap/pm/model'\nimport { Plugin, PluginKey, type EditorState, type Transaction } from '@tiptap/pm/state'\nimport { Mapping, ReplaceAroundStep } from '@tiptap/pm/transform'\nimport type { EditorView, NodeView } from '@tiptap/pm/view'\nimport { NodeViewWrapper, ReactNodeViewRenderer, ReactRenderer, type ReactNodeViewProps } from '@tiptap/react'\nimport { Checkbox, type CheckboxProps } from './Checkbox'\nimport { calloutLook } from './RichText'\nimport { referenceMenuClick } from './ReferenceMenu'\n\n/*\n * The four nodes an editor of a block document needs that a stock schema does\n * not have (SPEC \u00A713.3) \u2014 moved here from Ship's `editorSchema.tsx` (RIC-14,\n * SHI-2) when Peek became the second editor of the same documents (MAN-9).\n *\n * They are one half of a pair. The other half is `@estiva-app/protocol`'s\n * `toEditorDocument` / `fromEditorDocument`, which turn a stored document into\n * the JSON these nodes read and back: the node names (`unknownBlock`,\n * `reference`, `attachment`) and the `blockId` attribute are that translation's,\n * and must not drift from it. This package does not import protocol, as\n * `RichText` does not: the translation hands over everything a view draws.\n *\n * Everything else a document can contain is already a ProseMirror node of the\n * same name \u2014 StarterKit's and TableKit's \u2014 which is why this file is short.\n */\n\n/**\n * The block types \u00A713.3 puts an id on, as the editor's node names \u2014\n * protocol's `EDITOR_BLOCK_TYPES`, less `unknownBlock`, which declares its own.\n */\nconst BLOCK_TYPES = [\n 'paragraph',\n 'heading',\n 'bulletList',\n 'orderedList',\n 'listItem',\n 'blockquote',\n 'codeBlock',\n 'horizontalRule',\n // A table and everything inside it (UIG-18): a row or a cell whose id was\n // re-minted on each save would detach any comment anchored to it.\n 'table',\n 'tableRow',\n 'tableHeader',\n 'tableCell',\n // Without this an attachment's id is re-minted on every save, and every\n // comment anchored to it stops resolving.\n 'attachment',\n]\nconst BLOCK_TYPE_SET = new Set(BLOCK_TYPES)\n\n/** protocol's `newBlockId`: six random bytes as hex. */\nfunction newBlockId(): string {\n return Array.from(crypto.getRandomValues(new Uint8Array(6)), (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** Whether anything typed or inserted is inside \u2014 protocol's `holdsText`. An empty paragraph is not; a lone line break is. */\nfunction holdsText(node: ProseMirrorNode): boolean {\n let found = false\n node.descendants((child) => {\n if (found) return false\n if ((child.isText && child.text !== '') || child.type.name === 'hardBreak' || child.type.name === 'reference') found = true\n return !found\n })\n return found\n}\n\ninterface Claim {\n pos: number\n node: ProseMirrorNode\n /** An unknown block: the translation hands its source back untouched, so re-minting it here would be undone on save. */\n fixed: boolean\n}\n\n/** Every block that claims an id, by id, in document order. */\nfunction claimsIn(doc: ProseMirrorNode): Map<string, Claim[]> {\n const claims = new Map<string, Claim[]>()\n doc.descendants((node, pos, parent) => {\n const fixed = node.type.name === 'unknownBlock'\n if (!fixed && !BLOCK_TYPE_SET.has(node.type.name)) return\n // A list inside a list item is no block (MAN-13): an id it arrived with is\n // cleared, never a claim that could take a real block's.\n if (parent?.type.name === 'listItem' && LISTS.has(node.type.name)) return\n const id = node.attrs.blockId\n if (typeof id !== 'string' || id === '') return\n const copies = claims.get(id)\n if (copies) copies.push({ pos, node, fixed })\n else claims.set(id, [{ pos, node, fixed }])\n })\n return claims\n}\n\n/**\n * The copy the block that was there before carried on into: where the start\n * of its content landed. Enter at the very start pushes that content into the\n * lower half; a split anywhere else leaves it in the upper; a pasted copy,\n * above or below, never moves it out of the original.\n */\nfunction carriedOn(copies: Claim[], before: Claim, mapping: Mapping): Claim | undefined {\n if (before.node.isLeaf) {\n const at = mapping.map(before.pos, 1)\n return copies.find((c) => c.pos === at)\n }\n const at = mapping.map(textStart(before), 1)\n return copies.find((c) => c.pos < at && at < c.pos + c.node.nodeSize)\n}\n\n/** The start of a block's text, inside the paragraph a list item or quote wraps it in. */\nfunction textStart({ pos, node }: { pos: number; node: ProseMirrorNode }): number {\n let start = pos + 1\n if (!node.isTextblock) {\n let found = false\n node.descendants((child, at) => {\n if (found) return false\n if (child.isTextblock) {\n start = pos + 1 + at + 1\n found = true\n }\n return !found\n })\n }\n return start\n}\n\n/**\n * Where a block claims an id another block keeps \u2014 see {@link BlockId}. An\n * unknown block always keeps its id; otherwise the copy the original carried\n * on into, and failing that protocol's rule: the first that holds text, or\n * the first.\n */\nfunction repeatedBlockIds(doc: ProseMirrorNode, before: ProseMirrorNode, mapping: Mapping): number[] {\n const claims = claimsIn(doc)\n let previous: Map<string, Claim[]> | undefined\n const losers: number[] = []\n for (const [id, copies] of claims) {\n if (copies.length < 2) continue\n previous ??= claimsIn(before)\n const original = previous.get(id)?.[0]\n const owner =\n copies.find((c) => c.fixed) ??\n (original && carriedOn(copies, original, mapping)) ??\n copies.find((c) => holdsText(c.node)) ??\n copies[0]\n for (const copy of copies) if (copy !== owner && !copy.fixed) losers.push(copy.pos)\n }\n return losers\n}\n\n/**\n * The blocks a save turns into blocks \u2014 exactly what protocol's\n * `blockFromEditor` walks \u2014 so a list, a table and its rows and cells are\n * walked into, and a list item or a quote is not: the save flattens their\n * paragraph wrappers into inline runs, so an id there would be dropped.\n */\nconst BLOCK_CONTAINERS = new Set(['bulletList', 'orderedList', 'table', 'tableRow', 'tableHeader', 'tableCell'])\n\n/** The wrappers the save flattens: their paragraphs become inline runs of theirs. */\nconst FLATTENED = new Set(['listItem', 'blockquote'])\n\n/**\n * The lists Tab puts inside a list item (MAN-13). \u00A713.3 keeps a list flat and\n * marks an item's depth, so the save walks into such a list for its items and\n * writes no block for the list itself: its items get ids, it gets none.\n */\nconst LISTS = new Set(['bulletList', 'orderedList'])\n\n/** The lists inside a list item, with where each starts. */\nfunction innerLists(item: ProseMirrorNode, pos: number): { pos: number; node: ProseMirrorNode }[] {\n const out: { pos: number; node: ProseMirrorNode }[] = []\n item.forEach((child, offset) => {\n if (LISTS.has(child.type.name)) out.push({ pos: pos + 1 + offset, node: child })\n })\n return out\n}\n\nconst idOf = (node: ProseMirrorNode): string | undefined => {\n const id = node.attrs.blockId\n return typeof id === 'string' && id !== '' ? id : undefined\n}\n\n/** Every block the save turns into a block, in document order. */\nfunction addressedBlocks(doc: ProseMirrorNode): { pos: number; node: ProseMirrorNode }[] {\n const out: { pos: number; node: ProseMirrorNode }[] = []\n const visit = (node: ProseMirrorNode, pos: number) => {\n if (!BLOCK_TYPE_SET.has(node.type.name)) return\n out.push({ pos, node })\n if (BLOCK_CONTAINERS.has(node.type.name)) node.forEach((child, offset) => visit(child, pos + 1 + offset))\n if (node.type.name === 'listItem') {\n for (const list of innerLists(node, pos)) list.node.forEach((child, offset) => visit(child, list.pos + 1 + offset))\n }\n }\n doc.forEach((node, offset) => visit(node, offset))\n return out\n}\n\n/** Where a block the save would give an id has none, so it would get a new one on every save. */\nfunction unnamedBlocks(doc: ProseMirrorNode): number[] {\n return addressedBlocks(doc)\n .filter((b) => idOf(b.node) === undefined)\n .map((b) => b.pos)\n}\n\n/**\n * Where a paragraph inside a list item or a quote claims an id, or a list\n * inside a list item does \u2014 both of which the save drops.\n */\nfunction hiddenIds(doc: ProseMirrorNode): number[] {\n const out: number[] = []\n doc.descendants((node, pos) => {\n if (!FLATTENED.has(node.type.name)) return\n node.forEach((child, offset) => {\n const inner = child.isTextblock || (node.type.name === 'listItem' && LISTS.has(child.type.name))\n if (inner && idOf(child) !== undefined) out.push(pos + 1 + offset)\n })\n })\n return out\n}\n\n/**\n * The id each block a wrap or a lift brought in takes over \u2014 see\n * {@link BlockId}. An id the save addressed before the change and does not\n * now goes to the innermost new block its text was carried into: the quote or\n * the list item around a paragraph, or the paragraph lifted out of one.\n * A block the change deleted hands nothing on. What it asks is whether the\n * block's own opening survived, the token left of its text, not its first\n * character: the `/` menu and a typed `> ` or `- ` delete what was typed at\n * the very start of the text in the same change as the wrap.\n */\nfunction handedOver(doc: ProseMirrorNode, before: ProseMirrorNode, mapping: Mapping, unnamed: number[]): Map<number, string> {\n const out = new Map<number, string>()\n if (unnamed.length === 0) return out\n const now = new Set(addressedBlocks(doc).map((b) => idOf(b.node)))\n // By where their text starts, and of a list and its item around the same text, the item first.\n const gone = addressedBlocks(before)\n .filter((b) => !b.node.isLeaf && idOf(b.node) !== undefined && !now.has(idOf(b.node)))\n .map((b) => ({ ...b, start: textStart(b) }))\n .sort((a, b) => a.start - b.start || b.pos - a.pos)\n for (const old of gone) {\n const id = idOf(old.node)!\n const { pos: at, deleted } = mapping.mapResult(old.start, -1)\n if (deleted) continue\n let inner: number | undefined\n for (const pos of unnamed) {\n if (out.has(pos)) continue\n if (pos < at && at < pos + doc.nodeAt(pos)!.nodeSize && (inner === undefined || pos > inner)) inner = pos\n }\n if (inner !== undefined) {\n out.set(inner, id)\n now.add(id)\n }\n }\n return out\n}\n\n/**\n * Whether a change could have made a repeat or a block without an id: only a\n * step that brings in blocks can, or one that wraps or lifts them \u2014 lifting\n * the paragraph out of a quote or a one-item list brings in nothing, yet\n * leaves a block the save addresses with the wrapper's missing id. Typing,\n * deleting and marks do neither, so most keystrokes skip the walk.\n */\nfunction bringsInBlocks(transactions: readonly Transaction[]): boolean {\n return transactions.some((tr) =>\n tr.steps.some((step) => {\n if (step instanceof ReplaceAroundStep) return true\n const slice = (step as { slice?: Slice }).slice\n let blocks = false\n slice?.content.forEach((node) => {\n if (node.isBlock) blocks = true\n })\n return blocks\n }),\n )\n}\n\n/**\n * `id` on every block, carried as the `blockId` attribute.\n *\n * A global attribute rather than one per node type, because \u00A713.3 puts an id\n * on **every** block and a type that quietly lacked one would mint a fresh id\n * on every save \u2014 detaching each anchored comment on it (RFC 0.4 \u00A76) with\n * nothing on screen to say so.\n *\n * Rendered as `data-block-id`, which is what `RichText` already emits, so the\n * reading and the editing surface address a block the same way.\n *\n * **A split gives the new half its own id, the moment it happens** (MAN-10).\n * ProseMirror copies a split node's attrs to both halves, so Enter leaves two\n * blocks claiming one id. Enter at the very start of a paragraph puts the\n * copy *above* it \u2014 and once something is typed there, nothing at save time\n * can tell which half was the original, so protocol's `fromEditorDocument`\n * gave the id, and every comment anchored to it, to the new line. Settled\n * here instead, in the same transaction, where the change itself says which\n * copy the original carried on into \u2014 so a copy pasted above a block does\n * not take its id either. The others get fresh ids. `fromEditorDocument`\n * keeps its own rule (the copy that holds text) as the backstop for an\n * editor without this extension; where the change says nothing, this falls\n * back to that rule too.\n *\n * **A block that arrives without an id gets one, the moment it arrives**\n * (MAN-11): a paste, a `/` menu insert, the empty paragraph a new editor\n * starts with and the one StarterKit keeps at the end. `fromEditorDocument`\n * mints an id for such a block on every save and the editor never sees it, so\n * each save gave it a different one \u2014 a blur with no edit published again, and\n * a comment anchored to it in between was detached. The editor's first\n * document is filled in when it is created, outside the undo history and\n * without an update, so opening a description is not an edit.\n *\n * **A wrap or a lift hands the id on** (MAN-12). Turning a paragraph into a\n * quote or a list wraps it, and ProseMirror leaves its attrs on the paragraph\n * inside \u2014 where the save flattens it and drops the id, so the quote or list\n * item was published under a new one and every comment anchored to the\n * paragraph was detached. The quote or the list item takes the paragraph's id\n * instead, and the list around it a fresh one; lifting the text back out hands\n * the id to the paragraph again. The paragraph inside keeps no id, as one\n * loaded from a stored document has none.\n */\nexport const BlockId = Extension.create({\n name: 'blockId',\n onCreate() {\n const { state, view } = this.editor\n const unnamed = unnamedBlocks(state.doc)\n if (unnamed.length === 0) return\n const tr = state.tr\n for (const pos of unnamed) tr.setNodeAttribute(pos, 'blockId', newBlockId())\n view.dispatch(tr.setMeta('addToHistory', false).setMeta('preventUpdate', true))\n },\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('blockIdSplit'),\n appendTransaction: (transactions, old, state) => {\n if (!bringsInBlocks(transactions)) return null\n const mapping = new Mapping()\n for (const tr of transactions) mapping.appendMapping(tr.mapping)\n const unnamed = unnamedBlocks(state.doc)\n const handed = handedOver(state.doc, old.doc, mapping, unnamed)\n const ids = new Map<number, string | null>()\n for (const pos of repeatedBlockIds(state.doc, old.doc, mapping)) ids.set(pos, newBlockId())\n for (const pos of unnamed) ids.set(pos, handed.get(pos) ?? newBlockId())\n for (const pos of hiddenIds(state.doc)) ids.set(pos, null)\n if (ids.size === 0) return null\n const tr = state.tr\n for (const [pos, id] of ids) tr.setNodeAttribute(pos, 'blockId', id)\n return tr\n },\n }),\n ]\n },\n addGlobalAttributes() {\n return [\n {\n types: BLOCK_TYPES,\n attributes: {\n blockId: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute('data-block-id'),\n renderHTML: (attributes: Record<string, unknown>) =>\n attributes.blockId ? { 'data-block-id': String(attributes.blockId) } : {},\n },\n },\n },\n ]\n },\n})\n\n/** An http(s) URL, or null. */\nconst webUrl = (raw: string | null): string | null => (raw && /^https?:\\/\\/\\S+$/i.test(raw) ? raw : null)\n\n/** What a reference's view is handed: the `nostr:` URI it stands for, and the URL a URL chip was written as. */\nexport interface ReferenceViewProps {\n uri: string\n href?: string\n}\n\nexport interface ReferenceNodeOptions {\n /**\n * Draws the reference as the thing it points at \u2014 the app's own chip, which\n * knows how to resolve it. Without one, the URI is drawn as text.\n */\n view: ComponentType<ReferenceViewProps> | null\n}\n\n/**\n * A `nostr:` reference, drawn as the thing it points at \u2014 SPEC \u00A713.1.\n *\n * An **atom**: it has no editable interior, because its text is the URI and\n * nobody edits half a `naddr`. Selecting it selects the whole reference, which\n * is also how deleting one works. `marks` carries the run's other marks, so a\n * bold reference comes back bold.\n *\n * `embed` is the card its writer chose to draw after its block \u2014 `compact`,\n * `detailed`, or null for none (SPEC \u00A713.3, \"A reference's card\") \u2014 and `extra`\n * every key of the mark this version does not know. Both are declared so that\n * ProseMirror keeps them: an undeclared attribute is dropped when the node is\n * made, and the card with it. `ReferenceCards` draws the cards.\n *\n * `href` makes it a **URL chip**: an app URL that reads as the chip of what it\n * names, and is that URL on the wire (\u00A713.3). `uri` is what it names, filled in\n * by `LinkChips` once the app has resolved it; until then it draws as the URL.\n */\nexport const ReferenceNode = Node.create<ReferenceNodeOptions>({\n name: 'reference',\n group: 'inline',\n inline: true,\n atom: true,\n selectable: true,\n draggable: false,\n addOptions() {\n return { view: null }\n },\n addAttributes() {\n return {\n // A pasted URL chip keeps only its URL: what it names is the editor's to resolve (`LinkChips`), never\n // the clipboard's, which could show one object while saving another URL (security review).\n uri: { default: '', parseHTML: (element: HTMLElement) => (webUrl(element.getAttribute('data-href')) ? '' : (element.getAttribute('data-reference') ?? '')) },\n marks: { default: [] as string[] },\n embed: { default: null as string | null, parseHTML: (element: HTMLElement) => element.getAttribute('data-embed') },\n extra: { default: null as Record<string, unknown> | null, parseHTML: () => null },\n // An http(s) URL only: `LinkChips` makes no other, and a pasted `javascript:` must not reach a saved link.\n href: { default: null as string | null, parseHTML: (element: HTMLElement) => webUrl(element.getAttribute('data-href')) },\n }\n },\n parseHTML() {\n return [{ tag: 'span[data-reference]' }]\n },\n renderHTML({ node }) {\n // A copy to the clipboard as HTML, and the whole drawing when no view is given.\n // The card goes along, so a paste keeps it; a URL chip copies as its URL.\n const embed = typeof node.attrs.embed === 'string' ? { 'data-embed': node.attrs.embed } : {}\n const href = typeof node.attrs.href === 'string' && node.attrs.href ? { 'data-href': node.attrs.href } : {}\n return ['span', { 'data-reference': String(node.attrs.uri), ...embed, ...href }, String(node.attrs.href || node.attrs.uri)]\n },\n renderText({ node }) {\n return String(node.attrs.href || node.attrs.uri)\n },\n addNodeView() {\n const View = this.options.view\n if (!View) return null\n return ReactNodeViewRenderer(({ node, editor, getPos }: ReactNodeViewProps) => {\n const uri = String(node.attrs.uri ?? '')\n const href = typeof node.attrs.href === 'string' && node.attrs.href ? node.attrs.href : undefined\n return (\n <NodeViewWrapper\n as=\"span\"\n className=\"inline\"\n // While editing, a click opens the chip's menu (`ReferenceMenu`) rather than following the chip.\n onClickCapture={(event: MouseEvent) => {\n const pos = getPos()\n if (typeof pos !== 'number' || !referenceMenuClick(editor, pos)) return\n event.preventDefault()\n event.stopPropagation()\n }}\n >\n {/* A URL chip not resolved yet reads as the URL it was written as. */}\n {uri ? <View uri={uri} {...(href ? { href } : {})} /> : <span>{href}</span>}\n </NodeViewWrapper>\n )\n })\n },\n})\n\n/**\n * A block this editor has no design for, kept whole.\n *\n * \u00A713.3: a reader MUST render an unknown block's inline text and MUST NOT drop\n * it silently. **An editor is the dangerous case** \u2014 ProseMirror discards a\n * node its schema does not know, and the save after that would delete somebody\n * else's block with no error anywhere on the way. So the block's own JSON\n * rides in `source`, and protocol's `fromEditorDocument` hands it back\n * untouched; `text` is its inline text, worked out by `toEditorDocument`.\n *\n * Not editable, deliberately: an affordance that lets a person change\n * something they cannot see is worse than one that says the content is there\n * and belongs to another surface.\n */\nexport const UnknownBlock = Node.create({\n name: 'unknownBlock',\n group: 'block',\n atom: true,\n selectable: true,\n addAttributes() {\n return {\n blockId: { default: null },\n source: { default: null },\n text: { default: '' },\n }\n },\n parseHTML() {\n return [{ tag: 'div[data-unknown-block]' }]\n },\n renderHTML({ node }) {\n return ['div', { 'data-unknown-block': String(node.attrs.blockId ?? '') }, String(node.attrs.text ?? '')]\n },\n addNodeView() {\n return ReactNodeViewRenderer(UnknownBlockView)\n },\n})\n\nfunction UnknownBlockView({ node }: ReactNodeViewProps) {\n const source = node.attrs.source as { id?: string; type?: string } | null\n const text = String(node.attrs.text ?? '')\n return (\n <NodeViewWrapper\n data-block-id={source?.id}\n data-block-type={source?.type}\n className=\"rounded-md border border-border-default bg-bg-inset px-2 py-1 text-text-secondary\"\n >\n {text || `A ${source?.type ?? 'block'} this app cannot edit yet.`}\n </NodeViewWrapper>\n )\n}\n\n/** What an attachment's view is handed: the block's `imeta` fields. */\nexport interface AttachmentViewProps {\n attrs: Record<string, unknown>\n}\n\nexport interface AttachmentNodeOptions {\n /** Draws the file \u2014 the app's own attachment card. Without one, its name is drawn as text. */\n view: ComponentType<AttachmentViewProps> | null\n}\n\n/**\n * A file placed in a document \u2014 SPEC \u00A713.3's `attachment` block (SHI-2).\n *\n * An atom: the file is the block, there is nothing inside it to edit, and a\n * caret that could sit \"inside\" an image is a way to produce documents no\n * reader has a rule for. Selectable and deletable, so removing one is Backspace\n * like anything else.\n *\n * Its attributes are the `imeta` fields exactly, because that is what \u00A713.3\n * specifies. Each is declared, and that is not boilerplate: ProseMirror **drops\n * an attribute its schema does not name**, so an undeclared `thumb` would\n * survive the editor and vanish on save \u2014 the silent deletion `UnknownBlock`\n * exists to prevent.\n */\nexport const AttachmentNode = Node.create<AttachmentNodeOptions>({\n name: 'attachment',\n group: 'block',\n atom: true,\n selectable: true,\n draggable: false,\n addOptions() {\n return { view: null }\n },\n addAttributes() {\n return {\n url: { default: '' },\n m: { default: '' },\n x: { default: '' },\n size: { default: 0 },\n dim: { default: null },\n thumb: { default: null },\n alt: { default: null },\n filename: { default: null },\n }\n },\n parseHTML() {\n return [{ tag: 'div[data-attachment-block]' }]\n },\n renderHTML({ node }) {\n // A copy to the clipboard, and the whole drawing when no view is given.\n return ['div', { 'data-attachment-block': String(node.attrs.x ?? '') }, String(node.attrs.filename ?? 'attachment')]\n },\n addNodeView() {\n const View = this.options.view\n if (!View) return null\n return ReactNodeViewRenderer(({ node }: ReactNodeViewProps) => (\n <NodeViewWrapper className=\"my-2\">\n <View attrs={node.attrs} />\n </NodeViewWrapper>\n ))\n },\n})\n\nconst TICK = 'checklistTick'\n\n/**\n * Whether a change is a click on a checklist item's box. The click does not\n * focus the editor, so no blur follows it: an editor that saves on blur saves\n * a tick straight away instead (RIC-19, Miky 2026-10-13: one click ticks).\n */\nexport function isChecklistTick(transaction: Transaction): boolean {\n return transaction.getMeta(TICK) === true\n}\n\n/** A list or quote command by name: StarterKit's declare them, and this package builds without StarterKit. */\ntype Chain = ReturnType<Editor['chain']>\nfunction named(chain: Chain, command: string, ...args: unknown[]): Chain {\n const own = (chain as unknown as Record<string, ((...a: unknown[]) => Chain) | undefined>)[command]\n return own ? own(...args) : chain\n}\n\n/** The list item the caret's paragraph is the first line of, if it is. */\nfunction itemAtCaret(state: EditorState): { node: ProseMirrorNode; pos: number } | null {\n const { $from } = state.selection\n if ($from.depth < 2) return null\n const item = $from.node(-1)\n if (item.type.name !== 'listItem' || $from.index(-1) !== 0) return null\n return { node: item, pos: $from.before(-1) }\n}\n\nconst sameAttrs = (a: ProseMirrorNode, b: ProseMirrorNode) =>\n Object.keys({ ...a.attrs, ...b.attrs }).every((key) => a.attrs[key] === b.attrs[key])\n\n/**\n * A list item, drawn with a box before it when it is a checklist item. The box\n * is the package's `Checkbox`, as `RichText` draws it, and the item's own\n * text and inner lists follow it in a `div`. Any change to the item's\n * attributes draws it again, which is what a tick is.\n *\n * The box is named by the item's words, and follows them as they are typed.\n * Whether it ticks follows the editor's `editable`, read again on every\n * update \u2014 `setEditable` sends one \u2014 not only when the item was drawn\n * (Katerina, review of #170). Not editable, it is a picture, and the box around\n * it says done or not to a screen reader, as `RichText`'s does.\n */\nfunction listItemView(editor: Editor, node: ProseMirrorNode, view: EditorView, getPos: () => number | undefined): NodeView {\n const toDOM = node.type.spec.toDOM\n const { dom, contentDOM } = toDOM ? DOMSerializer.renderSpec(document, toDOM(node)) : { dom: document.createElement('li'), contentDOM: undefined }\n const checked = node.attrs.checked\n if (typeof checked !== 'boolean') return { dom, contentDOM: contentDOM ?? dom, update: (next) => next.type === node.type && sameAttrs(next, node) }\n\n const li = dom as HTMLElement\n const box = document.createElement('span')\n box.contentEditable = 'false'\n box.setAttribute('data-checklist-box', '')\n const body = document.createElement('div')\n li.replaceChildren(box, body)\n const tick = (next: boolean) => {\n const pos = getPos()\n if (pos === undefined || !view.editable) return\n view.dispatch(view.state.tr.setNodeAttribute(pos, 'checked', next).setMeta(TICK, true))\n }\n let words = node.firstChild?.textContent.trim() || 'Item'\n const props = (): CheckboxProps => ({ checked, onChange: view.editable ? tick : undefined, 'aria-label': words })\n const renderer = new ReactRenderer<unknown, CheckboxProps>(Checkbox, { editor, props: props(), as: 'span' })\n box.appendChild(renderer.element)\n // What the box last drew: an update that changes neither leaves it alone, so typing does not draw every box again.\n let drawn = ''\n const sync = () => {\n const now = `${view.editable}:${words}`\n if (now === drawn) return\n drawn = now\n renderer.updateProps(props())\n for (const [name, value] of Object.entries({ role: 'checkbox', 'aria-checked': String(checked), 'aria-readonly': 'true', 'aria-label': words })) {\n if (view.editable) box.removeAttribute(name)\n else box.setAttribute(name, value)\n }\n }\n sync()\n editor.on('update', sync)\n return {\n dom: li,\n contentDOM: body,\n update: (next) => {\n if (next.type !== node.type || !sameAttrs(next, node)) return false\n words = next.firstChild?.textContent.trim() || 'Item'\n sync()\n return true\n },\n stopEvent: (event) => box.contains(event.target as globalThis.Node),\n ignoreMutation: (mutation) => box.contains(mutation.target),\n destroy: () => {\n editor.off('update', sync)\n renderer.destroy()\n },\n }\n}\n\n/**\n * A checklist item \u2014 SPEC \u00A713.3's `checked` on a list item (RIC-19): `true`\n * ticked, `false` not, absent a plain item. Add it beside `BlockId`.\n *\n * - **The box ticks with one click**, where the description is read, and the\n * change says so (`isChecklistTick`), so the app saves it at once.\n * - **`[ ] ` or `[] ` at the start of a line** makes it, `[x] ` ticked; in a\n * list it turns that item into one.\n * - **Enter** starts the next item unticked: a new item never takes the tick\n * of the one it was split from. At the start of an item, the text moves down\n * with its tick and the empty item above is unticked. On an empty item Enter\n * leaves the list, as in any list.\n * - **Backspace** at the start of a checklist item takes its box away first;\n * the next one joins it to the item above, as in any list.\n *\n * An editor without it drops `checked` on save and keeps the item and its text.\n */\nexport const Checklist = Extension.create({\n name: 'checklist',\n // Before the list item's own Enter and Backspace.\n priority: 101,\n addGlobalAttributes() {\n return [\n {\n types: ['listItem'],\n attributes: {\n checked: {\n default: null,\n keepOnSplit: false,\n parseHTML: (element: HTMLElement) => {\n const value = element.getAttribute('data-checked')\n return value === 'true' ? true : value === 'false' ? false : null\n },\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.checked === 'boolean' ? { 'data-checked': String(attributes.checked) } : {},\n },\n },\n },\n ]\n },\n addInputRules() {\n return [\n new InputRule({\n find: /^\\[([ xX]?)\\]\\s$/,\n handler: ({ state, range, match, chain }) => {\n const checked = match[1].toLowerCase() === 'x'\n const inList = itemAtCaret(state) !== null\n // Without a bullet list to make, the typed text stays as it was typed.\n if (!inList && !(chain() as unknown as Record<string, unknown>).toggleBulletList) return null\n const deleted = chain().deleteRange(range)\n const listed = inList ? deleted : named(deleted, 'toggleBulletList')\n listed.updateAttributes('listItem', { checked }).run()\n },\n }),\n ]\n },\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n const { state } = this.editor\n const item = itemAtCaret(state)\n const { $from, empty } = state.selection\n if (!item || typeof item.node.attrs.checked !== 'boolean') return false\n // Over a selection: the selection goes and the next item is unticked, as for a caret in the text.\n if (!empty) return this.editor.chain().deleteSelection().splitListItem('listItem', { checked: false }).run()\n // An empty item: the list item's own Enter, which leaves the list.\n if ($from.parent.content.size === 0) return false\n if ($from.parentOffset > 0) return this.editor.commands.splitListItem('listItem', { checked: false })\n return this.editor\n .chain()\n .splitListItem('listItem', { checked: item.node.attrs.checked })\n .command(({ tr }) => {\n tr.setNodeAttribute(tr.mapping.map(item.pos), 'checked', false)\n return true\n })\n .run()\n },\n Backspace: () => {\n const { state } = this.editor\n const item = itemAtCaret(state)\n const { $from, empty } = state.selection\n if (!item || !empty || $from.parentOffset > 0 || typeof item.node.attrs.checked !== 'boolean') return false\n this.editor.view.dispatch(state.tr.setNodeAttribute(item.pos, 'checked', null))\n return true\n },\n }\n },\n addProseMirrorPlugins() {\n const editor = this.editor\n return [\n new Plugin({\n key: new PluginKey('checklist'),\n props: { nodeViews: { listItem: (node, view, getPos) => listItemView(editor, node, view, getPos) } },\n }),\n ]\n },\n})\n\n/**\n * A callout \u2014 SPEC \u00A713.3's `tone` on a quote (RIC-19): a quote drawn to stand\n * out, in one of GitHub's alert kinds (`note`, `tip`, `important`, `warning`,\n * `caution`). Its look is `RichText`'s, by `data-tone`; a tone it does not\n * know looks like `note`, and the editor keeps it as written. Add it beside\n * `BlockId`.\n */\nexport const Callout = Extension.create({\n name: 'callout',\n addGlobalAttributes() {\n return [\n {\n types: ['blockquote'],\n attributes: {\n tone: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute('data-tone'),\n // As `RichText` draws it: the tone as written, and the look it is drawn in.\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.tone === 'string' ? { 'data-tone': attributes.tone, 'data-callout': calloutLook(attributes.tone) } : {},\n },\n },\n },\n ]\n },\n})\n", "/**\n * What a chip in a document offers its writer \u2014 Open, and its card: none,\n * compact or detailed (SPEC \u00A713.3, \"A reference's card\").\n *\n * **Where it opens** (Miky, 14 Oct): a click on a chip while editing opens the\n * menu instead of following the chip, and a chip just put in \u2014 pasted, picked\n * with `[`, or a pasted URL becoming its chip (`LinkChips`) \u2014 opens it by\n * itself, so the card can be chosen at once. That menu takes no focus: the\n * person is still writing, and typing on closes it with no card. Enter on a\n * selected chip opens it from the keyboard.\n *\n * **Keys while it is open**, handed over by the editor as the `/` menu's are:\n * \u2193 and \u2191 move through the rows (nothing is highlighted until they do, so\n * Enter after a paste still starts a new line), Enter chooses the highlighted\n * row, Escape closes it. Any edit or caret move closes it.\n *\n * Open does what a click on the chip does when reading: the app's own chip\n * gets the click, so it opens where the app opens it. A chip with nothing to\n * click opens its URL in a new tab.\n *\n * Two parts: the extension `ReferenceMenu` holds which chip the menu is open\n * on, and `ReferenceMenuView` draws it beside `EditorContent`. Pair them with\n * `ReferenceNode` and `ReferenceCards`.\n */\nimport { useCallback } from 'react'\nimport { Extension, type Editor } from '@tiptap/core'\nimport { useEditorState } from '@tiptap/react'\nimport { NodeSelection, Plugin, PluginKey, TextSelection, type EditorState, type Transaction } from '@tiptap/pm/state'\nimport { ReplaceStep } from '@tiptap/pm/transform'\nimport type { EditorView } from '@tiptap/pm/view'\nimport { IconCheck } from '@tabler/icons-react'\nimport { CHIPS_MADE } from './LinkChips'\nimport { MenuItem, MenuSection, MenuSeparator } from './Menu'\nimport { Popover } from './Popover'\nimport { setReferenceCardAt, type ReferenceCardLayout } from './ReferenceCards'\n\ninterface MenuState {\n /** The chip's position. */\n pos: number\n /** The highlighted row of `rowsOf`, or -1 for none. */\n active: number\n}\n\nconst key = new PluginKey<MenuState | null>('referenceMenu')\n\nconst LAYOUTS: { layout: ReferenceCardLayout; label: string }[] = [\n { layout: 'none', label: 'None' },\n { layout: 'compact', label: 'Compact' },\n { layout: 'detailed', label: 'Detailed' },\n]\n\n/** A row: Open, or a card. */\ntype Row = 'open' | ReferenceCardLayout\n\n/** The chip's own control to click, or null. */\nfunction chipTarget(view: EditorView, pos: number): HTMLElement | null {\n const dom = view.nodeDOM(pos)\n return dom instanceof HTMLElement ? dom.querySelector<HTMLElement>('a[href], button') : null\n}\n\n/** An http(s) URL chip's URL, or ''. */\nfunction webHref(view: EditorView, pos: number): string {\n const href = view.state.doc.nodeAt(pos)?.attrs.href\n return typeof href === 'string' && /^https?:\\/\\//i.test(href) ? href : ''\n}\n\n/**\n * The menu's rows: Open when there is something to open \u2014 the chip's own\n * control, or its URL \u2014 and the cards when the chip names something.\n */\nfunction rowsOf(view: EditorView, pos: number): Row[] {\n const open: Row[] = chipTarget(view, pos) || webHref(view, pos) ? ['open'] : []\n return view.state.doc.nodeAt(pos)?.attrs.uri ? [...open, ...LAYOUTS.map((card) => card.layout)] : open\n}\n\n/** A chip node, or null. */\nfunction chipAt(state: EditorState, pos: number) {\n const node = state.doc.nodeAt(pos)\n return node && node.type.name === 'reference' ? node : null\n}\n\n/**\n * The chip this transaction just put right before the caret \u2014 a paste, a `[`\n * pick (which adds a space after it), or a URL becoming its chip \u2014 or null.\n * An undo, and a document loaded over the editor, put nothing in.\n */\nfunction justPlaced(tr: Transaction, state: EditorState): number | null {\n if (!tr.docChanged || tr.getMeta('history$') || tr.getMeta('preventUpdate')) return null\n // A whole document set over the editor \u2014 someone else's edit loaded \u2014 is not something the person put in.\n const whole = tr.steps.some((step) => step instanceof ReplaceStep && step.from === 0 && step.to === tr.before.content.size)\n if (whole && tr.getMeta('uiEvent') !== 'paste') return null\n // A URL becoming its chip opens the menu only when it was pasted, with the caret still right after it.\n const made = tr.getMeta(CHIPS_MADE) as { pasted: boolean } | undefined\n if (made && !made.pasted) return null\n const { selection } = state\n if (!selection.empty) return null\n let at = selection.from\n let before = selection.$from.nodeBefore\n if (!made && before?.isText && before.text === ' ') {\n at -= 1\n before = state.doc.resolve(at).nodeBefore\n }\n if (!before || before.type.name !== 'reference' || !before.attrs.uri) return null\n const pos = at - before.nodeSize\n const old = tr.mapping.invert().map(pos)\n const was = tr.before.nodeAt(old)\n // The same chip as before the step \u2014 a key pressed after it, a card set on it \u2014 is not new. One put in just before\n // an identical chip pushes that chip along, so it no longer maps onto `pos` (code review).\n if (was && was.type.name === 'reference' && was.attrs.uri === before.attrs.uri && tr.mapping.map(old, 1) === pos) return null\n return pos\n}\n\nfunction dispatchMenu(view: EditorView, next: MenuState | null): void {\n view.dispatch(view.state.tr.setMeta(key, next))\n}\n\n// True while Open hands the click to the chip, so the chip's own click is not taken for a click that opens the menu.\nlet passing = false\n\n/** Opens what the chip at `pos` names, as a click on it does when reading. */\nfunction openChip(view: EditorView, pos: number): void {\n const target = chipTarget(view, pos)\n if (target) {\n passing = true\n try {\n target.click()\n } finally {\n passing = false\n }\n return\n }\n const href = webHref(view, pos)\n if (href) window.open(href, '_blank', 'noopener,noreferrer')\n}\n\n/** Runs `row` of the menu open on the chip at `pos`, and closes it. */\nfunction choose(editor: Editor, pos: number, row: Row | undefined): void {\n dispatchMenu(editor.view, null)\n if (row === 'open') openChip(editor.view, pos)\n else if (row) setReferenceCardAt(editor, pos, row)\n}\n\n/** Whether the menu is open, for an editor's own Escape: while it is, Escape closes it rather than leaving. */\nexport function isReferenceMenuOpen(state: EditorState): boolean {\n return !!key.getState(state)\n}\n\n/**\n * A click on the chip at `pos`, from its node view: while editing, with the\n * menu installed, it opens the menu, and the caller stops the click there.\n * Returns false when the click should go on to the chip.\n */\nexport function referenceMenuClick(editor: Editor, pos: number): boolean {\n if (passing || editor.isDestroyed || !editor.isEditable || key.getState(editor.state) === undefined) return false\n // A chip with nothing to open and no card to choose has no menu: the click goes on to it.\n if (!chipAt(editor.state, pos) || rowsOf(editor.view, pos).length === 0) return false\n editor.view.dispatch(caretAfter(editor.state, pos).setMeta(key, { pos, active: -1 }))\n return true\n}\n\n/**\n * The caret right after the chip, as a paste leaves it: a selected chip would\n * bring up the selection toolbar beside the menu, and typing would replace it.\n */\nfunction caretAfter(state: EditorState, pos: number): Transaction {\n const node = state.doc.nodeAt(pos)\n return state.tr.setSelection(TextSelection.create(state.doc, pos + (node?.nodeSize ?? 1)))\n}\n\n/** Which chip the menu is open on: see the module comment. */\nexport const ReferenceMenu = Extension.create({\n name: 'referenceMenu',\n // Before the editor's own keys, so Enter on a highlighted row is the row's, not a new line.\n priority: 1000,\n addProseMirrorPlugins() {\n const editor = this.editor\n return [\n new Plugin<MenuState | null>({\n key,\n state: {\n init: () => null,\n apply: (tr, value, before, after) => {\n const meta = tr.getMeta(key) as MenuState | null | undefined\n if (meta !== undefined) return meta\n const placed = justPlaced(tr, after)\n if (placed !== null) return { pos: placed, active: -1 }\n if (!value) return null\n const pos = tr.mapping.map(value.pos)\n if (!chipAt(after, pos)) return null\n // What another plugin appends \u2014 an id, a trailing line \u2014 is not the person typing on, and nor is a URL\n // elsewhere becoming its chip when its answer comes back (code review).\n if (tr.docChanged && !tr.getMeta('appendedTransaction') && tr.getMeta(CHIPS_MADE) === undefined) return null\n const onChip = after.selection instanceof NodeSelection && after.selection.from === pos\n if (tr.selectionSet && !after.selection.eq(before.selection) && !onChip) return null\n return pos === value.pos ? value : { ...value, pos }\n },\n },\n props: {\n handleDOMEvents: {\n // Focus gone, the keys no longer reach the menu, and a card chosen now would miss the save on leaving (code review).\n blur: (view) => {\n if (key.getState(view.state)) dispatchMenu(view, null)\n return false\n },\n },\n handleKeyDown: (view, event) => {\n // With a modifier, a key is the editor's: Shift+\u2193 extends the selection, Mod+Enter is not a pick.\n if (event.shiftKey || event.ctrlKey || event.metaKey || event.altKey) return false\n const open = key.getState(view.state)\n if (!open) {\n // Enter on a selected chip: the menu, from the keyboard, its first row highlighted.\n const { selection } = view.state\n if (event.key !== 'Enter' || !view.editable || !(selection instanceof NodeSelection) || selection.node.type.name !== 'reference') return false\n if (rowsOf(view, selection.from).length === 0) return false\n view.dispatch(caretAfter(view.state, selection.from).setMeta(key, { pos: selection.from, active: 0 }))\n return true\n }\n const rows = rowsOf(view, open.pos)\n if (event.key === 'Escape') {\n dispatchMenu(view, null)\n return true\n }\n if (rows.length === 0) return false\n if (event.key === 'ArrowDown') {\n dispatchMenu(view, { ...open, active: (open.active + 1) % rows.length })\n return true\n }\n if (event.key === 'ArrowUp') {\n dispatchMenu(view, { ...open, active: open.active <= 0 ? rows.length - 1 : open.active - 1 })\n return true\n }\n if (event.key === 'Enter' && open.active >= 0) {\n choose(editor, open.pos, rows[open.active])\n return true\n }\n return false\n },\n },\n }),\n ]\n },\n})\n\n/** What the panel draws: the chip the menu is open on, and its card. */\ninterface Shown {\n pos: number\n active: number\n named: boolean\n layout: ReferenceCardLayout\n}\n\nexport interface ReferenceMenuViewProps {\n editor: Editor | null\n}\n\n/** The menu `ReferenceMenu` holds, hung under its chip. Render it always, beside `EditorContent`. */\nexport function ReferenceMenuView({ editor }: ReferenceMenuViewProps) {\n const shown = useEditorState({\n editor,\n selector: ({ editor: ed }): Shown | null => {\n if (!ed || ed.isDestroyed) return null\n const open = key.getState(ed.state)\n if (!open) return null\n const node = chipAt(ed.state, open.pos)\n if (!node) return null\n const embed = node.attrs.embed\n // An unknown layout draws compact (\u00A713.3), so it reads as compact here.\n const layout: ReferenceCardLayout = embed === 'detailed' ? 'detailed' : typeof embed === 'string' && embed !== '' && embed !== 'none' ? 'compact' : 'none'\n return { pos: open.pos, active: open.active, named: !!node.attrs.uri, layout }\n },\n })\n // A press anywhere in the panel keeps focus in the text, as a row's does: the editor saves and ends its menus when it loses focus.\n const keepFocus = useCallback((list: HTMLDivElement | null) => {\n const panel = list?.closest('[role=\"dialog\"]')\n if (!panel) return\n const keep = (event: Event) => event.preventDefault()\n panel.addEventListener('mousedown', keep)\n return () => panel.removeEventListener('mousedown', keep)\n }, [])\n\n const live = shown && editor && !editor.isDestroyed ? editor : null\n const dom = live && shown ? live.view.nodeDOM(shown.pos) : null\n const anchor = dom instanceof HTMLElement ? dom : null\n const rows = live && shown ? rowsOf(live.view, shown.pos) : []\n const opens = rows[0] === 'open'\n const run = (row: Row) => {\n if (editor && shown) choose(editor, shown.pos, row)\n }\n\n return (\n // finalFocus={false}: focus never left the text.\n <Popover\n anchor={anchor}\n open={!!shown && !!anchor && rows.length > 0}\n onOpenChange={(next) => {\n if (!next && editor && !editor.isDestroyed && key.getState(editor.state)) dispatchMenu(editor.view, null)\n }}\n side=\"bottom\"\n align=\"left\"\n ariaLabel=\"Link\"\n finalFocus={false}\n >\n {shown && (\n <div ref={keepFocus} role=\"menu\" aria-label=\"Link\" className=\"flex flex-col gap-0.5\">\n {/* Only when there is something to open: the chip's own control, or its URL (code review). */}\n {opens && (\n <MenuItem\n role=\"menuitem\"\n tabIndex={-1}\n label=\"Open\"\n selected={shown.active === 0}\n onMouseDown={(event) => {\n event.preventDefault()\n run('open')\n }}\n />\n )}\n {shown.named && (\n <>\n {opens && <MenuSeparator />}\n <div role=\"group\" aria-label=\"Card\">\n <MenuSection label=\"Card\">\n {LAYOUTS.map(({ layout, label }, i) => (\n <MenuItem\n key={layout}\n role=\"menuitemradio\"\n aria-checked={shown.layout === layout}\n tabIndex={-1}\n label={label}\n selected={shown.active === i + (opens ? 1 : 0)}\n trailing={shown.layout === layout ? <IconCheck size={16} aria-hidden /> : undefined}\n onMouseDown={(event) => {\n event.preventDefault()\n run(layout)\n }}\n />\n ))}\n </MenuSection>\n </div>\n </>\n )}\n </div>\n )}\n </Popover>\n )\n}\n", "/**\n * An app URL in a document reads as the chip of what it names \u2014 SPEC \u00A713.3,\n * \"A reference's card\", and \u00A77.7.\n *\n * The URL stays the URL: the chip is the `reference` node with `href` set (a\n * URL chip, `ReferenceNode`), which a document writes back as the URL it was,\n * plain text with no card and a `link` mark with one. Never a `nostr:`\n * reference in its place: the URL is how a person addresses the object.\n *\n * What a URL names is the app's to say \u2014 its manifests and its relay \u2014 so the\n * app hands this extension two functions: `referenceOf`, what it has resolved\n * so far, and `ask`, to look up the URLs it has not. A URL that names nothing,\n * or whose answer has not come back, stays text; when `ask` settles the\n * document is looked at again.\n *\n * The URL being typed stays text while the caret is in it or at its end, so a\n * half-typed address never turns into a chip under the person's fingers. A\n * pasted URL is whole, so it is asked about and made a chip at once, the caret\n * still at its end \u2014 where `ReferenceMenu` opens its menu.\n */\nimport { Extension, type Editor } from '@tiptap/core'\nimport { closeHistory } from '@tiptap/pm/history'\nimport type { Node as PmNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey, type EditorState, type Selection, type Transaction } from '@tiptap/pm/state'\n\nexport interface LinkChipsOptions {\n /**\n * The `nostr:` reference an app URL names, from what the app has resolved\n * (\u00A77.7) \u2014 protocol's `referenceOfLink(url, resolveLink)`. Undefined while it\n * names nothing, or its answer has not come back.\n */\n referenceOf: ((url: string) => string | undefined) | null\n /** Look up what these URLs name. When it settles, the document is looked at again. */\n ask: ((urls: string[]) => Promise<unknown> | void) | null\n /**\n * How many URLs one editor asks about in its life, the first ones; the rest\n * stay text. An app's answer can cost a relay query, and a document pasted\n * full of links must not spend the writer's quota. Default 24.\n */\n limit: number\n}\n\ninterface LinkChipsState {\n options: LinkChipsOptions\n /** The last few pastes, mapped through every edit since. */\n pasted: PastedRange[]\n}\n\nconst key = new PluginKey<LinkChipsState>('linkChips')\n\n/** The meta on the transaction that made chips: `{ pasted }`. */\nexport const CHIPS_MADE = 'linkChipsMade'\n\n/** How many pastes are remembered; one is what the person just did. */\nconst PASTES_KEPT = 8\n\n/**\n * A link on words in marker text (skipped), or a bare URL \u2014 the grammar of\n * protocol's `urlRunsIn`, which a reader turns into chips by, so what an editor\n * makes a chip is what a reader would.\n */\nconst LINK_OR_URL = /(\\[[^[\\]\\n]+\\]\\((?:https?:\\/\\/|mailto:)[^\\s)]+\\))|(https?:\\/\\/[^\\s<>\"'`]+)/g\nconst TRAILING = new Set(['.', ',', ';', ':', '!', '?', ')', ']'])\n\n/** Each URL and where it sits; `end` is where its run ends before trailing punctuation is trimmed, which typing may yet continue. */\nfunction urlRuns(text: string): { url: string; from: number; to: number; end: number }[] {\n if (!text.includes('://')) return []\n const out: { url: string; from: number; to: number; end: number }[] = []\n for (const m of text.matchAll(LINK_OR_URL)) {\n if (m[1]) continue\n const start = m.index\n if (text.slice(Math.max(0, start - 2), start) === '](' || text[start - 1] === '[') continue\n let end = m[0].length\n while (end > 0 && TRAILING.has(m[0][end - 1])) end--\n out.push({ url: m[0].slice(0, end), from: start, to: start + end, end: start + m[0].length })\n }\n return out\n}\n\ninterface Found {\n /** Text URLs that name something: made chips. */\n chips: { from: number; to: number; url: string; uri: string; marks: string[]; pasted: boolean }[]\n /** URL chips whose URL is answered now: given their reference. */\n fills: { pos: number; uri: string }[]\n /** Every URL still waiting for an answer, or naming nothing. */\n open: string[]\n}\n\n/** Where something was pasted: a URL wholly inside one was not typed, so the caret at its end does not hold it as text. */\nexport interface PastedRange {\n from: number\n to: number\n}\n\n/**\n * What to change in a document, and what to ask. `selection` null treats no\n * URL as being typed; a URL inside one of `pasted` is never being typed.\n */\nexport function findLinkChips(doc: PmNode, selection: Selection | null, referenceOf: LinkChipsOptions['referenceOf'], pasted: readonly PastedRange[] = []): Found {\n const found: Found = { chips: [], fills: [], open: [] }\n const open = new Set<string>()\n doc.descendants((node, pos) => {\n // A fence is literal (\u00A713.1): its URLs are text.\n if (node.type.spec.code) return false\n if (node.type.name === 'reference') {\n const href = typeof node.attrs.href === 'string' ? node.attrs.href : ''\n if (href && !node.attrs.uri) {\n const uri = referenceOf?.(href)\n if (uri) found.fills.push({ pos, uri })\n else open.add(href)\n }\n return false\n }\n if (!node.isText || !node.text) return true\n // A URL in code is literal.\n if (node.marks.some((m) => m.type.spec.code || m.type.name === 'code')) return false\n // A link on words is the link's. A link whose text is its own URL \u2014 an editor's autolink, or its paste rule \u2014 is\n // that URL, as a document reads one (protocol's `inlineToEditor`): its chip, with the link mark left behind.\n const link = node.marks.find((m) => m.type.name === 'link')\n if (link && !(link.attrs.href === node.text && /^https?:\\/\\//.test(node.text))) return false\n const runs = link ? [{ url: node.text, from: 0, to: node.text.length, end: node.text.length }] : urlRuns(node.text)\n for (const run of runs) {\n const from = pos + run.from\n const to = pos + run.to\n // Being typed or selected: the caret inside it or at its end (`https://a.` is still being typed), or a\n // selection over any of it. A caret before it is not.\n // A pasted URL is whole: it becomes its chip with the caret still at its end, so its menu can open at once.\n const wasPasted = pasted.some((range) => range.from <= from && pos + run.end <= range.to)\n const typing = selection && selection.to > from && selection.from <= pos + run.end\n if (typing && !(selection.empty && wasPasted)) continue\n const uri = referenceOf?.(run.url)\n if (uri) found.chips.push({ from, to, url: run.url, uri, marks: node.marks.filter((m) => m !== link).map((m) => m.type.name), pasted: wasPasted })\n else open.add(run.url)\n }\n return false\n })\n found.open = [...open]\n return found\n}\n\nfunction apply(state: EditorState, found: Found): Transaction | null {\n if (!found.chips.length && !found.fills.length) return null\n const type = state.schema.nodes.reference\n if (!type) return null\n const tr = state.tr\n // A fill keeps the node's size, so the chips' positions still hold; the chips go last first.\n for (const { pos, uri } of found.fills) {\n const node = tr.doc.nodeAt(pos)\n if (node) tr.setNodeMarkup(pos, undefined, { ...node.attrs, uri })\n }\n for (const chip of [...found.chips].reverse()) {\n tr.replaceWith(chip.from, chip.to, type.create({ uri: chip.uri, href: chip.url, marks: chip.marks }))\n }\n // Whether a pasted URL became its chip: `ReferenceMenu` opens on that one, never on a URL typed by hand.\n const pasted = found.chips.some((chip) => chip.pasted)\n tr.setMeta(CHIPS_MADE, { pasted })\n // A pasted URL's chip is its own undo step, so Ctrl+Z gives the URL back rather than taking the paste away (OPT-19,\n // as Peek's pasted link did). Any other chip is not a step of the person's: undo takes back what they typed.\n return pasted ? closeHistory(tr) : tr.setMeta('addToHistory', false)\n}\n\n/**\n * Look at the document again \u2014 after the app learned what more URLs name some\n * other way. Dispatches only when there is a chip to make: an empty\n * transaction would wake every other plugin (a trailing paragraph appears).\n */\nexport function refreshLinkChips(editor: Editor): void {\n if (editor.isDestroyed) return\n const chips = key.getState(editor.state)\n if (!chips) return\n const tr = apply(editor.state, findLinkChips(editor.state.doc, editor.state.selection, chips.options.referenceOf, chips.pasted))\n if (tr) editor.view.dispatch(tr)\n}\n\n/**\n * Turns the app URLs in a document into URL chips. Pair it with `ReferenceNode`;\n * `ReferenceCards` then draws a card a URL chip asks for.\n */\nexport const LinkChips = Extension.create<LinkChipsOptions>({\n name: 'linkChips',\n addOptions() {\n return { referenceOf: null, ask: null, limit: 24 }\n },\n addProseMirrorPlugins() {\n const options = this.options\n const editor = this.editor\n return [\n new Plugin<LinkChipsState>({\n key,\n // The options, where `refreshLinkChips` can read them from the state alone, and where the last pastes landed.\n state: {\n init: () => ({ options, pasted: [] }),\n apply: (tr, value, before, after) => {\n if (!tr.docChanged) return value\n // A paste edited inside is being typed again: forgotten, so each keystroke in it is not an ask (security review).\n const pasted = value.pasted\n .map((range) => ({ from: tr.mapping.map(range.from, 1), to: tr.mapping.map(range.to, -1), size: range.to - range.from }))\n .filter((range) => range.from < range.to && range.to - range.from === range.size)\n .map(({ from, to }) => ({ from, to }))\n if (tr.getMeta('uiEvent') === 'paste') {\n const from = tr.mapping.map(before.selection.from, -1)\n if (from < after.selection.to) pasted.push({ from, to: after.selection.to })\n }\n return { options, pasted: pasted.slice(-PASTES_KEPT) }\n },\n },\n appendTransaction: (trs, _old, state) => {\n if (!trs.some((tr) => tr.docChanged || tr.selectionSet)) return null\n return apply(state, findLinkChips(state.doc, state.selection, options.referenceOf, key.getState(state)?.pasted))\n },\n view: (view) => {\n // Each URL is asked once; one whose ask failed is forgotten, so a later look asks again.\n const asked = new Set<string>()\n // The URL being typed is not asked about: each prefix of it would spend one of `limit`, and a query.\n const look = (state: EditorState) => {\n if (!options.ask) return\n const { open } = findLinkChips(state.doc, state.selection, options.referenceOf, key.getState(state)?.pasted)\n const fresh = open.filter((url) => !asked.has(url)).slice(0, Math.max(0, options.limit - asked.size))\n if (!fresh.length) return\n for (const url of fresh) asked.add(url)\n const forget = () => {\n for (const url of fresh) asked.delete(url)\n }\n let answer: Promise<unknown> | void\n try {\n answer = options.ask(fresh)\n } catch {\n forget()\n return\n }\n void Promise.resolve(answer).then(() => refreshLinkChips(editor), forget)\n }\n look(view.state)\n // Once the view is up, so a dispatch lands: what the app already knows turns into chips on open.\n queueMicrotask(() => {\n try {\n refreshLinkChips(editor)\n } catch {\n // Unmounted before it ran.\n }\n })\n return {\n update: (next, prev) => {\n // A caret leaving a URL finishes it, so a move is looked at as an edit is.\n if (next.state.doc !== prev.doc || !next.state.selection.eq(prev.selection)) look(next.state)\n },\n }\n },\n }),\n ]\n },\n})\n", "/**\n * The cards a block document's writer chose, drawn after the line that holds\n * each reference \u2014 SPEC \u00A713.3, \"A reference's card\".\n *\n * The choice is `embed` on the `reference` node (`ReferenceNode`), so the card\n * moves and goes with its chip. What a card looks like is the app's \u2014 it knows\n * how to resolve an object and draw its row or its card \u2014 so this extension\n * draws nothing itself: after every textblock whose chips ask for a card it\n * places one empty, non-editable slot, and `useReferenceCards` hands the app\n * those slots to portal its cards into. A portal, rather than a React root per\n * slot, keeps the app's providers around the card.\n *\n * **A slot keeps its element while its block lives.** It is keyed by the\n * nearest block id (the `blockId` attribute), so typing above a card does not\n * re-make its element, and the card does not reload on every keystroke.\n *\n * The slot sits at the end of the textblock, inside it: a paragraph's card is\n * under the paragraph, a list item's under the item at its indent, a table\n * cell's in the cell.\n */\nimport { Extension, type Editor } from '@tiptap/core'\nimport type { Node as PmNode } from '@tiptap/pm/model'\nimport { NodeSelection, Plugin, PluginKey, type EditorState } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\nimport { useEffect, useState } from 'react'\n\n/** A layout a writer can choose; `none` is no card. */\nexport type ReferenceCardLayout = 'none' | 'compact' | 'detailed'\n\n/** One card to draw: the reference's `nostr:` URI and how much to show. */\nexport interface ReferenceCard {\n uri: string\n layout: Exclude<ReferenceCardLayout, 'none'>\n}\n\n/** One slot: where an app draws the cards of one line. */\nexport interface ReferenceCardSlot {\n key: string\n element: HTMLElement\n cards: ReferenceCard[]\n}\n\ninterface Placement {\n key: string\n /** The end of the textblock's content, where the slot goes. */\n pos: number\n cards: ReferenceCard[]\n}\n\n/** `embed` as a node holds it: an unknown string draws as compact (\u00A713.3), anything else is none. */\nfunction layoutOf(raw: unknown): ReferenceCard['layout'] | undefined {\n if (typeof raw !== 'string' || raw === '' || raw === 'none') return undefined\n return raw === 'detailed' ? 'detailed' : 'compact'\n}\n\n/** Every line that asks for a card, in document order. */\nexport function referenceCardPlacements(doc: PmNode): Placement[] {\n const out: Placement[] = []\n const seen = new Map<string, number>()\n doc.descendants((node, pos, parent) => {\n if (!node.isTextblock) return true\n const cards: ReferenceCard[] = []\n node.forEach((child) => {\n if (child.type.name !== 'reference') return\n const layout = layoutOf(child.attrs.embed)\n // A URL chip whose URL is not resolved yet (`LinkChips`) has nothing to draw a card of, yet.\n if (layout && child.attrs.uri) cards.push({ uri: String(child.attrs.uri), layout })\n })\n if (cards.length) {\n // The textblock's own id, or its list item's or cell's: the nearest one.\n const id = node.attrs.blockId ?? parent?.attrs.blockId\n const base = id ? `b:${String(id)}` : `p:${pos}`\n const n = seen.get(base) ?? 0\n seen.set(base, n + 1)\n out.push({ key: n ? `${base}:${n}` : base, pos: pos + node.nodeSize - 1, cards })\n }\n return false\n })\n return out\n}\n\ninterface ReferenceCardsStorage {\n /** One element per slot key, made once and reused, so the app's portal stays put. */\n elements: Map<string, HTMLElement>\n}\n\nconst key = new PluginKey<DecorationSet>('referenceCards')\n\nfunction elementFor(storage: ReferenceCardsStorage, slotKey: string): HTMLElement {\n let element = storage.elements.get(slotKey)\n if (!element) {\n element = document.createElement('div')\n element.setAttribute('data-reference-cards', slotKey)\n element.setAttribute('contenteditable', 'false')\n storage.elements.set(slotKey, element)\n }\n return element\n}\n\nfunction storageOf(editor: Editor): ReferenceCardsStorage | undefined {\n return (editor.storage as unknown as { referenceCards?: ReferenceCardsStorage }).referenceCards\n}\n\n/**\n * Places a slot after each line whose chips ask for a card. Pair it with\n * `ReferenceNode`, and draw the cards with `useReferenceCards`.\n */\nexport const ReferenceCards = Extension.create<Record<string, never>, ReferenceCardsStorage>({\n name: 'referenceCards',\n addStorage() {\n return { elements: new Map() }\n },\n addProseMirrorPlugins() {\n const storage = this.storage\n const decorate = (doc: PmNode) =>\n DecorationSet.create(\n doc,\n referenceCardPlacements(doc).map((p) =>\n Decoration.widget(p.pos, () => elementFor(storage, p.key), {\n key: p.key,\n side: 1,\n ignoreSelection: true,\n // A click on a card is the card's, not a caret placement in the line.\n stopEvent: () => true,\n }),\n ),\n )\n return [\n new Plugin<DecorationSet>({\n key,\n state: {\n init: (_, state) => decorate(state.doc),\n apply: (tr, set) => (tr.docChanged ? decorate(tr.doc) : set),\n },\n props: { decorations: (state) => key.getState(state) },\n }),\n ]\n },\n})\n\n/**\n * The slots to draw cards into, kept current as the document changes. Draw\n * each with `createPortal(<cards/>, slot.element)`, keyed by `slot.key`.\n */\nexport function useReferenceCards(editor: Editor | null): ReferenceCardSlot[] {\n const [slots, setSlots] = useState<ReferenceCardSlot[]>([])\n useEffect(() => {\n if (!editor) return\n const storage = storageOf(editor)\n if (!storage) return\n let last = ''\n const read = () => {\n if (editor.isDestroyed) return\n const next = referenceCardPlacements(editor.state.doc).map((p) => ({ key: p.key, element: elementFor(storage, p.key), cards: p.cards }))\n // Only a change in what is drawn re-renders the app; typing does not.\n const signature = JSON.stringify(next.map((s) => [s.key, s.cards]))\n if (signature === last) return\n last = signature\n const live = new Set(next.map((s) => s.key))\n for (const k of storage.elements.keys()) if (!live.has(k)) storage.elements.delete(k)\n setSlots(next)\n }\n read()\n editor.on('transaction', read)\n return () => {\n editor.off('transaction', read)\n }\n }, [editor])\n return slots\n}\n\n/**\n * The chip the person has selected, with what it names and its card \u2014 or null.\n *\n * Selected as a node (a click, where a click on the chip does not follow its\n * link), or inside a selected range that holds that one chip and nothing but\n * spaces besides (Shift+arrow over it, or a drag across it): an app whose chip\n * is a link has no other way to select it.\n */\nexport function referenceAtSelection(state: EditorState): { pos: number; uri: string; layout: ReferenceCardLayout } | null {\n const { selection } = state\n let found: { pos: number; node: PmNode } | null = null\n if (selection instanceof NodeSelection) {\n if (selection.node.type.name === 'reference') found = { pos: selection.from, node: selection.node }\n } else if (!selection.empty) {\n let only = true\n state.doc.nodesBetween(selection.from, selection.to, (node, pos) => {\n if (node.type.name === 'reference') {\n if (found) only = false\n found = { pos, node }\n return false\n }\n if (node.isText) {\n const from = Math.max(selection.from, pos) - pos\n const to = Math.min(selection.to, pos + node.nodeSize) - pos\n if ((node.text ?? '').slice(from, to).trim() !== '') only = false\n }\n return true\n })\n if (!only) found = null\n }\n if (!found) return null\n const { pos, node } = found as { pos: number; node: PmNode }\n // A URL chip that names nothing yet has no card to choose.\n if (!node.attrs.uri) return null\n return { pos, uri: String(node.attrs.uri ?? ''), layout: layoutOf(node.attrs.embed) ?? 'none' }\n}\n\n/** Sets the card of the selected chip. `none` removes it. The selection stays as it was. */\nexport function setReferenceCard(editor: Editor, layout: ReferenceCardLayout): boolean {\n const at = referenceAtSelection(editor.state)\n return at ? setReferenceCardAt(editor, at.pos, layout) : false\n}\n\n/** Sets the card of the chip at `pos` \u2014 one that names something. `none` removes it. The selection stays as it was. */\nexport function setReferenceCardAt(editor: Editor, pos: number, layout: ReferenceCardLayout): boolean {\n const node = editor.state.doc.nodeAt(pos)\n if (!node || node.type.name !== 'reference' || !node.attrs.uri) return false\n const wasNode = editor.state.selection instanceof NodeSelection && editor.state.selection.from === pos\n // Same size, so a range maps onto itself; a node selection is made again, since the node is replaced.\n const tr = editor.state.tr.setNodeMarkup(pos, undefined, { ...node.attrs, embed: layout === 'none' ? null : layout })\n if (wasNode) tr.setSelection(NodeSelection.create(tr.doc, pos))\n editor.view.dispatch(tr)\n return true\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\nimport { IconBold, IconCode, IconItalic, IconLink, IconUnderline } from '@tabler/icons-react'\nimport { Form } from './Form'\nimport { Popover } from './Popover'\nimport { TextInput } from './TextInput'\nimport { Toolbar, ToolbarButton } from './Toolbar'\nimport { shortcutLabel } from './shortcuts'\n\n/**\n * The selection, kept visible while something else has the focus \u2014 Peek's\n * `src/extensions/keptSelection.ts` (PEE-19), moved into the package with the\n * `SelectionToolbar` that needs it (UIG-31, Katerina's ruling 29 September).\n *\n * The toolbar's address field takes the focus, and with it the document's\n * one selection, so the words being linked lost their highlight the moment\n * the field opened (measured 2026-09-07). Linear keeps it, and Katerina asked\n * for the same. The editor's own selection never moved \u2014 ProseMirror keeps it\n * while unfocused \u2014 so this only *draws* it: an inline decoration over the\n * range, in `bg-selected` \u2014 the token Peek's composer paints `::selection` in,\n * mapped through edits and cleared when the field closes.\n *\n * A decoration rather than a second selection, because a document has one\n * and the field needs it. Editor-internal by design (the editor owns its own\n * drawing); nothing here floats, focuses or handles a key.\n */\n\nexport interface KeptRange {\n from: number\n to: number\n}\n\n/** The plugin's key: read what is being kept, or dispatch a change to it. */\nexport const keptSelectionKey = new PluginKey<KeptRange | null>('keptSelection')\n\n/** The extension an editor registers so a selection can stay drawn while a field has the focus. */\nexport const KeptSelection = Extension.create({\n name: 'keptSelection',\n\n addProseMirrorPlugins() {\n return [\n new Plugin<KeptRange | null>({\n key: keptSelectionKey,\n state: {\n init: () => null,\n apply(tr, previous) {\n const meta = tr.getMeta(keptSelectionKey) as KeptRange | null | undefined\n if (meta !== undefined) return meta\n if (!previous) return null\n // Follow the words through an edit, so applying the link to them\n // (or typing before them) does not leave the highlight behind.\n return { from: tr.mapping.map(previous.from), to: tr.mapping.map(previous.to) }\n },\n },\n props: {\n decorations(state) {\n const range = keptSelectionKey.getState(state)\n if (!range || range.from === range.to) return null\n return DecorationSet.create(state.doc, [\n Decoration.inline(range.from, range.to, { class: 'kept-selection bg-bg-selected' }),\n ])\n },\n },\n }),\n ]\n },\n})\n\n/** Draw the selection as it is now, until `releaseSelection`. */\nexport function keepSelection(editor: Editor) {\n const { from, to } = editor.state.selection\n editor.view.dispatch(editor.state.tr.setMeta(keptSelectionKey, { from, to }))\n}\n\n/** Stop drawing it. Nothing kept, nothing dispatched. */\nexport function releaseSelection(editor: Editor) {\n if (editor.isDestroyed || !keptSelectionKey.getState(editor.state)) return\n editor.view.dispatch(editor.state.tr.setMeta(keptSelectionKey, null))\n}\n\n/** What is being drawn, or null. */\nexport function keptSelection(editor: Editor): KeptRange | null {\n return keptSelectionKey.getState(editor.state) ?? null\n}\n\n/** A mark the strip can toggle \u2014 the suite's inline vocabulary (SPEC \u00A713.1) less link and reference. */\nexport type MarkId = 'bold' | 'italic' | 'underline' | 'code'\n\nconst MARKS: Record<MarkId, { label: string; keys: string }> = {\n bold: { label: 'Bold', keys: 'Mod-B' },\n italic: { label: 'Italic', keys: 'Mod-I' },\n underline: { label: 'Underline', keys: 'Mod-U' },\n code: { label: 'Code', keys: 'Mod-E' },\n}\n\n/**\n * Toggle one mark over the current selection. The core `toggleMark`, which\n * each mark's own `toggleBold` and the like wrap: the package names no Tiptap\n * extension, only the marks the suite's format has (SPEC \u00A713.1).\n */\nfunction toggleMark(editor: Editor, id: MarkId): boolean {\n return editor.chain().focus().toggleMark(id).run()\n}\n\n/** The address the selection sits in, or '' \u2014 what the link field opens with. */\nexport function currentLink(editor: Editor): string {\n const href: unknown = editor.getAttributes('link').href\n return typeof href === 'string' ? href : ''\n}\n\n/**\n * What somebody typed into the link field \u2192 an `href`, or null when it cannot\n * be one. A bare host (`google.com`) gets `https://`. A scheme is kept only when\n * SPEC \u00A713.1 lets a reader follow it (http, https, mailto); `javascript:` and\n * the like are refused here. `localhost:5173` is a host and a port: a scheme is\n * never followed by a digit.\n */\nexport function normalizeHref(raw: string): string | null {\n const s = raw.trim()\n if (!s || /\\s/.test(s)) return null\n if (/^(https?:|mailto:)/i.test(s)) return s\n if (/^[a-z][a-z0-9+.-]*:(?!\\d)/i.test(s)) return null\n return `https://${s}`\n}\n\n/**\n * Apply the link field to the selection. Empty removes the link; an address\n * sets it, over the whole link when the caret sits inside one. `false` means\n * the text could not be an address, and the field should stay open.\n */\nexport function applyLink(editor: Editor, raw: string): boolean {\n const chain = editor.chain().focus().extendMarkRange('link')\n // The core commands the Link extension's `setLink` and `unsetLink` wrap, with\n // the same meta; `normalizeHref` has already refused what it would refuse.\n if (!raw.trim()) return chain.unsetMark('link', { extendEmptyMarkRange: true }).setMeta('preventAutolink', true).run()\n const href = normalizeHref(raw)\n if (!href) return false\n return chain.setMark('link', { href }).setMeta('preventAutolink', true).run()\n}\n\n/**\n * Bold / italic / underline / link over selected text \u2014 Peek's\n * `SelectionToolbar` (PEE-18, PEE-19), moved into the package at Katerina's\n * word (29 September, UIG-31) when Ship's description became its second user.\n * Unchanged but for two props: `marks`, which buttons the strip has (Peek's\n * three by default; Ship adds code), and `link`, whether it has the Link button\n * and its field (Peek's editor has a link mark; an editor without one leaves it\n * off). The editor must register `KeptSelection` when `link` is on.\n *\n * The marks apply to a run you have already written, which is why they are\n * not in the `/` menu: typing `/` replaces the selection, so by definition\n * there is nothing selected to apply them to.\n *\n * **The chrome is the shared `Popover`, anchored** (P26). It was a `Menu`, and\n * it never was one: a menu gives its contents roving focus and typeahead,\n * which would fight the link field this panel exists for. An anchored\n * `Popover` is the mode built for exactly this \u2014 it takes no focus, so the\n * caret stays in the text. It is laid out as a row instead of a column\n * through `className`, which `cn` merges last. The panel still portals out of\n * the composer, so no overflow clips it, and Floating UI clamps it on screen.\n *\n * **The anchor is the selection's own point** since 0.12.2. This toolbar is\n * *centred* over the selection (her design, PEE-19, after Linear), and the\n * package's `Popover` aligns left or right only \u2014 Base UI has `center`, the\n * package does not expose it (ADOPTION **B21**). So the placement arithmetic\n * and the hand-measured `SIZE` box stay for now, and the rect handed over is\n * positioned so the panel lands on the same pixel it always has: the\n * package's own 4px gap is subtracted from this file's 8px, and the rect's\n * left edge is the panel's. When `align=\"center\"` exists, `SIZE`, `GAP` and\n * `placement` all go and the anchor becomes the selection's own rect.\n *\n * **Two faces** (PEE-19 \u2014 Katerina, 2026-09-07, after Linear). `marks` is the\n * row of four buttons. `link` is one text field for the address: it opens from\n * the Link button, and opens by itself when the settled selection sits inside\n * a link, with that link's address in it. There is no open, embed or delete\n * button, by her call: the field is the whole control.\n *\n * **What applies the field** (her rule, same day): Enter, and clicking away \u2014\n * anywhere outside the toolbar, the editor included. Enter on an empty field\n * removes the link; clicking away with an empty field changes nothing, since\n * removing is deliberate and leaving is not. Escape drops what was typed and\n * puts the caret back. Once applied, the caret lands after the link, and\n * typing goes on as plain text \u2014 the mark is not inclusive (editorKit).\n *\n * While the field has focus the editor has not, so everything below that\n * hides the toolbar on blur or on an empty editor selection stands down in\n * `link` mode. The editor keeps its selection while unfocused; `focus()` in\n * the apply command puts it back.\n *\n * **Why it waits.** Showing on every selection change put the toolbar under the\n * pointer mid-drag, and made it blink on a plain click \u2014 ProseMirror restores\n * the previous selection when the editor takes focus and collapses it a tick\n * later, so there is a real, non-empty range in between. Both go away by\n * refusing to show while the pointer is down and re-reading the selection after\n * a short settle rather than trusting the event that woke us.\n */\n\nconst ICONS: Record<MarkId, typeof IconBold> = {\n bold: IconBold,\n italic: IconItalic,\n underline: IconUnderline,\n code: IconCode,\n}\n\ntype Mode = 'marks' | 'link'\n\n/** How long a selection must stand still before the toolbar appears. */\nconst SETTLE_MS = 250\nconst GAP = 8\n/** The gap the package's `Popover` adds of its own, subtracted above so the\n * panel keeps this file's 8px. Read from `Popover.tsx`, not guessed. */\nconst POPOVER_GAP = 4\n\nexport interface SelectionToolbarProps {\n /** The editor whose selection the strip follows. `null` while it loads. */\n editor: Editor | null\n /** Which marks the strip toggles, in order. Default bold, italic, underline. */\n marks?: readonly MarkId[]\n /** The Link button and its address field. Needs a link mark in the editor and `KeptSelection`. Default off. */\n link?: boolean\n /**\n * More of the strip, after the marks: the app's own `ToolbarButton`s for what\n * is selected \u2014 a chip's card, say. Each keeps the selection by preventing\n * its press's default, as the marks do. Nothing is added when it renders\n * nothing.\n */\n children?: ReactNode\n}\n\nexport function SelectionToolbar({ editor, marks = ['bold', 'italic', 'underline'], link = false, children }: SelectionToolbarProps) {\n /** The selection's top edge and horizontal centre; the face decides the rest. */\n const [anchor, setAnchor] = useState<{ top: number; centre: number } | null>(null)\n const [mode, setMode] = useState<Mode>('marks')\n const [url, setUrl] = useState('')\n // Re-render as marks turn on and off while the caret moves through the\n // selection, so the pressed states stay honest.\n const [, setTick] = useState(0)\n const timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)\n const pointerDown = useRef(false)\n const field = useRef<HTMLInputElement>(null)\n /** What the field holds while it is open; null while it is closed. The\n * listeners below are bound once, so they read this rather than state. */\n const draft = useRef<string | null>(null)\n // Read by the same listeners. Kept in an effect, declared before the focus\n // effect below so the ref already says `link` when the field's focus blurs\n // the editor.\n const modeRef = useRef<Mode>('marks')\n useEffect(() => {\n modeRef.current = mode\n }, [mode])\n // Read by `settle`, which is bound once: words already a link open the field only when there is one.\n const linkOn = useRef(link)\n linkOn.current = link\n\n const hide = useCallback(() => {\n clearTimeout(timer.current)\n draft.current = null\n if (editor) releaseSelection(editor)\n setAnchor(null)\n setMode('marks')\n }, [editor])\n\n /**\n * Leaving the field applies it (Enter or a click away). An address links the\n * words and the caret moves past them; an empty field on the way out is a\n * shrug, not a removal. Idempotent: the outside click reaches this twice,\n * once from the editor press and once from the Menu's own listener.\n */\n const commit = useCallback(() => {\n const text = draft.current\n if (text === null || !editor) return\n draft.current = null\n if (text.trim() && applyLink(editor, text)) {\n editor.commands.setTextSelection(editor.state.selection.to)\n }\n hide()\n }, [editor, hide])\n\n /** Read the selection as it is *now* and place the toolbar, or hide it. */\n const settle = useCallback(() => {\n if (!editor || editor.isDestroyed) return\n // Keeping the selection drawn is itself a transaction, which schedules\n // this; by then the field has the focus, and reading `hasFocus()` here\n // would close it.\n if (modeRef.current === 'link') return\n const { state, view } = editor\n const { empty, from, to } = state.selection\n if (empty || from === to || !view.hasFocus() || pointerDown.current) {\n setAnchor(null)\n setMode('marks')\n return\n }\n const start = view.coordsAtPos(from)\n const end = view.coordsAtPos(to)\n const centre = (Math.min(start.left, end.left) + Math.max(start.right, end.right)) / 2\n // Words that are already a link open on the field, with their address in it.\n const inLink = linkOn.current && editor.isActive('link')\n const address = inLink ? currentLink(editor) : ''\n draft.current = inLink ? address : null\n // The field is about to take the focus; the words stay highlighted.\n if (inLink) keepSelection(editor)\n setMode(inLink ? 'link' : 'marks')\n setUrl(address)\n setAnchor({ top: start.top, centre })\n }, [editor])\n\n useEffect(() => {\n if (!editor) return\n\n const schedule = () => {\n // The field owns the keyboard and the focus; the editor's own events\n // stand down until it closes.\n if (modeRef.current === 'link') return\n setTick((t) => t + 1)\n clearTimeout(timer.current)\n // A collapsed selection hides at once \u2014 waiting to hide is what made the\n // click blink. Only *showing* is deferred.\n if (editor.state.selection.empty) {\n setAnchor(null)\n return\n }\n timer.current = setTimeout(settle, SETTLE_MS)\n }\n\n // The field taking focus is the one blur that must not hide us.\n const onBlur = () => {\n if (modeRef.current !== 'link') hide()\n }\n\n // Only a press *inside the editor* starts a selection. A press on the\n // toolbar itself must not hide it \u2014 doing so unmounted the button before\n // its click could land, so nothing ever applied.\n const startsSelection = (e: MouseEvent) =>\n e.target instanceof Node && editor.view.dom.contains(e.target)\n\n const onPointerDown = (e: MouseEvent) => {\n if (!startsSelection(e)) return\n // A press in the editor while the field is open is a click away: apply\n // first, before the editor moves the caret under the press.\n commit()\n pointerDown.current = true\n hide()\n }\n const onPointerUp = () => {\n if (!pointerDown.current) return\n pointerDown.current = false\n schedule()\n }\n\n editor.on('selectionUpdate', schedule)\n editor.on('transaction', schedule)\n editor.on('blur', onBlur)\n // @estiva-escape(no-hand-rolled-behaviour): watches presses in the editor to follow a text selection; the Popover it opens in has no trigger to own that (Peek's UIG-8 escape, moved here with the part, UIG-31)\n document.addEventListener('mousedown', onPointerDown, true)\n document.addEventListener('mouseup', onPointerUp, true)\n\n return () => {\n clearTimeout(timer.current)\n editor.off('selectionUpdate', schedule)\n editor.off('transaction', schedule)\n editor.off('blur', onBlur)\n document.removeEventListener('mousedown', onPointerDown, true)\n document.removeEventListener('mouseup', onPointerUp, true)\n }\n }, [editor, settle, hide, commit])\n\n // The field takes focus once it is on screen. Not `autoFocus`, and not in\n // the same frame: the Menu renders once hidden to measure itself, and a\n // hidden input cannot take focus \u2014 measured 2026-09-07, the field opened\n // unfocused on a re-selected link. An address already in it is selected\n // whole, so typing replaces it.\n useEffect(() => {\n if (mode !== 'link' || !anchor) return\n const frame = requestAnimationFrame(() => {\n field.current?.focus()\n field.current?.select()\n })\n return () => cancelAnimationFrame(frame)\n }, [mode, anchor])\n\n if (!editor || !anchor) return null\n\n const openField = () => {\n const address = currentLink(editor)\n draft.current = address\n keepSelection(editor)\n setUrl(address)\n setMode('link')\n }\n /** Enter. Unlike a click away, an empty field here means remove. */\n const submit = () => {\n if (!url.trim()) {\n applyLink(editor, '')\n hide()\n editor.commands.focus()\n return\n }\n commit()\n }\n const discard = () => {\n hide()\n editor.commands.focus()\n }\n\n /* A zero-sized rect at the selection's own top edge and centre. The panel's\n width is no longer this file's business: `align=\"center\"` puts the\n panel's middle over this point (B21, package 0.12.2), so the measured\n `SIZE` table and the placement arithmetic that stood in for it are gone.\n The top is this file's GAP less the package's own 4, so the panel keeps\n the 8px it has always had. */\n const anchorRect = new DOMRect(anchor.centre, anchor.top - (GAP - POPOVER_GAP), 0, 0)\n\n return (\n <Popover\n anchor={anchorRect}\n open\n side=\"top\"\n align=\"center\"\n onOpenChange={(next) => {\n if (!next) commit()\n }}\n ariaLabel=\"Text formatting\"\n // The panel gives up its column; the strip inside draws the row. The 4px\n // around it is `contentClassName`'s: on `className` it was added to the\n // package's 8px from 0.12.6 on, and the toolbar grew 8px a side\n // (Katerina, 2026-09-13; migration docs D68, Finding 60). This is the\n // package's own `Popover/A toolbar` story, class for class.\n className=\"w-auto min-w-0\"\n contentClassName=\"p-1\"\n >\n {mode === 'link' ? (\n /* One field, so no strip: a toolbar of one control is a toolbar in\n name only, and `ToolbarInput` cannot take the ref this field is\n focused by (ADOPTION B25). */\n // The package Form (UIG-7): Enter applies the link. `free` and `flex`: one field, which keeps its own height.\n <Form onSubmit={submit} layout=\"free\" className=\"flex\">\n <TextInput\n ref={field}\n value={url}\n onChange={(e) => {\n setUrl(e.target.value)\n draft.current = e.target.value\n }}\n onKeyDown={(e) => {\n if (e.key === 'Escape') {\n // Before the Menu's own Escape, which would apply instead.\n e.stopPropagation()\n discard()\n }\n }}\n placeholder=\"Enter link URL\"\n aria-label=\"Link URL\"\n className=\"w-[264px]\"\n />\n </Form>\n ) : (\n /* Four marks and a link button: one Tab stop, \u2190 \u2192 along them (D29). */\n <Toolbar aria-label=\"Text formatting\" surface={false}>\n {marks.map((id) => {\n const Icon = ICONS[id]\n const active = editor.isActive(id)\n return (\n <ToolbarButton\n key={id}\n aria-label={MARKS[id].label}\n pressed={active}\n tooltip={MARKS[id].label}\n tooltipShortcut={shortcutLabel(MARKS[id].keys)}\n // Keep the selection: focus must not leave the editor on press.\n onMouseDown={(e) => e.preventDefault()}\n onClick={() => toggleMark(editor, id)}\n >\n <Icon size={16} stroke={1.5} />\n </ToolbarButton>\n )\n })}\n {link && (\n <ToolbarButton\n aria-label=\"Link\"\n tooltip=\"Link\"\n onMouseDown={(e) => e.preventDefault()}\n onClick={openField}\n >\n <IconLink size={16} stroke={1.5} />\n </ToolbarButton>\n )}\n {children}\n </Toolbar>\n )}\n </Popover>\n )\n}\n", "import { Extension } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\n\n/** As much of a `DataTransfer` as the paste reads. */\nexport interface ClipboardSource {\n files?: ArrayLike<File> | null\n getData(format: string): string\n}\n\n/** MIME type \u2192 the extension a pasted picture is named with. */\nconst IMAGE_EXTENSIONS: Record<string, string> = {\n 'image/png': 'png',\n 'image/jpeg': 'jpg',\n 'image/gif': 'gif',\n 'image/webp': 'webp',\n 'image/heic': 'heic',\n 'image/heif': 'heif',\n 'image/svg+xml': 'svg',\n}\n\n/** Names a browser invents for a clipboard picture. A made-up one is better. */\nconst GENERIC_STEMS = new Set(['image', 'blob', 'unknown', 'untitled', 'screenshot'])\n\n/*\n An unlisted type keeps its own subtype rather than becoming `png`, so a\n picture the app does not accept is refused by the app's own message instead\n of arriving disguised as one it does.\n*/\nfunction extensionFor(contentType: string): string {\n const type = contentType.toLowerCase()\n return IMAGE_EXTENSIONS[type] ?? type.split('/')[1]?.split('+')[0] ?? 'png'\n}\n\n/** `'Screenshot 2026-09-07 14-32-05.png'`: sortable, and readable on a file card. */\nexport function pastedImageName(contentType: string, at: Date = new Date(), index = 0): string {\n const pad = (n: number) => String(n).padStart(2, '0')\n const stamp =\n `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())} ` +\n `${pad(at.getHours())}-${pad(at.getMinutes())}-${pad(at.getSeconds())}`\n const nth = index > 0 ? ` (${index + 1})` : ''\n return `Screenshot ${stamp}${nth}.${extensionFor(contentType)}`\n}\n\n/** A real filename is kept; `image.png`, `blob` and '' are not names. */\nfunction hasUsableName(name: string): boolean {\n const dot = name.lastIndexOf('.')\n if (dot <= 0 || dot === name.length - 1) return false\n return !GENERIC_STEMS.has(name.slice(0, dot).toLowerCase())\n}\n\n/**\n * The pictures on the clipboard, named \u2014 `[]` when there is nothing to attach.\n *\n * **Text wins.** A copy from a spreadsheet or a document puts a rendered\n * picture of the selection beside the text; attaching the picture instead of\n * pasting the text is wrong every time. So a clipboard holding text is left to\n * the editor, and a screenshot, which holds none, lands here. Pictures only:\n * any other file comes through the app's own file picker.\n */\nexport function pastedImages(source: ClipboardSource | null | undefined, at: Date = new Date()): File[] {\n if (!source) return []\n if (source.getData('text/plain').trim() !== '') return []\n const images = Array.from(source.files ?? []).filter((f) => f.type.toLowerCase().startsWith('image/'))\n let generated = 0\n return images.map((file) =>\n hasUsableName(file.name)\n ? file\n : new File([file], pastedImageName(file.type, at, generated++), { type: file.type, lastModified: file.lastModified }),\n )\n}\n\nexport interface PastedImagesOptions {\n /**\n * The pictures a paste carried, named. Return `true` when the app took them\n * (an upload started), `false` to let the editor paste as it would have \u2014\n * for a box that cannot upload right now.\n */\n onImages: (files: File[]) => boolean\n}\n\n/**\n * Pasting a picture attaches it: a screenshot pasted into the editor reaches\n * the app as named files, the way the app's own file picker would hand them.\n * Text on the clipboard wins (see {@link pastedImages}).\n */\nexport const PastedImages = Extension.create<PastedImagesOptions>({\n name: 'pastedImages',\n addOptions() {\n return { onImages: () => false }\n },\n addProseMirrorPlugins() {\n const options = this.options\n return [\n new Plugin({\n key: new PluginKey('pastedImages'),\n props: {\n handlePaste: (_view, event) => {\n const files = pastedImages(event.clipboardData)\n return files.length > 0 && options.onImages(files)\n },\n },\n }),\n ]\n },\n})\n", "/** The editor's code block, coloured by language like RichText's (`@estiva-app/ui/editor`). */\nimport { CodeBlockLowlight } from '@tiptap/extension-code-block-lowlight'\nimport { lowlight } from './codeColours'\n\n/*\n The editor library guesses a language for a block that names none, or names\n one it does not know, and colours it by the guess. RichText never guesses, so\n that block was coloured while written and plain once read. A block is\n coloured only by the language it names, while written and when read\n (Katerina, 9 October: never guess).\n*/\nconst namedOnly = {\n highlight: (language: string, value: string) => lowlight().highlight(language, value),\n highlightAuto: (value: string) => ({ type: 'root' as const, children: [{ type: 'text' as const, value }], data: { language: undefined, relevance: 0 } }),\n listLanguages: () => lowlight().listLanguages(),\n registered: (language: string) => lowlight().registered(language),\n}\n\n/**\n * A code block in the editor, with the colours RichText reads it in (Katerina,\n * 9 October): the same highlight.js languages, the same classes, and the\n * editor's root wears `richTextClassName`, which colours them.\n *\n * It replaces the starter kit's code block: `StarterKit.configure({ codeBlock: false })`,\n * then add `CodeBlockColours`. The block keeps its name, `codeBlock`, and its\n * `language` attribute, so nothing already written changes.\n */\nexport const CodeBlockColours = CodeBlockLowlight.configure({ lowlight: namedOnly })\n", "import { createElement, type ReactNode } from 'react'\nimport { common, createLowlight } from 'lowlight'\n\n/**\n * Colours inside a code block, by language (Katerina, 9 October).\n *\n * highlight.js reads the code and names each word (a keyword, a string, a\n * comment); RichText's class list gives each name its colour from the theme's\n * `syntax-*` tokens. The same instance colours the editor's code block\n * (`CodeBlockColours`), so what is written looks the same as what is read.\n *\n * `common` is highlight.js's 37 everyday languages. A language outside them, or\n * none, leaves the code in one colour.\n *\n * Made the first time code is coloured, not when the package loads: a call at\n * the top of the file is kept by every app's bundler, so an app that never draws\n * code carried highlight.js too (measured: an app importing only `Link` was\n * 55 KB gzipped).\n */\nlet instance: ReturnType<typeof createLowlight> | undefined\nexport function lowlight() {\n return (instance ??= createLowlight(common))\n}\n\ninterface HastNode {\n type: string\n value?: string\n properties?: { className?: string[] }\n children?: HastNode[]\n}\n\nfunction draw(node: HastNode, key: number): ReactNode {\n if (node.type === 'text') return node.value\n return createElement('span', { key, className: node.properties?.className?.join(' ') }, (node.children ?? []).map(draw))\n}\n\n/** The code, its words wrapped in highlight.js's classes; the text as it is when the language is unknown. */\nexport function codeColours(text: string, language?: string): ReactNode {\n if (!language || !lowlight().registered(language)) return text\n return (lowlight().highlight(language, text).children as HastNode[]).map(draw)\n}\n", "// @estiva-escape(component-has-a-page, component-has-a-story): draws nothing of its own \u2014 it opens a SuggestionMenu from a Tiptap suggestion plugin, and SuggestionMenu's page and stories show it (UIG-31)\nimport { forwardRef, useRef, useImperativeHandle } from 'react'\nimport { ReactRenderer } from '@tiptap/react'\nimport { exitSuggestion, type SuggestionKeyDownProps, type SuggestionOptions, type SuggestionProps } from '@tiptap/suggestion'\nimport { SuggestionMenu, type SuggestionMenuHandle, type SuggestionMenuRow, type SuggestionMenuSection } from './SuggestionMenu'\n\n/*\n * Is a type-ahead list open? \u2014 Peek's counter (`mention.tsx`), moved here with\n * the lists it counts (UIG-31).\n *\n * A counter rather than a boolean: two suggestion plugins can overlap for a\n * moment while one hands over to the other (`/` choosing \"Mention\" types `@`).\n * An editor asks before it acts on Enter or Escape, so a key that chose a row\n * does not also send the message or cancel the edit.\n *\n * A list counts while it shows rows, not while its trigger listens: since a\n * space stopped closing one (f586437a), \"meet @ 5pm\" leaves `@` listening\n * behind a hidden list to the end of the line, and that Enter must still send.\n *\n * And only while its editor has focus (71a4577b): a list left by a click ends,\n * or it would stay counted and swallow Escape in the box clicked into.\n */\nlet openCount = 0\nlet lastClose = 0\n\n/**\n * Open, or closed less than 100ms ago \u2014 what an Enter handler asks. Choosing a\n * row with Enter closes the list, and that same Enter must not also send.\n */\nexport function isSuggestionActive(): boolean {\n return openCount > 0 || Date.now() - lastClose < 100\n}\n\n/**\n * Open *now*, with no grace period \u2014 what an Escape handler asks. The first\n * Escape closes the list, the second is somebody's deliberate press.\n */\nexport function isSuggestionOpen(): boolean {\n return openCount > 0\n}\n\n/** How a list looks and what fills it \u2014 everything but the editor's own wiring. */\nexport interface SuggestionPopupOptions<T> {\n /** Names the list \u2014 \"Commands\", \"People\". */\n ariaLabel: string\n /** The box's width, e.g. `w-[300px]`. */\n width: string\n /** The cap before the rows scroll, e.g. `max-h-[400px]`. */\n maxHeight: string\n /** The plugin's items, and the typed query, \u2192 the sections to draw. One unlabeled section by default. */\n sections?: (items: T[], query: string) => readonly SuggestionMenuSection<T>[]\n itemKey: (item: T) => string\n row: (item: T) => SuggestionMenuRow\n /**\n * With `refresh`: called with a listener while the list is open; the app\n * calls it when its data has changed (a search landing), and the open list is\n * drawn again for the same query. Returns the unsubscribe, run when the list\n * closes however it closes.\n */\n subscribe?: (listener: () => void) => () => void\n /** The items for a query, as the plugin's `items` answers \u2014 asked again when `subscribe` fires. */\n refresh?: (query: string) => T[]\n}\n\ntype Props<T> = SuggestionProps<T> & { options: SuggestionPopupOptions<T> }\n\n// `forwardRef`, so Tiptap's `ReactRenderer` hands its `ref` through whichever\n// way it detects a component that takes one.\nconst Popup = forwardRef<SuggestionMenuHandle, Props<unknown>>(function Popup({ options, ...props }, handle) {\n const menu = useRef<SuggestionMenuHandle>(null)\n useImperativeHandle(handle, () => ({ onKeyDown: (event) => menu.current?.onKeyDown(event) ?? false }))\n const sections = options.sections ? options.sections(props.items, props.query) : [{ items: props.items }]\n return (\n <SuggestionMenu\n handle={menu}\n rect={props.clientRect?.() ?? null}\n editorElement={props.editor.view.dom}\n ariaLabel={options.ariaLabel}\n width={options.width}\n maxHeight={options.maxHeight}\n sections={sections}\n itemKey={options.itemKey}\n row={options.row}\n onSelect={(item) => props.command(item)}\n />\n )\n})\n\n/**\n * The `render` of a Tiptap suggestion plugin, drawn as a `SuggestionMenu` \u2014\n * the popup controller Peek wrote four times (`mention.tsx`, `slashCommands.tsx`),\n * once.\n *\n * Suggestion({ editor, char: '/', items, command,\n * render: suggestionPopup({ ariaLabel: 'Commands', width: 'w-[300px]',\n * maxHeight: 'max-h-[400px]', itemKey, row, sections }) })\n *\n * It mounts the list when the query starts and removes it when it ends: the\n * plugin owns the list's life. Escape closes it and is used up; \u2191, \u2193 and Enter\n * go to the list; every other key stays the editor's. The list portals itself\n * (the package `Popover`), so the mount point is only a mount point.\n */\nexport function suggestionPopup<T>(options: SuggestionPopupOptions<T>): NonNullable<SuggestionOptions<T>['render']> {\n return () => {\n let component: ReactRenderer<SuggestionMenuHandle, Props<T>> | null = null\n let container: HTMLDivElement | null = null\n let last: SuggestionProps<T> | null = null\n let unsubscribe: (() => void) | undefined\n let counted = false\n let unlisten: (() => void) | undefined\n const count = (shown: boolean) => {\n if (shown === counted) return\n counted = shown\n openCount = shown ? openCount + 1 : Math.max(0, openCount - 1)\n }\n // The plugin's state outlives a blur \u2014 no transaction runs \u2014 so leaving the\n // box ends the query here, as Escape does: coming back finds no list, and\n // Enter sends (Miky, 2026-10-06). Leaving the window is not leaving the box,\n // and a press in the list's own panel never takes focus (SuggestionMenu).\n const listen = (props: SuggestionProps<T>) => {\n const { view } = props.editor\n const dom = view.dom\n const onBlur = () => {\n // A window that loses focus leaves its active element where it was.\n if (dom.contains(document.activeElement) || !last) return\n const from = last.range.from\n // Not a pick: no grace for the other box's Enter.\n count(false)\n for (const plugin of view.state.plugins) {\n const state = plugin.getState(view.state) as { active?: boolean; range?: { from: number } } | undefined\n if (state?.active && state.range?.from === from && plugin.spec.key) exitSuggestion(view, plugin.spec.key)\n }\n }\n dom.addEventListener('blur', onBlur)\n unlisten = () => dom.removeEventListener('blur', onBlur)\n }\n const exit = () => {\n unsubscribe?.()\n unsubscribe = undefined\n unlisten?.()\n unlisten = undefined\n last = null\n // The grace is for the Enter that picked a row and closed the list, not\n // for a keystroke that only left it with no rows.\n if (counted) lastClose = Date.now()\n count(false)\n if (!component) return\n component.destroy()\n container?.remove()\n component = null\n container = null\n }\n return {\n onStart: (props) => {\n count(props.items.length > 0)\n component = new ReactRenderer<SuggestionMenuHandle, Props<T>>(Popup as never, { props: { ...props, options }, editor: props.editor })\n container = document.createElement('div')\n document.body.appendChild(container)\n container.appendChild(component.element)\n last = props\n listen(props)\n const { subscribe, refresh } = options\n if (subscribe && refresh) {\n // The plugin asks for items only when the query changes; an answer\n // landing later draws the open list again with the same props.\n unsubscribe = subscribe(() => {\n if (!last || !component) return\n last = { ...last, items: refresh(last.query) }\n count(last.items.length > 0)\n component.updateProps({ ...last, options })\n })\n }\n },\n onUpdate: (props) => {\n last = props\n count(props.items.length > 0)\n component?.updateProps({ ...props, options })\n },\n onKeyDown: ({ event }: SuggestionKeyDownProps) => {\n if (event.key === 'Escape') {\n exit()\n return true\n }\n return component?.ref?.onKeyDown(event) ?? false\n },\n onExit: exit,\n }\n }\n}\n", "import { useMemo, type ReactNode } from 'react'\nimport type { Editor } from '@tiptap/core'\nimport type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport type { EditorView } from '@tiptap/pm/view'\nimport { IconCopy, IconTrash } from '@tabler/icons-react'\nimport { BlockHandle, blockAtY, type BlockHandleEditing, type HandleBlock } from './BlockHandle'\nimport { MenuItem, MenuSub } from './Menu'\n\n/** A row of the block menu's Turn into: what the block becomes. */\nexport interface BlockTurnInto {\n label: string\n /** Turns the block the caret is in. The handle puts the caret there first. */\n run: (editor: Editor) => void\n /** Whether the block already is this \u2014 drawn as the selected row. */\n active?: (node: ProseMirrorNode) => boolean\n}\n\nexport interface EditorBlockHandleProps {\n /** The editor. Not editable, or not there yet, and the handle reads: the caller's rows only. */\n editor: Editor | null\n /** Turn into's rows. Offered for a block of text \u2014 a paragraph, a heading, code. */\n turnInto?: readonly BlockTurnInto[]\n /** The caller's own rows, after the editing ones. */\n actions?: (block: HandleBlock) => ReactNode\n /** The `EditorContent`. */\n children: ReactNode\n className?: string\n}\n\n/** The editor's top-level blocks, where each starts and what it is. */\nfunction editorBlocks(view: EditorView): HandleBlock[] {\n const out: HandleBlock[] = []\n view.state.doc.forEach((node, pos) => {\n const element = view.nodeDOM(pos)\n if (element instanceof HTMLElement) out.push({ id: (node.attrs.blockId as string | null) || undefined, element, pos })\n })\n return out\n}\n\n/*\n Duplicate and Delete leave the editor focused, as Turn into and a drop do: the\n person is still in the field, so an editor that saves when they leave it saves\n these too \u2014 and one that loads someone else's edit while unfocused does not\n load it over them.\n*/\n\n/** A copy of the block straight after it, the caret in the copy. `BlockId` gives the copy a fresh id; the original keeps its own. */\nexport function duplicateBlock(editor: Editor, pos: number): boolean {\n const node = editor.state.doc.nodeAt(pos)\n if (!node) return false\n editor.view.dispatch(editor.state.tr.insert(pos + node.nodeSize, node).scrollIntoView())\n editor.commands.focus(pos + node.nodeSize + 1)\n return true\n}\n\n/** Removes the block, the caret where it was. The last block of a document becomes an empty paragraph, since a document holds at least one. */\nexport function deleteBlock(editor: Editor, pos: number): boolean {\n const { state } = editor\n const node = state.doc.nodeAt(pos)\n if (!node) return false\n const tr = state.doc.childCount === 1 ? state.tr.replaceWith(pos, pos + node.nodeSize, state.schema.nodes.paragraph.create()) : state.tr.delete(pos, pos + node.nodeSize)\n editor.view.dispatch(tr)\n editor.commands.focus(Math.min(pos + 1, editor.state.doc.content.size))\n return true\n}\n\n/**\n * Moves the block at `from` to `to` (a position between top-level blocks), the\n * caret in it: what a drop in the gutter does, where the editor has no drop of\n * its own. The node moves whole, attributes and all, so it keeps its id, as\n * ProseMirror's drop keeps it.\n */\nexport function moveBlock(editor: Editor, from: number, to: number): boolean {\n const node = editor.state.doc.nodeAt(from)\n if (!node || (to >= from && to <= from + node.nodeSize)) return false\n const tr = editor.state.tr.delete(from, from + node.nodeSize)\n const at = tr.mapping.map(to)\n editor.view.dispatch(tr.insert(at, node).scrollIntoView())\n editor.commands.focus(at + 1)\n return true\n}\n\n/**\n * Starts the browser's own drag of the block, as ProseMirror starts one of a\n * selected node: the block selected, its slice on `view.dragging` with `move`,\n * and its HTML on the transfer. ProseMirror's drop then deletes the selection\n * and inserts the slice with its attributes \u2014 so the block arrives with the\n * id it left with, and every comment anchored to it still finds it.\n */\nexport function startBlockDrag(editor: Editor, pos: number, event: Pick<globalThis.DragEvent, 'dataTransfer'>, image?: Element) {\n editor.commands.setNodeSelection(pos)\n const { view } = editor\n const slice = view.state.selection.content()\n const { dom, text } = view.serializeForClipboard(slice)\n const transfer = event.dataTransfer\n if (transfer) {\n transfer.clearData()\n transfer.setData('text/html', dom.innerHTML)\n transfer.setData('text/plain', text)\n transfer.effectAllowed = 'copyMove'\n if (image) transfer.setDragImage(image, 0, 0)\n }\n view.dragging = { slice, move: true }\n}\n\n/**\n * The drop line a dragged block shows, in the package's accent instead of the\n * editor's default (the text's colour). Give it to the editor's StarterKit:\n * `StarterKit.configure({ dropcursor: blockDropCursor })`.\n */\nexport const blockDropCursor = { color: false, class: 'bg-accent-primary' } as const\n\nconst NOTHING_YET: BlockHandleEditing = { find: () => null, rows: () => null, startDrag: () => {}, drop: () => {}, endDrag: () => {}, onChange: () => () => {} }\n\n/**\n * `BlockHandle` in an editor: drag the handle to move the block, and the menu\n * gains Turn into, Duplicate and Delete before the caller's rows.\n */\nexport function EditorBlockHandle({ editor, turnInto = [], actions, children, className }: EditorBlockHandleProps) {\n const editing = useMemo<BlockHandleEditing>(() => {\n // Before the editor exists there is no block to find \u2014 not the editor's whole element read as one.\n if (!editor) return NOTHING_YET\n // Where the block being dragged starts.\n let dragged: number | undefined\n return {\n find: (y) => blockAtY(editorBlocks(editor.view), y),\n rows: (block) => {\n const pos = block.pos\n // Not editable: the caller's rows only, and no drag.\n if (!editor.isEditable) return null\n const node = pos === undefined ? null : editor.state.doc.nodeAt(pos)\n if (pos === undefined || !node) return null\n return (\n <>\n {node.isTextblock && turnInto.length > 0 && (\n <MenuSub label=\"Turn into\">\n {turnInto.map((row) => (\n <MenuItem\n key={row.label}\n label={row.label}\n selected={row.active?.(node) ?? false}\n onClick={() => {\n // Inside the block's text, so the row's command turns this block and no other.\n editor.chain().focus().setTextSelection(pos + 1).run()\n row.run(editor)\n }}\n />\n ))}\n </MenuSub>\n )}\n <MenuItem label=\"Duplicate\" leading={<IconCopy size={16} stroke={1.5} className=\"text-text-secondary\" />} onClick={() => duplicateBlock(editor, pos)} />\n <MenuItem label=\"Delete\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={() => deleteBlock(editor, pos)} />\n </>\n )\n },\n startDrag: (block, event) => {\n dragged = block.pos\n if (block.pos !== undefined) startBlockDrag(editor, block.pos, event, block.element)\n },\n drop: (target, after) => {\n const node = target.pos === undefined ? null : editor.state.doc.nodeAt(target.pos)\n if (dragged === undefined || target.pos === undefined || !node) return\n moveBlock(editor, dragged, after ? target.pos + node.nodeSize : target.pos)\n },\n // ProseMirror clears `dragging` on its own dragend, which a drag started outside its element never reaches.\n endDrag: () => setTimeout(() => (editor.view.dragging = null), 50),\n onChange: (changed) => {\n editor.on('update', changed)\n return () => {\n editor.off('update', changed)\n }\n },\n }\n }, [editor, turnInto])\n\n return (\n <BlockHandle editing={editing} actions={actions} className={className}>\n {children}\n </BlockHandle>\n )\n}\n", "import { useCallback, useEffect, useRef, useState, type MouseEvent, type ReactNode } from 'react'\nimport type { Editor } from '@tiptap/core'\nimport { TextSelection, type Command } from '@tiptap/pm/state'\nimport { addColumnAfter, addColumnBefore, addRowAfter, addRowBefore, deleteColumn, deleteRow, deleteTable, toggleHeader } from '@tiptap/pm/tables'\nimport { IconColumnInsertLeft, IconColumnInsertRight, IconGripHorizontal, IconGripVertical, IconPlus, IconRowInsertBottom, IconRowInsertTop, IconTrash } from '@tabler/icons-react'\nimport { cn } from './cn'\nimport { IconButton } from './IconButton'\nimport { FLOATING_SURFACE_CLASSES } from './looks'\nimport { Menu, MenuItem, MenuSeparator } from './Menu'\n\nexport interface EditorTableControlsProps {\n /** The editor, with TableKit (`@tiptap/extension-table`) in its extensions. Not editable, or not there yet: no controls. */\n editor: Editor | null\n /** The `EditorContent`. */\n children: ReactNode\n className?: string\n}\n\n/** The cell the pointer is on, and the table and row it is in. */\ninterface Hovered {\n table: HTMLTableElement\n row: HTMLTableRowElement\n cell: HTMLTableCellElement\n}\n\n/** Where the controls go, measured from the frame: they depend on the document, not on a size of ours. */\ninterface Placed {\n tableBox: { top: number; left: number; width: number; height: number }\n rowBox: { top: number; height: number }\n columnBox: { left: number; width: number }\n}\n\n/*\n Every row runs the table command with the caret in a cell, as the command\n expects \u2014 the hovered cell's, or for a \u2795 bar the last row's or the last\n column's \u2014 and leaves the editor focused there, as the block menu does: the\n person is still in the field, so an editor that saves when they leave it\n saves this too.\n*/\n\n/** Puts the caret in `cell` and runs `command` there. */\nfunction inCell(editor: Editor, cell: HTMLTableCellElement, command: Command): boolean {\n const { view } = editor\n if (!view.dom.contains(cell)) return false\n const at = view.posAtDOM(cell, 0)\n view.dispatch(view.state.tr.setSelection(TextSelection.near(view.state.doc.resolve(at))))\n const done = command(view.state, view.dispatch, view)\n editor.commands.focus()\n return done\n}\n\n/** A row of header cells only: the table's header row. */\nfunction isHeaderRow(row: HTMLTableRowElement): boolean {\n return row.cells.length > 0 && Array.from(row.cells).every((c) => c.tagName === 'TH')\n}\n\n/**\n * The cell's row, or the whole table when it is its only one: a table holds at\n * least one row (prosemirror-tables refuses to delete it). Deleting the header\n * row makes the next row the header, so the table still opens with one.\n */\nexport function deleteTableRow(editor: Editor, cell: HTMLTableCellElement): boolean {\n const row = cell.closest('tr')\n const table = cell.closest('table')\n if (!row || !table || table.rows.length <= 1) return inCell(editor, cell, deleteTable)\n const header = isHeaderRow(row)\n return inCell(editor, cell, header ? (state, dispatch, view) => deleteRow(state, dispatch) && (!view || toggleHeader('row')(view.state, view.dispatch)) : deleteRow)\n}\n\n/** The same for a column: the last one deletes the table. */\nexport function deleteTableColumn(editor: Editor, cell: HTMLTableCellElement): boolean {\n const row = cell.closest('tr')\n return inCell(editor, cell, row && row.cells.length > 1 ? deleteColumn : deleteTable)\n}\n\n/** A row after the last one: the \u2795 under the table. */\nexport function appendTableRow(editor: Editor, table: HTMLTableElement): boolean {\n const last = table.rows[table.rows.length - 1]?.cells[0]\n return !!last && inCell(editor, last, addRowAfter)\n}\n\n/** A column after the last one: the \u2795 right of the table. */\nexport function appendTableColumn(editor: Editor, table: HTMLTableElement): boolean {\n const first = table.rows[0]\n const last = first?.cells[first.cells.length - 1]\n return !!last && inCell(editor, last, addColumnAfter)\n}\n\nfunction place(frame: HTMLElement, { table, row, cell }: Hovered): Placed {\n const at = frame.getBoundingClientRect()\n const t = table.getBoundingClientRect()\n const r = row.getBoundingClientRect()\n const c = cell.getBoundingClientRect()\n return {\n tableBox: { top: t.top - at.top, left: t.left - at.left, width: t.width, height: t.height },\n rowBox: { top: r.top - at.top, height: r.height },\n columnBox: { left: c.left - at.left, width: c.width },\n }\n}\n\nconst ICON = { size: 16, stroke: 1.5, className: 'text-text-secondary' } as const\n\n/**\n * A \u2795 press keeps the focus where it is, as the selection toolbar's buttons do:\n * the person never left the field, so an editor that saves when they leave it\n * does not save half-way, and History gets one line for the edit, not one per\n * press. (A handle's menu takes the focus, as every menu does.)\n */\nconst keepFocus = (e: MouseEvent) => e.preventDefault()\n\n/** What a handle and a \u2795 bar sit in: the elevated surface's border and fill, opaque over the table's lines, with no shadow at this size. */\nconst HANDLE_BOX = cn(FLOATING_SURFACE_CLASSES, 'shadow-none')\n\n/**\n * A table's own controls in an editor, as in Notion: point at a cell and a\n * handle sits on its row's left edge and on its column's top edge; press one\n * for Insert above and below (left and right), Delete, and Delete table. On\n * the last row a \u2795 bar under the table adds a row, and on the last column one\n * right of it adds a column. Tab in\n * the last cell adds a row too \u2014 that is TableKit's own.\n *\n * Deleting a table's only row or only column deletes the table, which cannot\n * be left with none.\n *\n * Built on the editor library's own table commands; the controls are ours.\n * Wrap the `EditorContent` in it, inside an `EditorBlockHandle` if there is\n * one: the block handle stays the table's, in the gutter.\n */\nexport function EditorTableControls({ editor, children, className }: EditorTableControlsProps) {\n const frame = useRef<HTMLDivElement>(null)\n // The cell pointed at and where its controls go, as one: the boxes mean nothing without their elements.\n const [hovered, setHovered] = useState<(Hovered & Placed) | null>(null)\n // A menu that is up keeps its row and column, wherever the pointer goes.\n const [menu, setMenu] = useState<'row' | 'column' | null>(null)\n const editable = !!editor && editor.isEditable\n\n const follow = useCallback(\n (target: EventTarget | null) => {\n if (!editor || !frame.current || !(target instanceof Element)) return\n // On a control of ours: keep what it was drawn for.\n if (target.closest('[data-table-control]')) return\n const cell = target.closest<HTMLTableCellElement>('td, th')\n const row = cell?.closest('tr')\n const table = row?.closest('table')\n if (!cell || !row || !table || !editor.view.dom.contains(table)) {\n setHovered(null)\n return\n }\n // Measured on every move, the same cell too: a panel resized or a cell's text wrapping moves the edges under a still pointer.\n const next = { table, row, cell, ...place(frame.current, { table, row, cell }) }\n setHovered((was) => (was && was.cell === cell && JSON.stringify([was.tableBox, was.rowBox, was.columnBox]) === JSON.stringify([next.tableBox, next.rowBox, next.columnBox]) ? was : next))\n },\n [editor],\n )\n\n /*\n Any change to the document moves everything under the controls, so they go\n until the pointer moves again: a row added or deleted here, and someone\n else's edit loaded into the field \u2014 which an app does without an `update`\n (`emitUpdate: false`), and which redraws the cells the controls point at.\n */\n useEffect(() => {\n if (!editor) return\n const changed = ({ transaction }: { transaction: { docChanged: boolean } }) => {\n if (transaction.docChanged && !menu) setHovered(null)\n }\n editor.on('transaction', changed)\n return () => {\n editor.off('transaction', changed)\n }\n }, [editor, menu])\n\n const shown = editable ? hovered : null\n const run = (act: () => boolean) => () => {\n if (editor) act()\n setMenu(null)\n setHovered(null)\n }\n\n return (\n <div\n ref={frame}\n className={cn('relative', className)}\n onMouseMove={(e) => {\n if (!menu) follow(e.target)\n }}\n onMouseLeave={() => {\n if (!menu) setHovered(null)\n }}\n >\n {children}\n {shown && editor && (\n <>\n {/* The row's handle, centred on the table's left edge: the box is ours, so the table's line does not show through it. */}\n <div data-table-control className={cn(HANDLE_BOX, 'absolute -translate-x-1/2 -translate-y-1/2')} style={{ left: shown.tableBox.left, top: shown.rowBox.top + shown.rowBox.height / 2 }}>\n <Menu\n open={menu === 'row'}\n onOpenChange={(open) => setMenu(open ? 'row' : null)}\n trigger={\n <IconButton aria-label=\"Row menu\" tooltip=\"Row\" className=\"h-6 w-4 p-0\">\n <IconGripVertical size={16} stroke={1.5} />\n </IconButton>\n }\n >\n {/* Nothing goes above a header row: it is the table's first row, and a header anywhere else is no header (RichText, the agent's text). */}\n {!isHeaderRow(shown.row) && <MenuItem label=\"Insert above\" leading={<IconRowInsertTop {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addRowBefore))} />}\n <MenuItem label=\"Insert below\" leading={<IconRowInsertBottom {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addRowAfter))} />\n <MenuSeparator />\n <MenuItem label=\"Delete\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => deleteTableRow(editor, shown.cell))} />\n <MenuItem label=\"Delete table\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => inCell(editor, shown.cell, deleteTable))} />\n </Menu>\n </div>\n {/* The column's handle, centred on the table's top edge. */}\n <div data-table-control className={cn(HANDLE_BOX, 'absolute -translate-x-1/2 -translate-y-1/2')} style={{ left: shown.columnBox.left + shown.columnBox.width / 2, top: shown.tableBox.top }}>\n <Menu\n open={menu === 'column'}\n onOpenChange={(open) => setMenu(open ? 'column' : null)}\n trigger={\n <IconButton aria-label=\"Column menu\" tooltip=\"Column\" className=\"h-4 w-6 p-0\">\n <IconGripHorizontal size={16} stroke={1.5} />\n </IconButton>\n }\n >\n <MenuItem label=\"Insert left\" leading={<IconColumnInsertLeft {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addColumnBefore))} />\n <MenuItem label=\"Insert right\" leading={<IconColumnInsertRight {...ICON} />} onClick={run(() => inCell(editor, shown.cell, addColumnAfter))} />\n <MenuSeparator />\n <MenuItem label=\"Delete\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => deleteTableColumn(editor, shown.cell))} />\n <MenuItem label=\"Delete table\" destructive leading={<IconTrash size={16} stroke={1.5} className=\"text-error-default\" />} onClick={run(() => inCell(editor, shown.cell, deleteTable))} />\n </Menu>\n </div>\n {/*\n The \u2795 bars, flush with the table so the pointer reaches them from a\n cell without leaving it \u2014 the row's from the last row, the column's\n from the last column. Anywhere else the one under the table would\n lie over the top of the next block for nothing.\n */}\n {shown.row === shown.table.rows[shown.table.rows.length - 1] && (\n <div data-table-control className={cn(HANDLE_BOX, 'absolute flex h-4')} style={{ left: shown.tableBox.left, top: shown.tableBox.top + shown.tableBox.height, width: shown.tableBox.width }}>\n <IconButton aria-label=\"Add row\" tooltip=\"Add row\" className=\"h-full w-full p-0\" onMouseDown={keepFocus} onClick={run(() => appendTableRow(editor, shown.table))}>\n <IconPlus size={12} stroke={1.5} />\n </IconButton>\n </div>\n )}\n {shown.cell === shown.row.cells[shown.row.cells.length - 1] && (\n <div data-table-control className={cn(HANDLE_BOX, 'absolute flex w-4')} style={{ left: shown.tableBox.left + shown.tableBox.width, top: shown.tableBox.top, height: shown.tableBox.height }}>\n <IconButton aria-label=\"Add column\" tooltip=\"Add column\" className=\"h-full w-full p-0\" onMouseDown={keepFocus} onClick={run(() => appendTableColumn(editor, shown.table))}>\n <IconPlus size={12} stroke={1.5} />\n </IconButton>\n </div>\n )}\n </>\n )}\n </div>\n )\n}\n", "/**\n * Looks two or more parts share, written once (UIG-25).\n *\n * A look typed again by hand draws the same today and drifts the day one of\n * them changes: nothing fails, one screen just looks slightly off. So a look\n * more than one part wears lives here, and each part takes it from here. The\n * lint that finds a copied look reads a class list wherever it is written; a\n * constant of classes ends in `_CLASSES`, so it is read as one.\n *\n * Only looks \u2014 colour, text, border, corner, shadow. Where a part sits and how\n * much room it takes stay in the part.\n */\n\n/**\n * A field's box: TextInput and Textarea. The hairline strengthens on hover \u2014 of the box, or of anywhere in the `Field` around it, label included, so the field is one thing under the pointer (Katerina, 28 September) \u2014 and not while switched off: a label passes its hover to its box, switched off or not (FIELD_DISABLED_CLASSES holds it back). Focus outranks it, so a focused field keeps its colour under the pointer.\n */\nexport const FIELD_BOX_CLASSES = 'bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong focus:border-border-focus group-hover/field:focus:border-border-focus rounded-lg'\n\n/** What a field's words look like, typed or waiting: TextInput, Textarea and Select. */\nexport const FIELD_TEXT_CLASSES = 'text-text-primary placeholder:text-text-muted'\n\n/** A field that cannot be used: TextInput, Textarea and Select. */\nexport const FIELD_DISABLED_CLASSES = 'disabled:pointer-events-none disabled:bg-bg-disabled disabled:text-text-disabled disabled:border-border-field group-hover/field:disabled:border-border-field'\n\n/** The ring a focused field wears in Signal: TextInput and Textarea. */\nexport const FIELD_FOCUS_RING_CLASSES = 'signal:transition-shadow signal:focus:shadow-focus-ring'\n\n/** A field's two sizes, its padding and its words: TextInput, Textarea (default only) and Select. The small one is the small Select's trigger. */\nexport const FIELD_SIZE_CLASSES = {\n default: 'px-3 py-2 text-input-value',\n small: 'h-6 min-h-6 px-2 text-caption',\n} as const\n\n/** The box around an input that sits inside it, before its focus look: SearchInput and ChipInput. */\nexport const FIELD_SHELL_CLASSES = 'bg-bg-field border border-border-field shadow-field hover:border-border-strong group-hover/field:border-border-strong rounded-lg transition-colors'\n\n/** A panel that floats over the page: Menu's panel and Tooltip. */\nexport const FLOATING_SURFACE_CLASSES = 'rounded-lg border border-border-default bg-bg-elevated shadow-lg'\n\n/** A count beside a label, in the muted colour: NavItem's number and SectionHeader's `count`. */\nexport const COUNT_TEXT_CLASSES = 'font-mono text-caption tabular-nums text-text-muted'\n\n/** A chip's words: Chip's label and Reaction's count. */\nexport const CHIP_TEXT_CLASSES = 'text-chip signal:font-mono signal:text-small signal:font-semibold signal:tabular-nums'\n\n/** Small secondary words that turn mono under signal: Kbd's key, CommandPalette's footer and MembersPill's count. */\nexport const MONO_CAPTION_CLASSES = 'text-caption text-text-secondary signal:font-mono signal:text-small'\n\n/** Words typed straight into an input with no box of its own: SearchInput and ChipInput. */\nexport const BARE_INPUT_CLASSES = 'bg-transparent text-text-primary placeholder:text-text-muted outline-none'\n\n/** A sidebar row's shape and its words at rest, brightening under the pointer: NavItem, and SectionHeader's `row` (a folder among rows). */\n// 8px corners, a MenuItem row's (Katerina, 2 October).\nexport const SIDEBAR_ROW_CLASSES = 'rounded-lg transition-colors'\nexport const SIDEBAR_ROW_TEXT_CLASSES = 'text-text-secondary hover:text-text-primary'\n\n/** A quiet icon control: secondary, brightening on a hover fill \u2014 IconButton's `muted`. */\nexport const MUTED_CONTROL_CLASSES = 'text-text-secondary hover:bg-bg-hover hover:text-text-primary'\n\n/** A control that opens a menu: a hover fill, held while its menu is up \u2014 PersonTrigger's row, and a ContainerHeader title with a menu. */\nexport const MENU_TRIGGER_CLASSES = 'cursor-pointer rounded-md transition-colors hover:bg-bg-hover data-[popup-open]:bg-bg-hover'\n\n/**\n * A panel that slides open and shut on Base UI's Collapsible: Base UI measures\n * it and writes its height to a variable, `auto` again once the slide ends, and\n * the panel is 0 high on its opening and closing frames. None under\n * prefers-reduced-motion. CollapsibleSection's rows, and the rows under a NavItem.\n */\nexport const COLLAPSIBLE_PANEL_CLASSES = 'h-[var(--collapsible-panel-height)] overflow-hidden transition-[height] duration-150 ease-out motion-reduce:transition-none data-[starting-style]:h-0 data-[ending-style]:h-0'\n", "import type { ReactNode } from 'react'\nimport { Extension, type Editor } from '@tiptap/core'\nimport { PluginKey } from '@tiptap/pm/state'\nimport Mention, { type MentionOptions } from '@tiptap/extension-mention'\nimport Suggestion from '@tiptap/suggestion'\nimport { NodeViewWrapper, ReactNodeViewRenderer, type ReactNodeViewProps } from '@tiptap/react'\nimport { Avatar } from './Avatar'\nimport { EnterHint } from './Menu'\nimport { inlineChipClassName } from './InlineChip'\nimport type { SuggestionMenuRow } from './SuggestionMenu'\nimport { suggestionPopup, type SuggestionPopupOptions } from './suggestionPopup'\n\n/*\n * The four composer triggers every Estiva app shares \u2014 `@`, `!@`, `[` and `/`\n * (CON-26, Miky 2026-10-02). Peek built them first and they move here unchanged\n * (CON-27); an app passes in only its own data: its people, the things `[` can\n * reference, and the `/` rows it can perform beside the shared Format section.\n *\n * **What a pick writes is not here.** The text a chip becomes in the body and\n * the tags it earns are `@estiva-app/conversation`'s (`mentionText`,\n * `messageReference`, `urgentTagsFor`), so a client without React writes the\n * same bytes. A chip here carries what that needs: a person's `pubkey`, a\n * reference's `uri`.\n */\n\n// \u2500\u2500\u2500 @ and !@ \u2500\u2500\u2500\n\n/** A person `@` and `!@` offer. */\nexport interface MentionPerson {\n /** The app's own key for the person \u2014 the row's key and the chip's `id`. */\n id: string\n name: string\n /** The second line of the row: a role, a team. */\n description?: string\n /** Their Nostr key, when they have one \u2014 what the body is written with (SPEC \u00A713.1). */\n pubkey?: string | null\n /** The face at the row's start. Default: the package `Avatar` for the name. */\n avatar?: ReactNode\n}\n\n/** How many people the list shows. */\nexport const PEOPLE_LIMIT = 6\n\n/** The people whose name holds the query, the first {@link PEOPLE_LIMIT} of them. */\nexport function filterPeople(people: readonly MentionPerson[], query: string): MentionPerson[] {\n const q = query.toLowerCase()\n return people.filter((person) => q === '' || person.name.toLowerCase().includes(q)).slice(0, PEOPLE_LIMIT)\n}\n\n/**\n * The people list `@` and `!@` open: the face, the name, the second line, and\n * the Enter hint while highlighted \u2014 `MenuItem` at its tall size.\n */\nexport function peopleMenu(urgent: boolean): SuggestionPopupOptions<MentionPerson> {\n const section = urgent ? 'Urgent mention' : 'People'\n return {\n ariaLabel: section,\n // The people and the reference lists are the same box: 658px, capped at 360.\n width: 'w-[658px]',\n maxHeight: 'max-h-[360px]',\n sections: (people) => [{ label: section, items: people, className: 'px-3 py-1' }],\n itemKey: (person) => person.id,\n row: (person) => ({\n label: person.name,\n description: person.description,\n leading: person.avatar ?? <Avatar size={32} alt={person.name} />,\n hint: <EnterHint />,\n size: 'tall',\n }),\n }\n}\n\nexport interface PersonMentionOptions {\n /** Everyone the list can offer, asked each time the query changes. */\n people: () => readonly MentionPerson[]\n}\n\ntype Doc = { descendants: (cb: (node: { type: { name: string }; attrs: Record<string, unknown> }) => boolean | void) => void }\n\nfunction optionsOf<T>(editor: Editor, name: string): T | undefined {\n return editor.extensionManager.extensions.find((one) => one.name === name)?.options as T | undefined\n}\n\n/**\n * A list keeps listening across spaces, so a name or a title can be more than\n * one word (f586437a, Miky 2026-10-06). It stops at a new line, Escape, a pick\n * or the cursor leaving \u2014 Suggestion's own \u2014 and once the query holds `stop`:\n * `]` for `[`, so \"see [the notes] above\" ends as text. While nothing matches,\n * the list is hidden rather than closed, and comes back if the words match again.\n * A space straight after the key is prose (\"meet @ 5pm\", \"- [ ] task\"), not a\n * query: every two-word name holds a space.\n */\nconst listening = (stop: string) => ({\n allowSpaces: true,\n shouldShow: ({ query }: { query: string }) => !/^\\s/.test(query) && !query.includes(stop),\n})\n\n/** `@`'s list and pick, or `!@`'s, for the node `name` \u2014 the extension's own, so a renamed one still finds its people. */\nfunction personSuggestion(urgent: boolean, name: string) {\n return {\n ...(urgent ? { char: '!@' } : {}),\n // A second `@` starts another mention; the first one has ended.\n ...listening('@'),\n items: ({ query, editor }: { query: string; editor: Editor }) => filterPeople(optionsOf<PersonMentionOptions>(editor, name)?.people() ?? [], query),\n render: suggestionPopup(peopleMenu(urgent)),\n command: ({ editor, range, props: person }: { editor: Editor; range: { from: number; to: number }; props: MentionPerson }) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n { type: name, attrs: { id: person.id, label: person.name, pubkey: person.pubkey ?? null } },\n { type: 'text', text: ' ' },\n ])\n .run()\n },\n }\n}\n\n/*\n What a chip keeps through HTML \u2014 a copy and paste, inside an editor or\n between two. The chip's own `renderHTML` writes each as a `data-*` attribute\n and these read them back. Until CON-27 none was written, so a pasted chip\n came back with every value null and was sent as `@null`. What is read is\n checked: a crafted `data-pubkey` that is not a key, or a `data-uri` that is\n not a `nostr:` reference, is dropped rather than written into the body.\n*/\nconst HEX_KEY = /^[0-9a-f]{64}$/\nconst NOSTR_URI = /^nostr:[a-z0-9]+$/i\nconst fromData = (name: string, valid?: RegExp) => ({\n parseHTML: (element: HTMLElement) => {\n const value = element.getAttribute(`data-${name}`)\n return value !== null && (!valid || valid.test(value)) ? value : null\n },\n // The node's own `renderHTML` writes it.\n renderHTML: () => ({}),\n})\n\nconst personAttributes = () => ({\n id: { default: null, ...fromData('id') },\n label: { default: null, ...fromData('label') },\n /**\n * The person's Nostr key, when they have one \u2014 SPEC \u00A713.1. `id` is the app's\n * own and means nothing to another app; this is what the body is written\n * with, so a mention survives a rename.\n */\n pubkey: { default: null, ...fromData('pubkey', HEX_KEY) },\n})\n\n/** The `data-*` a person chip is written with, so a paste reads it back. */\nfunction personData(attrs: Record<string, unknown>): Record<string, string> {\n return {\n 'data-id': String(attrs.id ?? ''),\n 'data-label': String(attrs.label ?? ''),\n ...(typeof attrs.pubkey === 'string' && attrs.pubkey ? { 'data-pubkey': attrs.pubkey } : {}),\n }\n}\n\n/**\n * `@` \u2014 a person, as a chip that writes their key (SPEC \u00A713.1).\n *\n * PersonMention.configure({ people: () => directory })\n *\n * Its `pubkey` and `label` are what `mentionText` takes.\n */\nexport const PersonMention = Mention.extend<MentionOptions<MentionPerson, MentionPerson> & PersonMentionOptions>({\n name: 'mention',\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: {}, people: () => [], suggestion: personSuggestion(false, this.name) }\n },\n addAttributes: personAttributes,\n renderHTML({ node }) {\n // No node view, so this spec is what the editor draws: the one chip.\n return ['span', { 'data-mention': 'true', ...personData(node.attrs), class: inlineChipClassName('person', 'cursor-default') }, `@${node.attrs.label}`]\n },\n parseHTML() {\n return [{ tag: 'span[data-mention]' }]\n },\n})\n\n/**\n * `!@` \u2014 a person, urgently: the same chip in the urgent tone. Its text is an\n * ordinary mention; the urgency travels as a tag (`urgentTagsFor`), so hand the\n * write {@link urgentPubkeys}.\n */\nexport const UrgentPersonMention = Mention.extend<MentionOptions<MentionPerson, MentionPerson> & PersonMentionOptions>({\n name: 'urgentMention',\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: {}, people: () => [], suggestion: personSuggestion(true, this.name) }\n },\n addAttributes: personAttributes,\n renderHTML({ node }) {\n return [\n 'span',\n { 'data-urgent-mention': 'true', ...personData(node.attrs), class: inlineChipClassName('urgent', 'cursor-default') },\n `@${node.attrs.label}`,\n ]\n },\n parseHTML() {\n return [{ tag: 'span[data-urgent-mention]' }]\n },\n})\n\n/**\n * The keys the document's urgent mentions name \u2014 what the write turns into\n * `[\"urgent\", <pubkey>]` tags. A chip with no key has no tag to carry; its text\n * is the old `!@Name`.\n */\nexport function urgentPubkeys(editor: { state: { doc: Doc } }): string[] | undefined {\n const urgent: string[] = []\n editor.state.doc.descendants((node) => {\n if (node.type.name === 'urgentMention' && typeof node.attrs.pubkey === 'string' && node.attrs.pubkey) urgent.push(node.attrs.pubkey)\n })\n return urgent.length > 0 ? urgent : undefined\n}\n\n// \u2500\u2500\u2500 [ \u2500\u2500\u2500\n\n/** Something `[` offers: a reference drawn as who said it and how it opens. */\nexport interface CaptionedItem {\n /** The referenced thing's id \u2014 the row's key and the chip's `id`. */\n id: string\n /** Who, as the list shows them. */\n label: string\n /** The opening words, marks stripped: the caption the row and the chip wear. */\n snippet: string\n /** `nostr:\u2026`, exactly as the body will carry it (`messageReference`). */\n uri: string\n /** What the query is matched against beside `label`. Default: `snippet`. */\n search?: string\n /** The row's second line, e.g. where the thing sits. The chip does not wear it. */\n description?: string\n /**\n * This row's own 16px icon, in place of the list's `icon`: what the thing is,\n * when the list holds several kinds \u2014 a topic, a project, a message\n * (Katerina, 7 October). Absent, the list's icon.\n */\n icon?: ReactNode\n}\n\n/** One labelled group of `[`'s rows, in the order the list draws them. */\nexport interface ReferenceSection {\n label: string\n items: readonly CaptionedItem[]\n}\n\n/** How many references the list shows. */\nexport const REFERENCE_LIMIT = 6\n\n/**\n * The references whose label or text holds the query \u2014 the last\n * {@link REFERENCE_LIMIT}, in the order given: a thread is unbounded, and a\n * list that grew with it would be a hundred rows of prose.\n */\nexport function filterReferences(items: readonly CaptionedItem[], query: string): CaptionedItem[] {\n const q = query.toLowerCase()\n const shown = q ? items.filter((item) => (item.search ?? item.snippet).toLowerCase().includes(q) || item.label.toLowerCase().includes(q)) : [...items]\n return shown.slice(-REFERENCE_LIMIT)\n}\n\nconst captionOf = (label: string, snippet: string) => (snippet ? `${label}: ${snippet}` : label)\n\nexport interface CaptionedReferenceOptions {\n /** Drawn before the caption: a 14px icon. */\n icon: ReactNode\n /**\n * The chip's 14px icon from what it points at \u2014 its `uri` \u2014 when chips name\n * several kinds of thing; `undefined` from it, or no `iconFor`, draws `icon`.\n * The same answer the list's row gave (Katerina, 7 October).\n */\n iconFor?: (uri: string | null) => ReactNode | undefined\n}\n\n/**\n * A reference mid-sentence, drawn as `label: snippet` and written as its `uri`\n * verbatim (SPEC \u00A713.1). The caption is the snapshot it was picked with \u2014 a\n * caption, never parsed. Inserted by {@link ReferenceTrigger}, a paste or a\n * draft; it opens no list of its own.\n */\nexport const CaptionedReference = Mention.extend<MentionOptions & CaptionedReferenceOptions>({\n name: 'captionedReference',\n group: 'inline',\n inline: true,\n atom: true,\n addOptions() {\n return { ...this.parent!(), HTMLAttributes: {}, icon: null, iconFor: undefined, suggestion: { char: '\\0', items: () => [] } }\n },\n addAttributes() {\n return {\n id: { default: null, ...fromData('id') },\n label: { default: null, ...fromData('label') },\n snippet: { default: '', parseHTML: (element: HTMLElement) => element.getAttribute('data-snippet') ?? '', renderHTML: () => ({}) },\n uri: { default: null, ...fromData('uri', NOSTR_URI) },\n }\n },\n renderHTML({ node }) {\n return [\n 'span',\n { [`data-${this.name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`]: 'true', 'data-id': String(node.attrs.id ?? ''), 'data-label': String(node.attrs.label ?? ''), 'data-snippet': String(node.attrs.snippet ?? ''), ...(node.attrs.uri ? { 'data-uri': String(node.attrs.uri) } : {}), class: inlineChipClassName('neutral', 'cursor-default') },\n captionOf(node.attrs.label, node.attrs.snippet),\n ]\n },\n parseHTML() {\n return [{ tag: `span[data-${this.name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}]` }]\n },\n addNodeView() {\n const { icon, iconFor } = this.options\n return ReactNodeViewRenderer(\n ({ node }: ReactNodeViewProps) => (\n <NodeViewWrapper as=\"span\" className={inlineChipClassName('neutral', 'max-w-[24ch] cursor-default')}>\n <span className=\"flex items-center justify-center w-4 h-4 shrink-0 text-text-secondary\">{iconFor?.(node.attrs.uri ?? null) ?? icon}</span>\n <span className=\"truncate\">{captionOf(node.attrs.label, node.attrs.snippet)}</span>\n </NodeViewWrapper>\n ),\n { as: 'span' },\n )\n },\n})\n\nexport interface ReferenceMenuOptions {\n /** Names the list, e.g. what can be referenced. */\n ariaLabel: string\n /** The section's heading. */\n sectionLabel: string\n /** At each row's start: a 16px icon \u2014 unless the row brings its own (`CaptionedItem.icon`), or `iconFor` answers. */\n icon: ReactNode\n /**\n * A row's 16px icon from what it points at \u2014 its `uri` \u2014 when the list holds\n * several kinds of thing; `undefined` from it draws `icon`. Give the chip the\n * same answer at 14px (`CaptionedReference`'s `iconFor`), so a pick keeps it.\n */\n iconFor?: (uri: string) => ReactNode | undefined\n}\n\n/** A row as the list holds it: which section it is drawn under, when the list has several. */\ntype ListedItem = CaptionedItem & { section?: string }\n\n/**\n * The list `[` opens: `label: snippet` rows, the same box as the people list.\n * One section headed `sectionLabel`, or \u2014 when the rows say which section they\n * are in \u2014 a heading for each, in the order the rows arrive.\n */\nexport function referenceMenu({ ariaLabel, sectionLabel, icon, iconFor }: ReferenceMenuOptions): SuggestionPopupOptions<CaptionedItem> {\n return {\n ariaLabel,\n width: 'w-[658px]',\n maxHeight: 'max-h-[360px]',\n sections: (items: ListedItem[]) => {\n const groups: { label: string; items: CaptionedItem[]; className: string }[] = []\n for (const item of items) {\n const label = item.section ?? sectionLabel\n const last = groups[groups.length - 1]\n if (last?.label === label) last.items.push(item)\n else groups.push({ label, items: [item], className: 'px-2' })\n }\n return groups\n },\n itemKey: (item) => `reference-${item.id}`,\n row: (item) => ({ leading: item.icon ?? iconFor?.(item.uri) ?? icon, label: captionOf(item.label, item.snippet), description: item.description, hint: <EnterHint /> }),\n }\n}\n\nexport interface ReferenceTriggerOptions extends ReferenceMenuOptions {\n /** Everything `[` can offer, asked each time the query changes. Narrowed by {@link filterReferences}. */\n items: () => readonly CaptionedItem[]\n /**\n * In place of `items`: the sections to draw for what was typed, already\n * narrowed and ordered by the app. Empty sections are not drawn.\n */\n sections?: (query: string) => readonly ReferenceSection[]\n /**\n * Called with a listener while the list is open; the app calls it when what\n * `sections` would answer has changed \u2014 a search landing \u2014 and the open list\n * is drawn again for the same query. Returns the unsubscribe.\n */\n subscribe?: (listener: () => void) => () => void\n /** The node a pick inserts. Default `captionedReference`. */\n nodeName: string\n}\n\nconst listed = (options: ReferenceTriggerOptions, query: string): ListedItem[] =>\n options.sections\n ? options.sections(query).flatMap((section) => section.items.map((item) => ({ ...item, section: section.label })))\n : filterReferences(options.items(), query)\n\n/**\n * `[` \u2014 reference something, inserted as a {@link CaptionedReference} chip.\n *\n * Its own extension rather than the chip's suggestion, so the chip's text (its\n * `renderText`) does not change with the key that summons the list.\n */\nexport const ReferenceTrigger = Extension.create<ReferenceTriggerOptions>({\n name: 'referenceTrigger',\n addOptions() {\n return { items: () => [], nodeName: 'captionedReference', ariaLabel: 'References', sectionLabel: 'References', icon: null }\n },\n addProseMirrorPlugins() {\n const options = this.options\n return [\n Suggestion<CaptionedItem>({\n editor: this.editor,\n char: '[',\n pluginKey: new PluginKey('referenceTrigger'),\n ...listening(']'),\n // As a mention's: only where the chip may go.\n allow: ({ state, range }) => {\n const type = state.schema.nodes[options.nodeName]\n return !!type && !!state.doc.resolve(range.from).parent.type.contentMatch.matchType(type)\n },\n items: ({ query }) => listed(options, query),\n render: suggestionPopup({ ...referenceMenu(options), subscribe: options.subscribe, refresh: (query) => listed(options, query) }),\n command: ({ editor, range, props: item }) => {\n editor\n .chain()\n .focus()\n .insertContentAt(range, [\n { type: options.nodeName, attrs: { id: item.id, label: item.label, snippet: item.snippet, uri: item.uri } },\n { type: 'text', text: ' ' },\n ])\n .run()\n },\n }),\n ]\n },\n})\n\n// \u2500\u2500\u2500 / \u2500\u2500\u2500\n\n/** A row of the `/` list, and what choosing it does. */\nexport interface SlashCommand {\n key: string\n row: SuggestionMenuRow\n /** Matched beside the row's label: an id, a trigger, a description. */\n keywords?: readonly string[]\n /** Runs once the `/query` that summoned the list has been removed. */\n run: (editor: Editor) => void\n /**\n * The extensions (by name) the row needs: an editor without one of them is\n * not offered the row, so no row makes something the editor cannot hold.\n */\n needs?: readonly string[]\n}\n\n/** Whether an editor has every extension a row needs. */\nconst offers = (editor: Editor, command: SlashCommand) =>\n !command.needs || command.needs.every((name) => editor.extensionManager.extensions.some((extension) => extension.name === name))\n\nexport interface SlashSection {\n label: string\n commands: readonly SlashCommand[]\n}\n\nexport type FormatId = 'paragraph' | 'heading' | 'subheading' | 'quote' | 'bulletList' | 'orderedList' | 'code' | 'checklist' | 'note' | 'warning'\n\nexport interface FormatDef {\n id: FormatId\n label: string\n /** What you type to get it \u2014 the input rule the extension registers. Blank when there is none. */\n trigger?: string\n /** The trigger when the format makes a block instead \u2014 Code's fence. Blank when it is the same. */\n blockTrigger?: string\n}\n\n/**\n * The formats `/` offers, in order. The hint is what you would type, not a key\n * chord. Bold, italic and underline are absent: they act on text already\n * written, so they are the selection toolbar's.\n */\nexport const FORMATS: readonly FormatDef[] = [\n { id: 'paragraph', label: 'Text' },\n { id: 'heading', label: 'Heading', trigger: '#' },\n { id: 'subheading', label: 'Subheading', trigger: '##' },\n { id: 'quote', label: 'Quote', trigger: '>' },\n { id: 'code', label: 'Code', trigger: '`', blockTrigger: '```' },\n { id: 'bulletList', label: 'Bulleted list', trigger: '-' },\n { id: 'orderedList', label: 'Numbered list', trigger: '1.' },\n]\n\n/**\n * What only a document holds \u2014 a checklist item and a callout (RIC-19) \u2014 so\n * only a block editor's `/` offers them (`formatSection('block')`): a\n * message's text has no spelling for either. They need the `Checklist` and\n * `Callout` extensions. Note and Warning are the two callouts offered (Miky,\n * 2026-10-13); the other tones are read and kept, not offered.\n */\nconst DOCUMENT_FORMATS: readonly FormatDef[] = [\n { id: 'checklist', label: 'Checklist', trigger: '[]' },\n { id: 'note', label: 'Note' },\n { id: 'warning', label: 'Warning' },\n]\n\n/** Code as a mark inside the line, or as a block of its own. */\nexport type CodeFormat = 'inline' | 'block'\n\n/**\n * Apply one format to the current block. `set*` wherever Tiptap has one:\n * choosing Heading should make a heading, not turn one off. `false` means\n * nothing changed, not that anything failed.\n */\nexport function applyFormat(editor: Editor, id: FormatId, code: CodeFormat = 'inline'): boolean {\n const chain = editor.chain().focus()\n /*\n The node's own command, called by name: StarterKit's extensions declare\n them, and this package builds without StarterKit. Its own command, rather\n than the core one it wraps, because a list's runs with the app's options\n (`keepMarks`, `itemTypeName`). An editor without that node changes nothing.\n */\n const run = (command: string, ...args: unknown[]): boolean => {\n const named = (chain as unknown as Record<string, ((...a: unknown[]) => { run: () => boolean }) | undefined>)[command]\n return named ? named(...args).run() : false\n }\n switch (id) {\n case 'paragraph':\n return run('setParagraph')\n case 'heading':\n return run('setHeading', { level: 1 })\n case 'subheading':\n return run('setHeading', { level: 2 })\n case 'quote':\n return run('toggleBlockquote')\n case 'bulletList':\n return run('toggleBulletList')\n case 'orderedList':\n return run('toggleOrderedList')\n case 'code':\n return run(code === 'block' ? 'toggleCodeBlock' : 'toggleCode')\n case 'checklist': {\n // In a list, that item becomes one; elsewhere, the line becomes a list of one.\n if (!editor.isActive('listItem') && !run('toggleBulletList')) return false\n // An item already a checklist item keeps its tick.\n if (typeof editor.getAttributes('listItem').checked === 'boolean') return true\n return editor.chain().updateAttributes('listItem', { checked: false }).run()\n }\n case 'note':\n case 'warning': {\n if (!editor.isActive('blockquote') && !run('toggleBlockquote')) return false\n return editor.chain().updateAttributes('blockquote', { tone: id }).run()\n }\n default:\n return false\n }\n}\n\n/** Words a row is also found by, beyond its id. */\nconst KEYWORDS: Partial<Record<FormatId, string[]>> = {\n checklist: ['todo', 'task'],\n note: ['callout'],\n warning: ['callout'],\n}\n\n/** The extension a row's block needs: without it the row would make a plain bullet or a plain quote (Katerina, review of #170). */\nconst NEEDS: Partial<Record<FormatId, string[]>> = {\n checklist: ['checklist'],\n note: ['callout'],\n warning: ['callout'],\n}\n\n/**\n * The Format section every app's `/` opens with. A block editor's also offers\n * a checklist and the callouts \u2014 only where the editor has the `Checklist` and\n * `Callout` extensions (`SlashCommand.needs`).\n */\nexport function formatSection(code: CodeFormat = 'inline'): SlashSection {\n return {\n label: 'Format',\n commands: (code === 'block' ? [...FORMATS, ...DOCUMENT_FORMATS] : FORMATS).map((format) => ({\n key: `format-${format.id}`,\n row: { label: format.label, shortcut: (code === 'block' && format.blockTrigger) || format.trigger },\n keywords: [format.id, ...(KEYWORDS[format.id] ?? [])],\n ...(NEEDS[format.id] ? { needs: NEEDS[format.id] } : {}),\n run: (editor) => {\n applyFormat(editor, format.id, code)\n },\n })),\n }\n}\n\n/** A row that types another trigger, so its own list opens \u2014 `@` from `/`. */\nexport function typeTrigger(input: string): SlashCommand['run'] {\n // Deferred a frame: the delete of the `/query` has to land first, or the\n // trigger's plugin sees the character arrive inside a range still being removed.\n return (editor) => {\n requestAnimationFrame(() => editor.commands.insertContent(input))\n }\n}\n\nfunction matches(command: SlashCommand, q: string): boolean {\n return [command.row.label, ...(command.keywords ?? [])].some((text) => text.toLowerCase().includes(q))\n}\n\n/** The sections narrowed to a query \u2014 the text typed after the `/`. Empty sections stay. */\nexport function filterSlashSections(sections: readonly SlashSection[], query: string): SlashSection[] {\n const q = query.toLowerCase()\n return sections.map((section) => ({ ...section, commands: q ? section.commands.filter((command) => matches(command, q)) : section.commands }))\n}\n\nexport interface SlashCommandsOptions {\n /** The sections, in order: {@link formatSection} first, then the app's own. */\n sections: readonly SlashSection[]\n}\n\ntype SlashRow = SlashCommand & { section: number }\n\n/**\n * `/` \u2014 format the line or insert something, at the caret, mid-sentence too.\n * A `/` inside a word (`and/or`, a path) opens nothing: the plugin wants a\n * space or the line's start before it.\n */\nexport const SlashCommands = Extension.create<SlashCommandsOptions>({\n name: 'slashCommands',\n addOptions() {\n return { sections: [formatSection()] }\n },\n addProseMirrorPlugins() {\n const sections = this.options.sections\n return [\n Suggestion<SlashRow>({\n editor: this.editor,\n char: '/',\n startOfLine: false,\n pluginKey: new PluginKey('slashCommands'),\n items: ({ query, editor }) =>\n filterSlashSections(sections, query).flatMap((section, at) =>\n section.commands.filter((command) => offers(editor, command)).map((command) => ({ ...command, section: at })),\n ),\n command: ({ editor, range, props }) => {\n editor.chain().focus().deleteRange(range).run()\n props.run(editor)\n },\n render: suggestionPopup<SlashRow>({\n ariaLabel: 'Commands',\n width: 'w-[300px]',\n maxHeight: 'max-h-[400px]',\n sections: (rows) => sections.map((section, at) => ({ label: section.label, items: rows.filter((row) => row.section === at) })),\n itemKey: (row) => row.key,\n row: (row) => row.row,\n }),\n }),\n ]\n },\n})\n", "import type { ComponentType, MouseEvent } from 'react'\nimport { Extension, InputRule, Node, type Editor } from '@tiptap/core'\nimport { DOMSerializer, type Node as ProseMirrorNode, type Slice } from '@tiptap/pm/model'\nimport { Plugin, PluginKey, type EditorState, type Transaction } from '@tiptap/pm/state'\nimport { Mapping, ReplaceAroundStep } from '@tiptap/pm/transform'\nimport type { EditorView, NodeView } from '@tiptap/pm/view'\nimport { NodeViewWrapper, ReactNodeViewRenderer, ReactRenderer, type ReactNodeViewProps } from '@tiptap/react'\nimport { Checkbox, type CheckboxProps } from './Checkbox'\nimport { calloutLook } from './RichText'\nimport { referenceMenuClick } from './ReferenceMenu'\n\n/*\n * The four nodes an editor of a block document needs that a stock schema does\n * not have (SPEC \u00A713.3) \u2014 moved here from Ship's `editorSchema.tsx` (RIC-14,\n * SHI-2) when Peek became the second editor of the same documents (MAN-9).\n *\n * They are one half of a pair. The other half is `@estiva-app/protocol`'s\n * `toEditorDocument` / `fromEditorDocument`, which turn a stored document into\n * the JSON these nodes read and back: the node names (`unknownBlock`,\n * `reference`, `attachment`) and the `blockId` attribute are that translation's,\n * and must not drift from it. This package does not import protocol, as\n * `RichText` does not: the translation hands over everything a view draws.\n *\n * Everything else a document can contain is already a ProseMirror node of the\n * same name \u2014 StarterKit's and TableKit's \u2014 which is why this file is short.\n */\n\n/**\n * The block types \u00A713.3 puts an id on, as the editor's node names \u2014\n * protocol's `EDITOR_BLOCK_TYPES`, less `unknownBlock`, which declares its own.\n */\nconst BLOCK_TYPES = [\n 'paragraph',\n 'heading',\n 'bulletList',\n 'orderedList',\n 'listItem',\n 'blockquote',\n 'codeBlock',\n 'horizontalRule',\n // A table and everything inside it (UIG-18): a row or a cell whose id was\n // re-minted on each save would detach any comment anchored to it.\n 'table',\n 'tableRow',\n 'tableHeader',\n 'tableCell',\n // Without this an attachment's id is re-minted on every save, and every\n // comment anchored to it stops resolving.\n 'attachment',\n]\nconst BLOCK_TYPE_SET = new Set(BLOCK_TYPES)\n\n/** protocol's `newBlockId`: six random bytes as hex. */\nfunction newBlockId(): string {\n return Array.from(crypto.getRandomValues(new Uint8Array(6)), (b) => b.toString(16).padStart(2, '0')).join('')\n}\n\n/** Whether anything typed or inserted is inside \u2014 protocol's `holdsText`. An empty paragraph is not; a lone line break is. */\nfunction holdsText(node: ProseMirrorNode): boolean {\n let found = false\n node.descendants((child) => {\n if (found) return false\n if ((child.isText && child.text !== '') || child.type.name === 'hardBreak' || child.type.name === 'reference') found = true\n return !found\n })\n return found\n}\n\ninterface Claim {\n pos: number\n node: ProseMirrorNode\n /** An unknown block: the translation hands its source back untouched, so re-minting it here would be undone on save. */\n fixed: boolean\n}\n\n/** Every block that claims an id, by id, in document order. */\nfunction claimsIn(doc: ProseMirrorNode): Map<string, Claim[]> {\n const claims = new Map<string, Claim[]>()\n doc.descendants((node, pos, parent) => {\n const fixed = node.type.name === 'unknownBlock'\n if (!fixed && !BLOCK_TYPE_SET.has(node.type.name)) return\n // A list inside a list item is no block (MAN-13): an id it arrived with is\n // cleared, never a claim that could take a real block's.\n if (parent?.type.name === 'listItem' && LISTS.has(node.type.name)) return\n const id = node.attrs.blockId\n if (typeof id !== 'string' || id === '') return\n const copies = claims.get(id)\n if (copies) copies.push({ pos, node, fixed })\n else claims.set(id, [{ pos, node, fixed }])\n })\n return claims\n}\n\n/**\n * The copy the block that was there before carried on into: where the start\n * of its content landed. Enter at the very start pushes that content into the\n * lower half; a split anywhere else leaves it in the upper; a pasted copy,\n * above or below, never moves it out of the original.\n */\nfunction carriedOn(copies: Claim[], before: Claim, mapping: Mapping): Claim | undefined {\n if (before.node.isLeaf) {\n const at = mapping.map(before.pos, 1)\n return copies.find((c) => c.pos === at)\n }\n const at = mapping.map(textStart(before), 1)\n return copies.find((c) => c.pos < at && at < c.pos + c.node.nodeSize)\n}\n\n/** The start of a block's text, inside the paragraph a list item or quote wraps it in. */\nfunction textStart({ pos, node }: { pos: number; node: ProseMirrorNode }): number {\n let start = pos + 1\n if (!node.isTextblock) {\n let found = false\n node.descendants((child, at) => {\n if (found) return false\n if (child.isTextblock) {\n start = pos + 1 + at + 1\n found = true\n }\n return !found\n })\n }\n return start\n}\n\n/**\n * Where a block claims an id another block keeps \u2014 see {@link BlockId}. An\n * unknown block always keeps its id; otherwise the copy the original carried\n * on into, and failing that protocol's rule: the first that holds text, or\n * the first.\n */\nfunction repeatedBlockIds(doc: ProseMirrorNode, before: ProseMirrorNode, mapping: Mapping): number[] {\n const claims = claimsIn(doc)\n let previous: Map<string, Claim[]> | undefined\n const losers: number[] = []\n for (const [id, copies] of claims) {\n if (copies.length < 2) continue\n previous ??= claimsIn(before)\n const original = previous.get(id)?.[0]\n const owner =\n copies.find((c) => c.fixed) ??\n (original && carriedOn(copies, original, mapping)) ??\n copies.find((c) => holdsText(c.node)) ??\n copies[0]\n for (const copy of copies) if (copy !== owner && !copy.fixed) losers.push(copy.pos)\n }\n return losers\n}\n\n/**\n * The blocks a save turns into blocks \u2014 exactly what protocol's\n * `blockFromEditor` walks \u2014 so a list, a table and its rows and cells are\n * walked into, and a list item or a quote is not: the save flattens their\n * paragraph wrappers into inline runs, so an id there would be dropped.\n */\nconst BLOCK_CONTAINERS = new Set(['bulletList', 'orderedList', 'table', 'tableRow', 'tableHeader', 'tableCell'])\n\n/** The wrappers the save flattens: their paragraphs become inline runs of theirs. */\nconst FLATTENED = new Set(['listItem', 'blockquote'])\n\n/**\n * The lists Tab puts inside a list item (MAN-13). \u00A713.3 keeps a list flat and\n * marks an item's depth, so the save walks into such a list for its items and\n * writes no block for the list itself: its items get ids, it gets none.\n */\nconst LISTS = new Set(['bulletList', 'orderedList'])\n\n/** The lists inside a list item, with where each starts. */\nfunction innerLists(item: ProseMirrorNode, pos: number): { pos: number; node: ProseMirrorNode }[] {\n const out: { pos: number; node: ProseMirrorNode }[] = []\n item.forEach((child, offset) => {\n if (LISTS.has(child.type.name)) out.push({ pos: pos + 1 + offset, node: child })\n })\n return out\n}\n\nconst idOf = (node: ProseMirrorNode): string | undefined => {\n const id = node.attrs.blockId\n return typeof id === 'string' && id !== '' ? id : undefined\n}\n\n/** Every block the save turns into a block, in document order. */\nfunction addressedBlocks(doc: ProseMirrorNode): { pos: number; node: ProseMirrorNode }[] {\n const out: { pos: number; node: ProseMirrorNode }[] = []\n const visit = (node: ProseMirrorNode, pos: number) => {\n if (!BLOCK_TYPE_SET.has(node.type.name)) return\n out.push({ pos, node })\n if (BLOCK_CONTAINERS.has(node.type.name)) node.forEach((child, offset) => visit(child, pos + 1 + offset))\n if (node.type.name === 'listItem') {\n for (const list of innerLists(node, pos)) list.node.forEach((child, offset) => visit(child, list.pos + 1 + offset))\n }\n }\n doc.forEach((node, offset) => visit(node, offset))\n return out\n}\n\n/** Where a block the save would give an id has none, so it would get a new one on every save. */\nfunction unnamedBlocks(doc: ProseMirrorNode): number[] {\n return addressedBlocks(doc)\n .filter((b) => idOf(b.node) === undefined)\n .map((b) => b.pos)\n}\n\n/**\n * Where a paragraph inside a list item or a quote claims an id, or a list\n * inside a list item does \u2014 both of which the save drops.\n */\nfunction hiddenIds(doc: ProseMirrorNode): number[] {\n const out: number[] = []\n doc.descendants((node, pos) => {\n if (!FLATTENED.has(node.type.name)) return\n node.forEach((child, offset) => {\n const inner = child.isTextblock || (node.type.name === 'listItem' && LISTS.has(child.type.name))\n if (inner && idOf(child) !== undefined) out.push(pos + 1 + offset)\n })\n })\n return out\n}\n\n/**\n * The id each block a wrap or a lift brought in takes over \u2014 see\n * {@link BlockId}. An id the save addressed before the change and does not\n * now goes to the innermost new block its text was carried into: the quote or\n * the list item around a paragraph, or the paragraph lifted out of one.\n * A block the change deleted hands nothing on. What it asks is whether the\n * block's own opening survived, the token left of its text, not its first\n * character: the `/` menu and a typed `> ` or `- ` delete what was typed at\n * the very start of the text in the same change as the wrap.\n */\nfunction handedOver(doc: ProseMirrorNode, before: ProseMirrorNode, mapping: Mapping, unnamed: number[]): Map<number, string> {\n const out = new Map<number, string>()\n if (unnamed.length === 0) return out\n const now = new Set(addressedBlocks(doc).map((b) => idOf(b.node)))\n // By where their text starts, and of a list and its item around the same text, the item first.\n const gone = addressedBlocks(before)\n .filter((b) => !b.node.isLeaf && idOf(b.node) !== undefined && !now.has(idOf(b.node)))\n .map((b) => ({ ...b, start: textStart(b) }))\n .sort((a, b) => a.start - b.start || b.pos - a.pos)\n for (const old of gone) {\n const id = idOf(old.node)!\n const { pos: at, deleted } = mapping.mapResult(old.start, -1)\n if (deleted) continue\n let inner: number | undefined\n for (const pos of unnamed) {\n if (out.has(pos)) continue\n if (pos < at && at < pos + doc.nodeAt(pos)!.nodeSize && (inner === undefined || pos > inner)) inner = pos\n }\n if (inner !== undefined) {\n out.set(inner, id)\n now.add(id)\n }\n }\n return out\n}\n\n/**\n * Whether a change could have made a repeat or a block without an id: only a\n * step that brings in blocks can, or one that wraps or lifts them \u2014 lifting\n * the paragraph out of a quote or a one-item list brings in nothing, yet\n * leaves a block the save addresses with the wrapper's missing id. Typing,\n * deleting and marks do neither, so most keystrokes skip the walk.\n */\nfunction bringsInBlocks(transactions: readonly Transaction[]): boolean {\n return transactions.some((tr) =>\n tr.steps.some((step) => {\n if (step instanceof ReplaceAroundStep) return true\n const slice = (step as { slice?: Slice }).slice\n let blocks = false\n slice?.content.forEach((node) => {\n if (node.isBlock) blocks = true\n })\n return blocks\n }),\n )\n}\n\n/**\n * `id` on every block, carried as the `blockId` attribute.\n *\n * A global attribute rather than one per node type, because \u00A713.3 puts an id\n * on **every** block and a type that quietly lacked one would mint a fresh id\n * on every save \u2014 detaching each anchored comment on it (RFC 0.4 \u00A76) with\n * nothing on screen to say so.\n *\n * Rendered as `data-block-id`, which is what `RichText` already emits, so the\n * reading and the editing surface address a block the same way.\n *\n * **A split gives the new half its own id, the moment it happens** (MAN-10).\n * ProseMirror copies a split node's attrs to both halves, so Enter leaves two\n * blocks claiming one id. Enter at the very start of a paragraph puts the\n * copy *above* it \u2014 and once something is typed there, nothing at save time\n * can tell which half was the original, so protocol's `fromEditorDocument`\n * gave the id, and every comment anchored to it, to the new line. Settled\n * here instead, in the same transaction, where the change itself says which\n * copy the original carried on into \u2014 so a copy pasted above a block does\n * not take its id either. The others get fresh ids. `fromEditorDocument`\n * keeps its own rule (the copy that holds text) as the backstop for an\n * editor without this extension; where the change says nothing, this falls\n * back to that rule too.\n *\n * **A block that arrives without an id gets one, the moment it arrives**\n * (MAN-11): a paste, a `/` menu insert, the empty paragraph a new editor\n * starts with and the one StarterKit keeps at the end. `fromEditorDocument`\n * mints an id for such a block on every save and the editor never sees it, so\n * each save gave it a different one \u2014 a blur with no edit published again, and\n * a comment anchored to it in between was detached. The editor's first\n * document is filled in when it is created, outside the undo history and\n * without an update, so opening a description is not an edit.\n *\n * **A wrap or a lift hands the id on** (MAN-12). Turning a paragraph into a\n * quote or a list wraps it, and ProseMirror leaves its attrs on the paragraph\n * inside \u2014 where the save flattens it and drops the id, so the quote or list\n * item was published under a new one and every comment anchored to the\n * paragraph was detached. The quote or the list item takes the paragraph's id\n * instead, and the list around it a fresh one; lifting the text back out hands\n * the id to the paragraph again. The paragraph inside keeps no id, as one\n * loaded from a stored document has none.\n */\nexport const BlockId = Extension.create({\n name: 'blockId',\n onCreate() {\n const { state, view } = this.editor\n const unnamed = unnamedBlocks(state.doc)\n if (unnamed.length === 0) return\n const tr = state.tr\n for (const pos of unnamed) tr.setNodeAttribute(pos, 'blockId', newBlockId())\n view.dispatch(tr.setMeta('addToHistory', false).setMeta('preventUpdate', true))\n },\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('blockIdSplit'),\n appendTransaction: (transactions, old, state) => {\n if (!bringsInBlocks(transactions)) return null\n const mapping = new Mapping()\n for (const tr of transactions) mapping.appendMapping(tr.mapping)\n const unnamed = unnamedBlocks(state.doc)\n const handed = handedOver(state.doc, old.doc, mapping, unnamed)\n const ids = new Map<number, string | null>()\n for (const pos of repeatedBlockIds(state.doc, old.doc, mapping)) ids.set(pos, newBlockId())\n for (const pos of unnamed) ids.set(pos, handed.get(pos) ?? newBlockId())\n for (const pos of hiddenIds(state.doc)) ids.set(pos, null)\n if (ids.size === 0) return null\n const tr = state.tr\n for (const [pos, id] of ids) tr.setNodeAttribute(pos, 'blockId', id)\n return tr\n },\n }),\n ]\n },\n addGlobalAttributes() {\n return [\n {\n types: BLOCK_TYPES,\n attributes: {\n blockId: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute('data-block-id'),\n renderHTML: (attributes: Record<string, unknown>) =>\n attributes.blockId ? { 'data-block-id': String(attributes.blockId) } : {},\n },\n },\n },\n ]\n },\n})\n\n/** An http(s) URL, or null. */\nconst webUrl = (raw: string | null): string | null => (raw && /^https?:\\/\\/\\S+$/i.test(raw) ? raw : null)\n\n/** What a reference's view is handed: the `nostr:` URI it stands for, and the URL a URL chip was written as. */\nexport interface ReferenceViewProps {\n uri: string\n href?: string\n}\n\nexport interface ReferenceNodeOptions {\n /**\n * Draws the reference as the thing it points at \u2014 the app's own chip, which\n * knows how to resolve it. Without one, the URI is drawn as text.\n */\n view: ComponentType<ReferenceViewProps> | null\n}\n\n/**\n * A `nostr:` reference, drawn as the thing it points at \u2014 SPEC \u00A713.1.\n *\n * An **atom**: it has no editable interior, because its text is the URI and\n * nobody edits half a `naddr`. Selecting it selects the whole reference, which\n * is also how deleting one works. `marks` carries the run's other marks, so a\n * bold reference comes back bold.\n *\n * `embed` is the card its writer chose to draw after its block \u2014 `compact`,\n * `detailed`, or null for none (SPEC \u00A713.3, \"A reference's card\") \u2014 and `extra`\n * every key of the mark this version does not know. Both are declared so that\n * ProseMirror keeps them: an undeclared attribute is dropped when the node is\n * made, and the card with it. `ReferenceCards` draws the cards.\n *\n * `href` makes it a **URL chip**: an app URL that reads as the chip of what it\n * names, and is that URL on the wire (\u00A713.3). `uri` is what it names, filled in\n * by `LinkChips` once the app has resolved it; until then it draws as the URL.\n */\nexport const ReferenceNode = Node.create<ReferenceNodeOptions>({\n name: 'reference',\n group: 'inline',\n inline: true,\n atom: true,\n selectable: true,\n draggable: false,\n addOptions() {\n return { view: null }\n },\n addAttributes() {\n return {\n // A pasted URL chip keeps only its URL: what it names is the editor's to resolve (`LinkChips`), never\n // the clipboard's, which could show one object while saving another URL (security review).\n uri: { default: '', parseHTML: (element: HTMLElement) => (webUrl(element.getAttribute('data-href')) ? '' : (element.getAttribute('data-reference') ?? '')) },\n marks: { default: [] as string[] },\n embed: { default: null as string | null, parseHTML: (element: HTMLElement) => element.getAttribute('data-embed') },\n extra: { default: null as Record<string, unknown> | null, parseHTML: () => null },\n // An http(s) URL only: `LinkChips` makes no other, and a pasted `javascript:` must not reach a saved link.\n href: { default: null as string | null, parseHTML: (element: HTMLElement) => webUrl(element.getAttribute('data-href')) },\n }\n },\n parseHTML() {\n return [{ tag: 'span[data-reference]' }]\n },\n renderHTML({ node }) {\n // A copy to the clipboard as HTML, and the whole drawing when no view is given.\n // The card goes along, so a paste keeps it; a URL chip copies as its URL.\n const embed = typeof node.attrs.embed === 'string' ? { 'data-embed': node.attrs.embed } : {}\n const href = typeof node.attrs.href === 'string' && node.attrs.href ? { 'data-href': node.attrs.href } : {}\n return ['span', { 'data-reference': String(node.attrs.uri), ...embed, ...href }, String(node.attrs.href || node.attrs.uri)]\n },\n renderText({ node }) {\n return String(node.attrs.href || node.attrs.uri)\n },\n addNodeView() {\n const View = this.options.view\n if (!View) return null\n return ReactNodeViewRenderer(({ node, editor, getPos }: ReactNodeViewProps) => {\n const uri = String(node.attrs.uri ?? '')\n const href = typeof node.attrs.href === 'string' && node.attrs.href ? node.attrs.href : undefined\n return (\n <NodeViewWrapper\n as=\"span\"\n className=\"inline\"\n // While editing, a click opens the chip's menu (`ReferenceMenu`) rather than following the chip.\n onClickCapture={(event: MouseEvent) => {\n const pos = getPos()\n if (typeof pos !== 'number' || !referenceMenuClick(editor, pos)) return\n event.preventDefault()\n event.stopPropagation()\n }}\n >\n {/* A URL chip not resolved yet reads as the URL it was written as. */}\n {uri ? <View uri={uri} {...(href ? { href } : {})} /> : <span>{href}</span>}\n </NodeViewWrapper>\n )\n })\n },\n})\n\n/**\n * A block this editor has no design for, kept whole.\n *\n * \u00A713.3: a reader MUST render an unknown block's inline text and MUST NOT drop\n * it silently. **An editor is the dangerous case** \u2014 ProseMirror discards a\n * node its schema does not know, and the save after that would delete somebody\n * else's block with no error anywhere on the way. So the block's own JSON\n * rides in `source`, and protocol's `fromEditorDocument` hands it back\n * untouched; `text` is its inline text, worked out by `toEditorDocument`.\n *\n * Not editable, deliberately: an affordance that lets a person change\n * something they cannot see is worse than one that says the content is there\n * and belongs to another surface.\n */\nexport const UnknownBlock = Node.create({\n name: 'unknownBlock',\n group: 'block',\n atom: true,\n selectable: true,\n addAttributes() {\n return {\n blockId: { default: null },\n source: { default: null },\n text: { default: '' },\n }\n },\n parseHTML() {\n return [{ tag: 'div[data-unknown-block]' }]\n },\n renderHTML({ node }) {\n return ['div', { 'data-unknown-block': String(node.attrs.blockId ?? '') }, String(node.attrs.text ?? '')]\n },\n addNodeView() {\n return ReactNodeViewRenderer(UnknownBlockView)\n },\n})\n\nfunction UnknownBlockView({ node }: ReactNodeViewProps) {\n const source = node.attrs.source as { id?: string; type?: string } | null\n const text = String(node.attrs.text ?? '')\n return (\n <NodeViewWrapper\n data-block-id={source?.id}\n data-block-type={source?.type}\n className=\"rounded-md border border-border-default bg-bg-inset px-2 py-1 text-text-secondary\"\n >\n {text || `A ${source?.type ?? 'block'} this app cannot edit yet.`}\n </NodeViewWrapper>\n )\n}\n\n/** What an attachment's view is handed: the block's `imeta` fields. */\nexport interface AttachmentViewProps {\n attrs: Record<string, unknown>\n}\n\nexport interface AttachmentNodeOptions {\n /** Draws the file \u2014 the app's own attachment card. Without one, its name is drawn as text. */\n view: ComponentType<AttachmentViewProps> | null\n}\n\n/**\n * A file placed in a document \u2014 SPEC \u00A713.3's `attachment` block (SHI-2).\n *\n * An atom: the file is the block, there is nothing inside it to edit, and a\n * caret that could sit \"inside\" an image is a way to produce documents no\n * reader has a rule for. Selectable and deletable, so removing one is Backspace\n * like anything else.\n *\n * Its attributes are the `imeta` fields exactly, because that is what \u00A713.3\n * specifies. Each is declared, and that is not boilerplate: ProseMirror **drops\n * an attribute its schema does not name**, so an undeclared `thumb` would\n * survive the editor and vanish on save \u2014 the silent deletion `UnknownBlock`\n * exists to prevent.\n */\nexport const AttachmentNode = Node.create<AttachmentNodeOptions>({\n name: 'attachment',\n group: 'block',\n atom: true,\n selectable: true,\n draggable: false,\n addOptions() {\n return { view: null }\n },\n addAttributes() {\n return {\n url: { default: '' },\n m: { default: '' },\n x: { default: '' },\n size: { default: 0 },\n dim: { default: null },\n thumb: { default: null },\n alt: { default: null },\n filename: { default: null },\n }\n },\n parseHTML() {\n return [{ tag: 'div[data-attachment-block]' }]\n },\n renderHTML({ node }) {\n // A copy to the clipboard, and the whole drawing when no view is given.\n return ['div', { 'data-attachment-block': String(node.attrs.x ?? '') }, String(node.attrs.filename ?? 'attachment')]\n },\n addNodeView() {\n const View = this.options.view\n if (!View) return null\n return ReactNodeViewRenderer(({ node }: ReactNodeViewProps) => (\n <NodeViewWrapper className=\"my-2\">\n <View attrs={node.attrs} />\n </NodeViewWrapper>\n ))\n },\n})\n\nconst TICK = 'checklistTick'\n\n/**\n * Whether a change is a click on a checklist item's box. The click does not\n * focus the editor, so no blur follows it: an editor that saves on blur saves\n * a tick straight away instead (RIC-19, Miky 2026-10-13: one click ticks).\n */\nexport function isChecklistTick(transaction: Transaction): boolean {\n return transaction.getMeta(TICK) === true\n}\n\n/** A list or quote command by name: StarterKit's declare them, and this package builds without StarterKit. */\ntype Chain = ReturnType<Editor['chain']>\nfunction named(chain: Chain, command: string, ...args: unknown[]): Chain {\n const own = (chain as unknown as Record<string, ((...a: unknown[]) => Chain) | undefined>)[command]\n return own ? own(...args) : chain\n}\n\n/** The list item the caret's paragraph is the first line of, if it is. */\nfunction itemAtCaret(state: EditorState): { node: ProseMirrorNode; pos: number } | null {\n const { $from } = state.selection\n if ($from.depth < 2) return null\n const item = $from.node(-1)\n if (item.type.name !== 'listItem' || $from.index(-1) !== 0) return null\n return { node: item, pos: $from.before(-1) }\n}\n\nconst sameAttrs = (a: ProseMirrorNode, b: ProseMirrorNode) =>\n Object.keys({ ...a.attrs, ...b.attrs }).every((key) => a.attrs[key] === b.attrs[key])\n\n/**\n * A list item, drawn with a box before it when it is a checklist item. The box\n * is the package's `Checkbox`, as `RichText` draws it, and the item's own\n * text and inner lists follow it in a `div`. Any change to the item's\n * attributes draws it again, which is what a tick is.\n *\n * The box is named by the item's words, and follows them as they are typed.\n * Whether it ticks follows the editor's `editable`, read again on every\n * update \u2014 `setEditable` sends one \u2014 not only when the item was drawn\n * (Katerina, review of #170). Not editable, it is a picture, and the box around\n * it says done or not to a screen reader, as `RichText`'s does.\n */\nfunction listItemView(editor: Editor, node: ProseMirrorNode, view: EditorView, getPos: () => number | undefined): NodeView {\n const toDOM = node.type.spec.toDOM\n const { dom, contentDOM } = toDOM ? DOMSerializer.renderSpec(document, toDOM(node)) : { dom: document.createElement('li'), contentDOM: undefined }\n const checked = node.attrs.checked\n if (typeof checked !== 'boolean') return { dom, contentDOM: contentDOM ?? dom, update: (next) => next.type === node.type && sameAttrs(next, node) }\n\n const li = dom as HTMLElement\n const box = document.createElement('span')\n box.contentEditable = 'false'\n box.setAttribute('data-checklist-box', '')\n const body = document.createElement('div')\n li.replaceChildren(box, body)\n const tick = (next: boolean) => {\n const pos = getPos()\n if (pos === undefined || !view.editable) return\n view.dispatch(view.state.tr.setNodeAttribute(pos, 'checked', next).setMeta(TICK, true))\n }\n let words = node.firstChild?.textContent.trim() || 'Item'\n const props = (): CheckboxProps => ({ checked, onChange: view.editable ? tick : undefined, 'aria-label': words })\n const renderer = new ReactRenderer<unknown, CheckboxProps>(Checkbox, { editor, props: props(), as: 'span' })\n box.appendChild(renderer.element)\n // What the box last drew: an update that changes neither leaves it alone, so typing does not draw every box again.\n let drawn = ''\n const sync = () => {\n const now = `${view.editable}:${words}`\n if (now === drawn) return\n drawn = now\n renderer.updateProps(props())\n for (const [name, value] of Object.entries({ role: 'checkbox', 'aria-checked': String(checked), 'aria-readonly': 'true', 'aria-label': words })) {\n if (view.editable) box.removeAttribute(name)\n else box.setAttribute(name, value)\n }\n }\n sync()\n editor.on('update', sync)\n return {\n dom: li,\n contentDOM: body,\n update: (next) => {\n if (next.type !== node.type || !sameAttrs(next, node)) return false\n words = next.firstChild?.textContent.trim() || 'Item'\n sync()\n return true\n },\n stopEvent: (event) => box.contains(event.target as globalThis.Node),\n ignoreMutation: (mutation) => box.contains(mutation.target),\n destroy: () => {\n editor.off('update', sync)\n renderer.destroy()\n },\n }\n}\n\n/**\n * A checklist item \u2014 SPEC \u00A713.3's `checked` on a list item (RIC-19): `true`\n * ticked, `false` not, absent a plain item. Add it beside `BlockId`.\n *\n * - **The box ticks with one click**, where the description is read, and the\n * change says so (`isChecklistTick`), so the app saves it at once.\n * - **`[ ] ` or `[] ` at the start of a line** makes it, `[x] ` ticked; in a\n * list it turns that item into one.\n * - **Enter** starts the next item unticked: a new item never takes the tick\n * of the one it was split from. At the start of an item, the text moves down\n * with its tick and the empty item above is unticked. On an empty item Enter\n * leaves the list, as in any list.\n * - **Backspace** at the start of a checklist item takes its box away first;\n * the next one joins it to the item above, as in any list.\n *\n * An editor without it drops `checked` on save and keeps the item and its text.\n */\nexport const Checklist = Extension.create({\n name: 'checklist',\n // Before the list item's own Enter and Backspace.\n priority: 101,\n addGlobalAttributes() {\n return [\n {\n types: ['listItem'],\n attributes: {\n checked: {\n default: null,\n keepOnSplit: false,\n parseHTML: (element: HTMLElement) => {\n const value = element.getAttribute('data-checked')\n return value === 'true' ? true : value === 'false' ? false : null\n },\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.checked === 'boolean' ? { 'data-checked': String(attributes.checked) } : {},\n },\n },\n },\n ]\n },\n addInputRules() {\n return [\n new InputRule({\n find: /^\\[([ xX]?)\\]\\s$/,\n handler: ({ state, range, match, chain }) => {\n const checked = match[1].toLowerCase() === 'x'\n const inList = itemAtCaret(state) !== null\n // Without a bullet list to make, the typed text stays as it was typed.\n if (!inList && !(chain() as unknown as Record<string, unknown>).toggleBulletList) return null\n const deleted = chain().deleteRange(range)\n const listed = inList ? deleted : named(deleted, 'toggleBulletList')\n listed.updateAttributes('listItem', { checked }).run()\n },\n }),\n ]\n },\n addKeyboardShortcuts() {\n return {\n Enter: () => {\n const { state } = this.editor\n const item = itemAtCaret(state)\n const { $from, empty } = state.selection\n if (!item || typeof item.node.attrs.checked !== 'boolean') return false\n // Over a selection: the selection goes and the next item is unticked, as for a caret in the text.\n if (!empty) return this.editor.chain().deleteSelection().splitListItem('listItem', { checked: false }).run()\n // An empty item: the list item's own Enter, which leaves the list.\n if ($from.parent.content.size === 0) return false\n if ($from.parentOffset > 0) return this.editor.commands.splitListItem('listItem', { checked: false })\n return this.editor\n .chain()\n .splitListItem('listItem', { checked: item.node.attrs.checked })\n .command(({ tr }) => {\n tr.setNodeAttribute(tr.mapping.map(item.pos), 'checked', false)\n return true\n })\n .run()\n },\n Backspace: () => {\n const { state } = this.editor\n const item = itemAtCaret(state)\n const { $from, empty } = state.selection\n if (!item || !empty || $from.parentOffset > 0 || typeof item.node.attrs.checked !== 'boolean') return false\n this.editor.view.dispatch(state.tr.setNodeAttribute(item.pos, 'checked', null))\n return true\n },\n }\n },\n addProseMirrorPlugins() {\n const editor = this.editor\n return [\n new Plugin({\n key: new PluginKey('checklist'),\n props: { nodeViews: { listItem: (node, view, getPos) => listItemView(editor, node, view, getPos) } },\n }),\n ]\n },\n})\n\n/**\n * A callout \u2014 SPEC \u00A713.3's `tone` on a quote (RIC-19): a quote drawn to stand\n * out, in one of GitHub's alert kinds (`note`, `tip`, `important`, `warning`,\n * `caution`). Its look is `RichText`'s, by `data-tone`; a tone it does not\n * know looks like `note`, and the editor keeps it as written. Add it beside\n * `BlockId`.\n */\nexport const Callout = Extension.create({\n name: 'callout',\n addGlobalAttributes() {\n return [\n {\n types: ['blockquote'],\n attributes: {\n tone: {\n default: null,\n parseHTML: (element: HTMLElement) => element.getAttribute('data-tone'),\n // As `RichText` draws it: the tone as written, and the look it is drawn in.\n renderHTML: (attributes: Record<string, unknown>) =>\n typeof attributes.tone === 'string' ? { 'data-tone': attributes.tone, 'data-callout': calloutLook(attributes.tone) } : {},\n },\n },\n },\n ]\n },\n})\n", "/**\n * What a chip in a document offers its writer \u2014 Open, and its card: none,\n * compact or detailed (SPEC \u00A713.3, \"A reference's card\").\n *\n * **Where it opens** (Miky, 14 Oct): a click on a chip while editing opens the\n * menu instead of following the chip, and a chip just put in \u2014 pasted, picked\n * with `[`, or a pasted URL becoming its chip (`LinkChips`) \u2014 opens it by\n * itself, so the card can be chosen at once. That menu takes no focus: the\n * person is still writing, and typing on closes it with no card. Enter on a\n * selected chip opens it from the keyboard.\n *\n * **Keys while it is open**, handed over by the editor as the `/` menu's are:\n * \u2193 and \u2191 move through the rows (nothing is highlighted until they do, so\n * Enter after a paste still starts a new line), Enter chooses the highlighted\n * row, Escape closes it. Any edit or caret move closes it.\n *\n * Open does what a click on the chip does when reading: the app's own chip\n * gets the click, so it opens where the app opens it. A chip with nothing to\n * click opens its URL in a new tab.\n *\n * Two parts: the extension `ReferenceMenu` holds which chip the menu is open\n * on, and `ReferenceMenuView` draws it beside `EditorContent`. Pair them with\n * `ReferenceNode` and `ReferenceCards`.\n */\nimport { useCallback } from 'react'\nimport { Extension, type Editor } from '@tiptap/core'\nimport { useEditorState } from '@tiptap/react'\nimport { NodeSelection, Plugin, PluginKey, TextSelection, type EditorState, type Transaction } from '@tiptap/pm/state'\nimport { ReplaceStep } from '@tiptap/pm/transform'\nimport type { EditorView } from '@tiptap/pm/view'\nimport { IconCheck } from '@tabler/icons-react'\nimport { CHIPS_MADE } from './LinkChips'\nimport { MenuItem, MenuSection, MenuSeparator } from './Menu'\nimport { Popover } from './Popover'\nimport { setReferenceCardAt, type ReferenceCardLayout } from './ReferenceCards'\n\ninterface MenuState {\n /** The chip's position. */\n pos: number\n /** The highlighted row of `rowsOf`, or -1 for none. */\n active: number\n}\n\nconst key = new PluginKey<MenuState | null>('referenceMenu')\n\nconst LAYOUTS: { layout: ReferenceCardLayout; label: string }[] = [\n { layout: 'none', label: 'None' },\n { layout: 'compact', label: 'Compact' },\n { layout: 'detailed', label: 'Detailed' },\n]\n\n/** A row: Open, or a card. */\ntype Row = 'open' | ReferenceCardLayout\n\n/** The chip's own control to click, or null. */\nfunction chipTarget(view: EditorView, pos: number): HTMLElement | null {\n const dom = view.nodeDOM(pos)\n return dom instanceof HTMLElement ? dom.querySelector<HTMLElement>('a[href], button') : null\n}\n\n/** An http(s) URL chip's URL, or ''. */\nfunction webHref(view: EditorView, pos: number): string {\n const href = view.state.doc.nodeAt(pos)?.attrs.href\n return typeof href === 'string' && /^https?:\\/\\//i.test(href) ? href : ''\n}\n\n/**\n * The menu's rows: Open when there is something to open \u2014 the chip's own\n * control, or its URL \u2014 and the cards when the chip names something.\n */\nfunction rowsOf(view: EditorView, pos: number): Row[] {\n const open: Row[] = chipTarget(view, pos) || webHref(view, pos) ? ['open'] : []\n return view.state.doc.nodeAt(pos)?.attrs.uri ? [...open, ...LAYOUTS.map((card) => card.layout)] : open\n}\n\n/** A chip node, or null. */\nfunction chipAt(state: EditorState, pos: number) {\n const node = state.doc.nodeAt(pos)\n return node && node.type.name === 'reference' ? node : null\n}\n\n/**\n * The chip this transaction just put right before the caret \u2014 a paste, a `[`\n * pick (which adds a space after it), or a URL becoming its chip \u2014 or null.\n * An undo, and a document loaded over the editor, put nothing in.\n */\nfunction justPlaced(tr: Transaction, state: EditorState): number | null {\n if (!tr.docChanged || tr.getMeta('history$') || tr.getMeta('preventUpdate')) return null\n // A whole document set over the editor \u2014 someone else's edit loaded \u2014 is not something the person put in.\n const whole = tr.steps.some((step) => step instanceof ReplaceStep && step.from === 0 && step.to === tr.before.content.size)\n if (whole && tr.getMeta('uiEvent') !== 'paste') return null\n // A URL becoming its chip opens the menu only when it was pasted, with the caret still right after it.\n const made = tr.getMeta(CHIPS_MADE) as { pasted: boolean } | undefined\n if (made && !made.pasted) return null\n const { selection } = state\n if (!selection.empty) return null\n let at = selection.from\n let before = selection.$from.nodeBefore\n if (!made && before?.isText && before.text === ' ') {\n at -= 1\n before = state.doc.resolve(at).nodeBefore\n }\n if (!before || before.type.name !== 'reference' || !before.attrs.uri) return null\n const pos = at - before.nodeSize\n const old = tr.mapping.invert().map(pos)\n const was = tr.before.nodeAt(old)\n // The same chip as before the step \u2014 a key pressed after it, a card set on it \u2014 is not new. One put in just before\n // an identical chip pushes that chip along, so it no longer maps onto `pos` (code review).\n if (was && was.type.name === 'reference' && was.attrs.uri === before.attrs.uri && tr.mapping.map(old, 1) === pos) return null\n return pos\n}\n\nfunction dispatchMenu(view: EditorView, next: MenuState | null): void {\n view.dispatch(view.state.tr.setMeta(key, next))\n}\n\n// True while Open hands the click to the chip, so the chip's own click is not taken for a click that opens the menu.\nlet passing = false\n\n/** Opens what the chip at `pos` names, as a click on it does when reading. */\nfunction openChip(view: EditorView, pos: number): void {\n const target = chipTarget(view, pos)\n if (target) {\n passing = true\n try {\n target.click()\n } finally {\n passing = false\n }\n return\n }\n const href = webHref(view, pos)\n if (href) window.open(href, '_blank', 'noopener,noreferrer')\n}\n\n/** Runs `row` of the menu open on the chip at `pos`, and closes it. */\nfunction choose(editor: Editor, pos: number, row: Row | undefined): void {\n dispatchMenu(editor.view, null)\n if (row === 'open') openChip(editor.view, pos)\n else if (row) setReferenceCardAt(editor, pos, row)\n}\n\n/** Whether the menu is open, for an editor's own Escape: while it is, Escape closes it rather than leaving. */\nexport function isReferenceMenuOpen(state: EditorState): boolean {\n return !!key.getState(state)\n}\n\n/**\n * A click on the chip at `pos`, from its node view: while editing, with the\n * menu installed, it opens the menu, and the caller stops the click there.\n * Returns false when the click should go on to the chip.\n */\nexport function referenceMenuClick(editor: Editor, pos: number): boolean {\n if (passing || editor.isDestroyed || !editor.isEditable || key.getState(editor.state) === undefined) return false\n // A chip with nothing to open and no card to choose has no menu: the click goes on to it.\n if (!chipAt(editor.state, pos) || rowsOf(editor.view, pos).length === 0) return false\n editor.view.dispatch(caretAfter(editor.state, pos).setMeta(key, { pos, active: -1 }))\n return true\n}\n\n/**\n * The caret right after the chip, as a paste leaves it: a selected chip would\n * bring up the selection toolbar beside the menu, and typing would replace it.\n */\nfunction caretAfter(state: EditorState, pos: number): Transaction {\n const node = state.doc.nodeAt(pos)\n return state.tr.setSelection(TextSelection.create(state.doc, pos + (node?.nodeSize ?? 1)))\n}\n\n/** Which chip the menu is open on: see the module comment. */\nexport const ReferenceMenu = Extension.create({\n name: 'referenceMenu',\n // Before the editor's own keys, so Enter on a highlighted row is the row's, not a new line.\n priority: 1000,\n addProseMirrorPlugins() {\n const editor = this.editor\n return [\n new Plugin<MenuState | null>({\n key,\n state: {\n init: () => null,\n apply: (tr, value, before, after) => {\n const meta = tr.getMeta(key) as MenuState | null | undefined\n if (meta !== undefined) return meta\n const placed = justPlaced(tr, after)\n if (placed !== null) return { pos: placed, active: -1 }\n if (!value) return null\n const pos = tr.mapping.map(value.pos)\n if (!chipAt(after, pos)) return null\n // What another plugin appends \u2014 an id, a trailing line \u2014 is not the person typing on, and nor is a URL\n // elsewhere becoming its chip when its answer comes back (code review).\n if (tr.docChanged && !tr.getMeta('appendedTransaction') && tr.getMeta(CHIPS_MADE) === undefined) return null\n const onChip = after.selection instanceof NodeSelection && after.selection.from === pos\n if (tr.selectionSet && !after.selection.eq(before.selection) && !onChip) return null\n return pos === value.pos ? value : { ...value, pos }\n },\n },\n props: {\n handleDOMEvents: {\n // Focus gone, the keys no longer reach the menu, and a card chosen now would miss the save on leaving (code review).\n blur: (view) => {\n if (key.getState(view.state)) dispatchMenu(view, null)\n return false\n },\n },\n handleKeyDown: (view, event) => {\n // With a modifier, a key is the editor's: Shift+\u2193 extends the selection, Mod+Enter is not a pick.\n if (event.shiftKey || event.ctrlKey || event.metaKey || event.altKey) return false\n const open = key.getState(view.state)\n if (!open) {\n // Enter on a selected chip: the menu, from the keyboard, its first row highlighted.\n const { selection } = view.state\n if (event.key !== 'Enter' || !view.editable || !(selection instanceof NodeSelection) || selection.node.type.name !== 'reference') return false\n if (rowsOf(view, selection.from).length === 0) return false\n view.dispatch(caretAfter(view.state, selection.from).setMeta(key, { pos: selection.from, active: 0 }))\n return true\n }\n const rows = rowsOf(view, open.pos)\n if (event.key === 'Escape') {\n dispatchMenu(view, null)\n return true\n }\n if (rows.length === 0) return false\n if (event.key === 'ArrowDown') {\n dispatchMenu(view, { ...open, active: (open.active + 1) % rows.length })\n return true\n }\n if (event.key === 'ArrowUp') {\n dispatchMenu(view, { ...open, active: open.active <= 0 ? rows.length - 1 : open.active - 1 })\n return true\n }\n if (event.key === 'Enter' && open.active >= 0) {\n choose(editor, open.pos, rows[open.active])\n return true\n }\n return false\n },\n },\n }),\n ]\n },\n})\n\n/** What the panel draws: the chip the menu is open on, and its card. */\ninterface Shown {\n pos: number\n active: number\n named: boolean\n layout: ReferenceCardLayout\n}\n\nexport interface ReferenceMenuViewProps {\n editor: Editor | null\n}\n\n/** The menu `ReferenceMenu` holds, hung under its chip. Render it always, beside `EditorContent`. */\nexport function ReferenceMenuView({ editor }: ReferenceMenuViewProps) {\n const shown = useEditorState({\n editor,\n selector: ({ editor: ed }): Shown | null => {\n if (!ed || ed.isDestroyed) return null\n const open = key.getState(ed.state)\n if (!open) return null\n const node = chipAt(ed.state, open.pos)\n if (!node) return null\n const embed = node.attrs.embed\n // An unknown layout draws compact (\u00A713.3), so it reads as compact here.\n const layout: ReferenceCardLayout = embed === 'detailed' ? 'detailed' : typeof embed === 'string' && embed !== '' && embed !== 'none' ? 'compact' : 'none'\n return { pos: open.pos, active: open.active, named: !!node.attrs.uri, layout }\n },\n })\n // A press anywhere in the panel keeps focus in the text, as a row's does: the editor saves and ends its menus when it loses focus.\n const keepFocus = useCallback((list: HTMLDivElement | null) => {\n const panel = list?.closest('[role=\"dialog\"]')\n if (!panel) return\n const keep = (event: Event) => event.preventDefault()\n panel.addEventListener('mousedown', keep)\n return () => panel.removeEventListener('mousedown', keep)\n }, [])\n\n const live = shown && editor && !editor.isDestroyed ? editor : null\n const dom = live && shown ? live.view.nodeDOM(shown.pos) : null\n const anchor = dom instanceof HTMLElement ? dom : null\n const rows = live && shown ? rowsOf(live.view, shown.pos) : []\n const opens = rows[0] === 'open'\n const run = (row: Row) => {\n if (editor && shown) choose(editor, shown.pos, row)\n }\n\n return (\n // finalFocus={false}: focus never left the text.\n <Popover\n anchor={anchor}\n open={!!shown && !!anchor && rows.length > 0}\n onOpenChange={(next) => {\n if (!next && editor && !editor.isDestroyed && key.getState(editor.state)) dispatchMenu(editor.view, null)\n }}\n side=\"bottom\"\n align=\"left\"\n ariaLabel=\"Link\"\n finalFocus={false}\n >\n {shown && (\n <div ref={keepFocus} role=\"menu\" aria-label=\"Link\" className=\"flex flex-col gap-0.5\">\n {/* Only when there is something to open: the chip's own control, or its URL (code review). */}\n {opens && (\n <MenuItem\n role=\"menuitem\"\n tabIndex={-1}\n label=\"Open\"\n selected={shown.active === 0}\n onMouseDown={(event) => {\n event.preventDefault()\n run('open')\n }}\n />\n )}\n {shown.named && (\n <>\n {opens && <MenuSeparator />}\n <div role=\"group\" aria-label=\"Card\">\n <MenuSection label=\"Card\">\n {LAYOUTS.map(({ layout, label }, i) => (\n <MenuItem\n key={layout}\n role=\"menuitemradio\"\n aria-checked={shown.layout === layout}\n tabIndex={-1}\n label={label}\n selected={shown.active === i + (opens ? 1 : 0)}\n trailing={shown.layout === layout ? <IconCheck size={16} aria-hidden /> : undefined}\n onMouseDown={(event) => {\n event.preventDefault()\n run(layout)\n }}\n />\n ))}\n </MenuSection>\n </div>\n </>\n )}\n </div>\n )}\n </Popover>\n )\n}\n", "/**\n * An app URL in a document reads as the chip of what it names \u2014 SPEC \u00A713.3,\n * \"A reference's card\", and \u00A77.7.\n *\n * The URL stays the URL: the chip is the `reference` node with `href` set (a\n * URL chip, `ReferenceNode`), which a document writes back as the URL it was,\n * plain text with no card and a `link` mark with one. Never a `nostr:`\n * reference in its place: the URL is how a person addresses the object.\n *\n * What a URL names is the app's to say \u2014 its manifests and its relay \u2014 so the\n * app hands this extension two functions: `referenceOf`, what it has resolved\n * so far, and `ask`, to look up the URLs it has not. A URL that names nothing,\n * or whose answer has not come back, stays text; when `ask` settles the\n * document is looked at again.\n *\n * The URL being typed stays text while the caret is in it or at its end, so a\n * half-typed address never turns into a chip under the person's fingers. A\n * pasted URL is whole, so it is asked about and made a chip at once, the caret\n * still at its end \u2014 where `ReferenceMenu` opens its menu.\n */\nimport { Extension, type Editor } from '@tiptap/core'\nimport { closeHistory } from '@tiptap/pm/history'\nimport type { Node as PmNode } from '@tiptap/pm/model'\nimport { Plugin, PluginKey, type EditorState, type Selection, type Transaction } from '@tiptap/pm/state'\n\nexport interface LinkChipsOptions {\n /**\n * The `nostr:` reference an app URL names, from what the app has resolved\n * (\u00A77.7) \u2014 protocol's `referenceOfLink(url, resolveLink)`. Undefined while it\n * names nothing, or its answer has not come back.\n */\n referenceOf: ((url: string) => string | undefined) | null\n /** Look up what these URLs name. When it settles, the document is looked at again. */\n ask: ((urls: string[]) => Promise<unknown> | void) | null\n /**\n * How many URLs one editor asks about in its life, the first ones; the rest\n * stay text. An app's answer can cost a relay query, and a document pasted\n * full of links must not spend the writer's quota. Default 24.\n */\n limit: number\n}\n\ninterface LinkChipsState {\n options: LinkChipsOptions\n /** The last few pastes, mapped through every edit since. */\n pasted: PastedRange[]\n}\n\nconst key = new PluginKey<LinkChipsState>('linkChips')\n\n/** The meta on the transaction that made chips: `{ pasted }`. */\nexport const CHIPS_MADE = 'linkChipsMade'\n\n/** How many pastes are remembered; one is what the person just did. */\nconst PASTES_KEPT = 8\n\n/**\n * A link on words in marker text (skipped), or a bare URL \u2014 the grammar of\n * protocol's `urlRunsIn`, which a reader turns into chips by, so what an editor\n * makes a chip is what a reader would.\n */\nconst LINK_OR_URL = /(\\[[^[\\]\\n]+\\]\\((?:https?:\\/\\/|mailto:)[^\\s)]+\\))|(https?:\\/\\/[^\\s<>\"'`]+)/g\nconst TRAILING = new Set(['.', ',', ';', ':', '!', '?', ')', ']'])\n\n/** Each URL and where it sits; `end` is where its run ends before trailing punctuation is trimmed, which typing may yet continue. */\nfunction urlRuns(text: string): { url: string; from: number; to: number; end: number }[] {\n if (!text.includes('://')) return []\n const out: { url: string; from: number; to: number; end: number }[] = []\n for (const m of text.matchAll(LINK_OR_URL)) {\n if (m[1]) continue\n const start = m.index\n if (text.slice(Math.max(0, start - 2), start) === '](' || text[start - 1] === '[') continue\n let end = m[0].length\n while (end > 0 && TRAILING.has(m[0][end - 1])) end--\n out.push({ url: m[0].slice(0, end), from: start, to: start + end, end: start + m[0].length })\n }\n return out\n}\n\ninterface Found {\n /** Text URLs that name something: made chips. */\n chips: { from: number; to: number; url: string; uri: string; marks: string[]; pasted: boolean }[]\n /** URL chips whose URL is answered now: given their reference. */\n fills: { pos: number; uri: string }[]\n /** Every URL still waiting for an answer, or naming nothing. */\n open: string[]\n}\n\n/** Where something was pasted: a URL wholly inside one was not typed, so the caret at its end does not hold it as text. */\nexport interface PastedRange {\n from: number\n to: number\n}\n\n/**\n * What to change in a document, and what to ask. `selection` null treats no\n * URL as being typed; a URL inside one of `pasted` is never being typed.\n */\nexport function findLinkChips(doc: PmNode, selection: Selection | null, referenceOf: LinkChipsOptions['referenceOf'], pasted: readonly PastedRange[] = []): Found {\n const found: Found = { chips: [], fills: [], open: [] }\n const open = new Set<string>()\n doc.descendants((node, pos) => {\n // A fence is literal (\u00A713.1): its URLs are text.\n if (node.type.spec.code) return false\n if (node.type.name === 'reference') {\n const href = typeof node.attrs.href === 'string' ? node.attrs.href : ''\n if (href && !node.attrs.uri) {\n const uri = referenceOf?.(href)\n if (uri) found.fills.push({ pos, uri })\n else open.add(href)\n }\n return false\n }\n if (!node.isText || !node.text) return true\n // A URL in code is literal.\n if (node.marks.some((m) => m.type.spec.code || m.type.name === 'code')) return false\n // A link on words is the link's. A link whose text is its own URL \u2014 an editor's autolink, or its paste rule \u2014 is\n // that URL, as a document reads one (protocol's `inlineToEditor`): its chip, with the link mark left behind.\n const link = node.marks.find((m) => m.type.name === 'link')\n if (link && !(link.attrs.href === node.text && /^https?:\\/\\//.test(node.text))) return false\n const runs = link ? [{ url: node.text, from: 0, to: node.text.length, end: node.text.length }] : urlRuns(node.text)\n for (const run of runs) {\n const from = pos + run.from\n const to = pos + run.to\n // Being typed or selected: the caret inside it or at its end (`https://a.` is still being typed), or a\n // selection over any of it. A caret before it is not.\n // A pasted URL is whole: it becomes its chip with the caret still at its end, so its menu can open at once.\n const wasPasted = pasted.some((range) => range.from <= from && pos + run.end <= range.to)\n const typing = selection && selection.to > from && selection.from <= pos + run.end\n if (typing && !(selection.empty && wasPasted)) continue\n const uri = referenceOf?.(run.url)\n if (uri) found.chips.push({ from, to, url: run.url, uri, marks: node.marks.filter((m) => m !== link).map((m) => m.type.name), pasted: wasPasted })\n else open.add(run.url)\n }\n return false\n })\n found.open = [...open]\n return found\n}\n\nfunction apply(state: EditorState, found: Found): Transaction | null {\n if (!found.chips.length && !found.fills.length) return null\n const type = state.schema.nodes.reference\n if (!type) return null\n const tr = state.tr\n // A fill keeps the node's size, so the chips' positions still hold; the chips go last first.\n for (const { pos, uri } of found.fills) {\n const node = tr.doc.nodeAt(pos)\n if (node) tr.setNodeMarkup(pos, undefined, { ...node.attrs, uri })\n }\n for (const chip of [...found.chips].reverse()) {\n tr.replaceWith(chip.from, chip.to, type.create({ uri: chip.uri, href: chip.url, marks: chip.marks }))\n }\n // Whether a pasted URL became its chip: `ReferenceMenu` opens on that one, never on a URL typed by hand.\n const pasted = found.chips.some((chip) => chip.pasted)\n tr.setMeta(CHIPS_MADE, { pasted })\n // A pasted URL's chip is its own undo step, so Ctrl+Z gives the URL back rather than taking the paste away (OPT-19,\n // as Peek's pasted link did). Any other chip is not a step of the person's: undo takes back what they typed.\n return pasted ? closeHistory(tr) : tr.setMeta('addToHistory', false)\n}\n\n/**\n * Look at the document again \u2014 after the app learned what more URLs name some\n * other way. Dispatches only when there is a chip to make: an empty\n * transaction would wake every other plugin (a trailing paragraph appears).\n */\nexport function refreshLinkChips(editor: Editor): void {\n if (editor.isDestroyed) return\n const chips = key.getState(editor.state)\n if (!chips) return\n const tr = apply(editor.state, findLinkChips(editor.state.doc, editor.state.selection, chips.options.referenceOf, chips.pasted))\n if (tr) editor.view.dispatch(tr)\n}\n\n/**\n * Turns the app URLs in a document into URL chips. Pair it with `ReferenceNode`;\n * `ReferenceCards` then draws a card a URL chip asks for.\n */\nexport const LinkChips = Extension.create<LinkChipsOptions>({\n name: 'linkChips',\n addOptions() {\n return { referenceOf: null, ask: null, limit: 24 }\n },\n addProseMirrorPlugins() {\n const options = this.options\n const editor = this.editor\n return [\n new Plugin<LinkChipsState>({\n key,\n // The options, where `refreshLinkChips` can read them from the state alone, and where the last pastes landed.\n state: {\n init: () => ({ options, pasted: [] }),\n apply: (tr, value, before, after) => {\n if (!tr.docChanged) return value\n // A paste edited inside is being typed again: forgotten, so each keystroke in it is not an ask (security review).\n const pasted = value.pasted\n .map((range) => ({ from: tr.mapping.map(range.from, 1), to: tr.mapping.map(range.to, -1), size: range.to - range.from }))\n .filter((range) => range.from < range.to && range.to - range.from === range.size)\n .map(({ from, to }) => ({ from, to }))\n if (tr.getMeta('uiEvent') === 'paste') {\n const from = tr.mapping.map(before.selection.from, -1)\n if (from < after.selection.to) pasted.push({ from, to: after.selection.to })\n }\n return { options, pasted: pasted.slice(-PASTES_KEPT) }\n },\n },\n appendTransaction: (trs, _old, state) => {\n if (!trs.some((tr) => tr.docChanged || tr.selectionSet)) return null\n return apply(state, findLinkChips(state.doc, state.selection, options.referenceOf, key.getState(state)?.pasted))\n },\n view: (view) => {\n // Each URL is asked once; one whose ask failed is forgotten, so a later look asks again.\n const asked = new Set<string>()\n // The URL being typed is not asked about: each prefix of it would spend one of `limit`, and a query.\n const look = (state: EditorState) => {\n if (!options.ask) return\n const { open } = findLinkChips(state.doc, state.selection, options.referenceOf, key.getState(state)?.pasted)\n // The limit counts the URLs the document still holds unanswered: a message box lives across many messages,\n // and one that counted every URL it was ever asked would stop making chips after a day's links (OPT-19).\n const waiting = new Set(open)\n for (const url of asked) if (!waiting.has(url)) asked.delete(url)\n const fresh = open.filter((url) => !asked.has(url)).slice(0, Math.max(0, options.limit - asked.size))\n if (!fresh.length) return\n for (const url of fresh) asked.add(url)\n const forget = () => {\n for (const url of fresh) asked.delete(url)\n }\n let answer: Promise<unknown> | void\n try {\n answer = options.ask(fresh)\n } catch {\n forget()\n return\n }\n void Promise.resolve(answer).then(() => refreshLinkChips(editor), forget)\n }\n look(view.state)\n // Once the view is up, so a dispatch lands: what the app already knows turns into chips on open.\n queueMicrotask(() => {\n try {\n refreshLinkChips(editor)\n } catch {\n // Unmounted before it ran.\n }\n })\n return {\n update: (next, prev) => {\n // A caret leaving a URL finishes it, so a move is looked at as an edit is.\n if (next.state.doc !== prev.doc || !next.state.selection.eq(prev.selection)) look(next.state)\n },\n }\n },\n }),\n ]\n },\n})\n", "/**\n * The cards a block document's writer chose, drawn after the line that holds\n * each reference \u2014 SPEC \u00A713.3, \"A reference's card\".\n *\n * The choice is `embed` on the `reference` node (`ReferenceNode`), so the card\n * moves and goes with its chip. What a card looks like is the app's \u2014 it knows\n * how to resolve an object and draw its row or its card \u2014 so this extension\n * draws nothing itself: after every textblock whose chips ask for a card it\n * places one empty, non-editable slot, and `useReferenceCards` hands the app\n * those slots to portal its cards into. A portal, rather than a React root per\n * slot, keeps the app's providers around the card.\n *\n * **A slot keeps its element while its block lives.** It is keyed by the\n * nearest block id (the `blockId` attribute), so typing above a card does not\n * re-make its element, and the card does not reload on every keystroke.\n *\n * The slot sits at the end of the textblock, inside it: a paragraph's card is\n * under the paragraph, a list item's under the item at its indent, a table\n * cell's in the cell.\n */\nimport { Extension, type Editor } from '@tiptap/core'\nimport type { Node as PmNode } from '@tiptap/pm/model'\nimport { NodeSelection, Plugin, PluginKey, type EditorState } from '@tiptap/pm/state'\nimport { Decoration, DecorationSet } from '@tiptap/pm/view'\nimport { useEffect, useState } from 'react'\n\n/** A layout a writer can choose; `none` is no card. */\nexport type ReferenceCardLayout = 'none' | 'compact' | 'detailed'\n\n/** One card to draw: the reference's `nostr:` URI and how much to show. */\nexport interface ReferenceCard {\n uri: string\n layout: Exclude<ReferenceCardLayout, 'none'>\n}\n\n/** One slot: where an app draws the cards of one line. */\nexport interface ReferenceCardSlot {\n key: string\n element: HTMLElement\n cards: ReferenceCard[]\n}\n\ninterface Placement {\n key: string\n /** The end of the textblock's content, where the slot goes. */\n pos: number\n cards: ReferenceCard[]\n}\n\n/** `embed` as a node holds it: an unknown string draws as compact (\u00A713.3), anything else is none. */\nfunction layoutOf(raw: unknown): ReferenceCard['layout'] | undefined {\n if (typeof raw !== 'string' || raw === '' || raw === 'none') return undefined\n return raw === 'detailed' ? 'detailed' : 'compact'\n}\n\n/** Every line that asks for a card, in document order. */\nexport function referenceCardPlacements(doc: PmNode): Placement[] {\n const out: Placement[] = []\n const seen = new Map<string, number>()\n doc.descendants((node, pos, parent) => {\n if (!node.isTextblock) return true\n const cards: ReferenceCard[] = []\n node.forEach((child) => {\n if (child.type.name !== 'reference') return\n const layout = layoutOf(child.attrs.embed)\n // A URL chip whose URL is not resolved yet (`LinkChips`) has nothing to draw a card of, yet.\n if (layout && child.attrs.uri) cards.push({ uri: String(child.attrs.uri), layout })\n })\n if (cards.length) {\n // The textblock's own id, or its list item's or cell's: the nearest one.\n const id = node.attrs.blockId ?? parent?.attrs.blockId\n const base = id ? `b:${String(id)}` : `p:${pos}`\n const n = seen.get(base) ?? 0\n seen.set(base, n + 1)\n out.push({ key: n ? `${base}:${n}` : base, pos: pos + node.nodeSize - 1, cards })\n }\n return false\n })\n return out\n}\n\ninterface ReferenceCardsStorage {\n /** One element per slot key, made once and reused, so the app's portal stays put. */\n elements: Map<string, HTMLElement>\n}\n\nconst key = new PluginKey<DecorationSet>('referenceCards')\n\nfunction elementFor(storage: ReferenceCardsStorage, slotKey: string): HTMLElement {\n let element = storage.elements.get(slotKey)\n if (!element) {\n element = document.createElement('div')\n element.setAttribute('data-reference-cards', slotKey)\n element.setAttribute('contenteditable', 'false')\n storage.elements.set(slotKey, element)\n }\n return element\n}\n\nfunction storageOf(editor: Editor): ReferenceCardsStorage | undefined {\n return (editor.storage as unknown as { referenceCards?: ReferenceCardsStorage }).referenceCards\n}\n\n/**\n * Places a slot after each line whose chips ask for a card. Pair it with\n * `ReferenceNode`, and draw the cards with `useReferenceCards`.\n */\nexport const ReferenceCards = Extension.create<Record<string, never>, ReferenceCardsStorage>({\n name: 'referenceCards',\n addStorage() {\n return { elements: new Map() }\n },\n addProseMirrorPlugins() {\n const storage = this.storage\n const decorate = (doc: PmNode) =>\n DecorationSet.create(\n doc,\n referenceCardPlacements(doc).map((p) =>\n Decoration.widget(p.pos, () => elementFor(storage, p.key), {\n key: p.key,\n side: 1,\n ignoreSelection: true,\n // A click on a card is the card's, not a caret placement in the line.\n stopEvent: () => true,\n }),\n ),\n )\n return [\n new Plugin<DecorationSet>({\n key,\n state: {\n init: (_, state) => decorate(state.doc),\n apply: (tr, set) => (tr.docChanged ? decorate(tr.doc) : set),\n },\n props: { decorations: (state) => key.getState(state) },\n }),\n ]\n },\n})\n\n/**\n * The slots to draw cards into, kept current as the document changes. Draw\n * each with `createPortal(<cards/>, slot.element)`, keyed by `slot.key`.\n */\nexport function useReferenceCards(editor: Editor | null): ReferenceCardSlot[] {\n const [slots, setSlots] = useState<ReferenceCardSlot[]>([])\n useEffect(() => {\n if (!editor) return\n const storage = storageOf(editor)\n if (!storage) return\n let last = ''\n const read = () => {\n if (editor.isDestroyed) return\n const next = referenceCardPlacements(editor.state.doc).map((p) => ({ key: p.key, element: elementFor(storage, p.key), cards: p.cards }))\n // Only a change in what is drawn re-renders the app; typing does not.\n const signature = JSON.stringify(next.map((s) => [s.key, s.cards]))\n if (signature === last) return\n last = signature\n const live = new Set(next.map((s) => s.key))\n for (const k of storage.elements.keys()) if (!live.has(k)) storage.elements.delete(k)\n setSlots(next)\n }\n read()\n editor.on('transaction', read)\n return () => {\n editor.off('transaction', read)\n }\n }, [editor])\n return slots\n}\n\n/**\n * The chip the person has selected, with what it names and its card \u2014 or null.\n *\n * Selected as a node (a click, where a click on the chip does not follow its\n * link), or inside a selected range that holds that one chip and nothing but\n * spaces besides (Shift+arrow over it, or a drag across it): an app whose chip\n * is a link has no other way to select it.\n */\nexport function referenceAtSelection(state: EditorState): { pos: number; uri: string; layout: ReferenceCardLayout } | null {\n const { selection } = state\n let found: { pos: number; node: PmNode } | null = null\n if (selection instanceof NodeSelection) {\n if (selection.node.type.name === 'reference') found = { pos: selection.from, node: selection.node }\n } else if (!selection.empty) {\n let only = true\n state.doc.nodesBetween(selection.from, selection.to, (node, pos) => {\n if (node.type.name === 'reference') {\n if (found) only = false\n found = { pos, node }\n return false\n }\n if (node.isText) {\n const from = Math.max(selection.from, pos) - pos\n const to = Math.min(selection.to, pos + node.nodeSize) - pos\n if ((node.text ?? '').slice(from, to).trim() !== '') only = false\n }\n return true\n })\n if (!only) found = null\n }\n if (!found) return null\n const { pos, node } = found as { pos: number; node: PmNode }\n // A URL chip that names nothing yet has no card to choose.\n if (!node.attrs.uri) return null\n return { pos, uri: String(node.attrs.uri ?? ''), layout: layoutOf(node.attrs.embed) ?? 'none' }\n}\n\n/** Sets the card of the selected chip. `none` removes it. The selection stays as it was. */\nexport function setReferenceCard(editor: Editor, layout: ReferenceCardLayout): boolean {\n const at = referenceAtSelection(editor.state)\n return at ? setReferenceCardAt(editor, at.pos, layout) : false\n}\n\n/** Sets the card of the chip at `pos` \u2014 one that names something. `none` removes it. The selection stays as it was. */\nexport function setReferenceCardAt(editor: Editor, pos: number, layout: ReferenceCardLayout): boolean {\n const node = editor.state.doc.nodeAt(pos)\n if (!node || node.type.name !== 'reference' || !node.attrs.uri) return false\n const wasNode = editor.state.selection instanceof NodeSelection && editor.state.selection.from === pos\n // Same size, so a range maps onto itself; a node selection is made again, since the node is replaced.\n const tr = editor.state.tr.setNodeMarkup(pos, undefined, { ...node.attrs, embed: layout === 'none' ? null : layout })\n if (wasNode) tr.setSelection(NodeSelection.create(tr.doc, pos))\n editor.view.dispatch(tr)\n return true\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["Extension", "Plugin", "PluginKey", "useRef", "jsx", "Popup", "jsx", "jsxs", "useCallback", "useEffect", "useRef", "useState", "IconTrash", "MenuItem", "Fragment", "jsx", "jsxs", "useRef", "useState", "useCallback", "useEffect", "IconTrash", "Extension", "PluginKey", "jsx", "jsxs", "Extension", "PluginKey", "named", "Extension", "Node", "Plugin", "PluginKey", "NodeViewWrapper", "ReactNodeViewRenderer", "ReactRenderer", "useCallback", "Extension", "NodeSelection", "Plugin", "PluginKey", "TextSelection", "Extension", "Plugin", "PluginKey", "MenuItem", "MenuSeparator", "Popover", "Extension", "Plugin", "PluginKey", "Decoration", "DecorationSet", "useEffect", "useState", "key", "node", "pos", "Fragment", "jsx", "jsxs", "key", "PluginKey", "TextSelection", "Extension", "Plugin", "NodeSelection", "keepFocus", "useCallback", "Popover", "MenuItem", "MenuSeparator", "jsx", "at", "Extension", "Plugin", "PluginKey", "tr", "Node", "ReactNodeViewRenderer", "NodeViewWrapper", "key", "ReactRenderer", "listed"]
|
|
7
7
|
}
|