{"version":3,"file":"index.mjs","names":["keymap","EditorSelection","styles","EditorView","keymap","EditorView","EditorView","EditorView","keymap","__dxlog_file","__dxlog_file","A","A","EditorView","__dxlog_file","styles","EditorView","__dxlog_file","styles","keymap","EditorView","__dxlog_file","EditorView","EditorSelection","keymap","EditorView","EditorView","__dxlog_file","EditorView","EditorView","EditorView","styles","EditorView","autoScroll","autoScrollExtension","__dxlog_file","EditorView","EditorState","placeholder","keymap","keymap","styles","EditorView","__dxlog_file","EditorView","EditorView","styles","EditorView","styles","EditorView","EditorView","__dxlog_file","EditorView","keymap","keymap","tokenTable","tags","PUNCTUATION","token","jsonLanguage","keymap","EditorSelection","snippets","EditorView","mermaidLanguage","tags","keymap","EditorView","styles","keymap","DEFAULTS","EditorState","EditorView","__dxlog_file","__dxlog_file","buildDecorations","EditorView","keymap","buildDecorations","EditorView","EditorView","EditorView","__dxlog_file","EditorView","EditorView","keymap","tags","styles","EditorView","__dxlog_file","EditorView","EditorView","EditorView","keymap","BOX_INSET_X","BOX_PADDING","EditorView","keymap","mergeRanges","PlaceholderWidget","EditorView","mergeRanges","deleteRanges","moveBlocks","replaceBlocks","mx","styles","EditorView","styles","EditorView","__dxlog_file","EditorSelection","keymap","EditorSelection","EditorView","EditorSelection","EditorState","EditorView","EditorView","EditorView","EditorView","EditorView"],"sources":["../../src/defaults.ts","../../src/extensions/behavior/bookmarks.ts","../../src/extensions/behavior/dnd.ts","../../src/extensions/behavior/inline-edit.ts","../../src/util/decorations.ts","../../src/util/doc-sync.ts","../../src/util/dom.ts","../../src/util/util.ts","../../src/extensions/behavior/listener.ts","../../src/extensions/behavior/spacing.ts","../../src/extensions/behavior/submit.ts","../../src/extensions/collab/automerge/cursor.ts","../../src/extensions/collab/automerge/defs.ts","../../src/extensions/collab/automerge/update-automerge.ts","../../src/extensions/collab/automerge/update-codemirror.ts","../../src/extensions/collab/automerge/sync.ts","../../src/extensions/collab/automerge/automerge.ts","../../src/extensions/collab/awareness/awareness.ts","../../src/extensions/collab/awareness/awareness-provider.ts","../../src/extensions/completion/autocomplete.ts","../../src/extensions/completion/match.ts","../../src/extensions/completion/mention.ts","../../src/extensions/state/busy.ts","../../src/extensions/completion/placeholder.ts","../../src/extensions/completion/typeahead.ts","../../src/styles/theme.ts","../../src/extensions/state/focus.ts","../../src/extensions/streaming/scrolling/crawler.ts","../../src/extensions/streaming/scrolling/auto-scroll.ts","../../src/extensions/streaming/scrolling/scroll-past-end.ts","../../src/extensions/streaming/scrolling/scrollbar-autohide.ts","../../src/extensions/streaming/scrolling/scroller.ts","../../src/extensions/core/factories.ts","../../src/extensions/core/modes.ts","../../src/extensions/debug/debug.ts","../../src/extensions/decoration/annotations.ts","../../src/extensions/decoration/assistant.ts","../../src/extensions/decoration/fader.ts","../../src/extensions/decoration/hashtag.ts","../../src/extensions/decoration/marker.ts","../../src/extensions/decoration/pos.ts","../../src/extensions/demo/blast.ts","../../src/extensions/demo/snippets.ts","../../src/extensions/language/datalog/datalog.ts","../../src/extensions/language/json/json.ts","../../src/extensions/language/markdown/formatting.ts","../../src/extensions/language/markdown/action.ts","../../src/extensions/language/mermaid/highlight.ts","../../src/extensions/language/mermaid/mermaid.ts","../../src/extensions/language/markdown/highlight.ts","../../src/extensions/language/markdown/bundle.ts","../../src/extensions/language/markdown/debug.ts","../../src/extensions/streaming/pending/pending-text.ts","../../src/extensions/streaming/pending/pending-text-stream.ts","../../src/extensions/streaming/typewriter.ts","../../src/extensions/widgets/stub.ts","../../src/extensions/widgets/widgets.ts","../../src/extensions/widgets/link-widgets.ts","../../src/extensions/language/markdown/changes.ts","../../src/extensions/language/markdown/image.ts","../../src/extensions/language/markdown/styles.ts","../../src/extensions/language/markdown/table.ts","../../src/extensions/language/markdown/decorate.ts","../../src/extensions/language/markdown/link.ts","../../src/extensions/language/markdown/substitutions.ts","../../src/extensions/language/turtle/turtle.ts","../../src/extensions/language/xml/extended-markdown.ts","../../src/extensions/language/xml/xml-block-decoration.ts","../../src/extensions/language/xml/xml-formatting.ts","../../src/extensions/widgets/anchor.ts","../../src/extensions/widgets/object-links.ts","../../src/extensions/language/xml/xml-util.ts","../../src/extensions/language/xml/xml-tags.ts","../../src/extensions/review/change-bar.ts","../../src/extensions/review/suggest.ts","../../src/extensions/review/suggestions-overlay.ts","../../src/extensions/review/track-changes.ts","../../src/extensions/state/modal.ts","../../src/extensions/structure/blocks/selection.ts","../../src/extensions/structure/blocks/drag.ts","../../src/extensions/structure/blocks/blocks.ts","../../src/extensions/structure/folding/folding.ts","../../src/extensions/structure/folding/turns.ts","../../src/extensions/structure/outliner/tree.ts","../../src/extensions/structure/outliner/dnd.ts","../../src/extensions/structure/outliner/commands.ts","../../src/extensions/structure/outliner/ghost.ts","../../src/extensions/structure/outliner/editor.ts","../../src/extensions/structure/outliner/menu.ts","../../src/extensions/structure/outliner/outliner.ts","../../src/extensions/walkthrough/diff-parser.ts","../../src/extensions/walkthrough/highlight.ts","../../src/extensions/walkthrough/theme.ts","../../src/extensions/walkthrough/diff-block.ts","../../src/extensions/walkthrough/sidebar.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport { mx } from '@dxos/ui-theme';\n\nimport { type ThemeExtensionsOptions } from './extensions/index.ts';\n\nexport const editorClassNames = (role?: string) =>\n  mx(\n    'dx-attention-surface data-[toolbar=disabled]:pt-2 dx-focus-ring-inset',\n    role === 'org.dxos.role.section'\n      ? '[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-h-24'\n      : 'dx-expand overflow-hidden',\n  );\n\nexport const documentSlots: ThemeExtensionsOptions['slots'] = {\n  content: {\n    /**\n     * CodeMirror content width.\n     * 40rem = 640px. Corresponds to initial plank width (Google docs, Stashpad, etc.)\n     * 50rem = 800px. Maximum content width for solo mode.\n     * NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).\n     */\n    className: mx(\n      // Inline-size container for widget sizing (children use `max-w-[100cqi]`).\n      // NOTE: Use inline-size, not full size containment — `container-type: size` on the\n      // editor content breaks CodeMirror's viewport measurement, leaving blank gaps during\n      // scroll until a click forces a re-measure.\n      'dx-container-type-inline-size',\n      // Wider margin for web (vs. mobile).\n      'pointer-fine:max-w-[min(50rem,100%-4rem)] pointer-coarse:max-w-[min(50rem,100%-2rem)]',\n      'mx-auto! w-full py-3!',\n    ),\n  },\n};\n\nexport const compactSlots: ThemeExtensionsOptions['slots'] = {\n  content: {\n    className: 'mx-2!',\n  },\n};\n\nexport const mobileSlots: ThemeExtensionsOptions['slots'] = {\n  content: {\n    className: 'mx-2!',\n  },\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { EditorSelection, type Extension, Prec, StateEffect, StateField } from '@codemirror/state';\nimport { type EditorView, keymap } from '@codemirror/view';\n\ntype Bookmark = {\n  id: string;\n  pos: number;\n  name: string;\n};\n\nexport const addBookmark = StateEffect.define<Bookmark>();\nexport const removeBookmark = StateEffect.define<string>();\nexport const clearBookmarks = StateEffect.define<void>();\n\n/**\n * Registers a bookmarks state field and Mod-Arrow navigation keys.\n */\nexport const bookmarks = (): Extension => {\n  return [\n    bookmarksField,\n    Prec.highest(\n      keymap.of([\n        {\n          key: 'Mod-ArrowUp',\n          run: (view) => navigateBookmark(view, 'up'),\n        },\n        {\n          key: 'Mod-ArrowDown',\n          run: (view) => navigateBookmark(view, 'down'),\n        },\n      ]),\n    ),\n  ];\n};\n\n// Jump to the nearest bookmark before/after the caret. Declines the key (returns false) when there\n// is none so it falls through to the default behavior rather than being silently swallowed.\nconst navigateBookmark = (view: EditorView, direction: 'up' | 'down'): boolean => {\n  const { bookmarks } = view.state.field(bookmarksField);\n  const pos = view.state.selection.main.head;\n  const target =\n    direction === 'up'\n      ? [...bookmarks].reverse().find((bookmark) => bookmark.pos < pos)\n      : bookmarks.find((bookmark) => bookmark.pos > pos);\n  if (!target) {\n    return false;\n  }\n\n  view.dispatch({ selection: EditorSelection.cursor(target.pos), scrollIntoView: true });\n  return true;\n};\n\ntype BookmarkFieldState = {\n  bookmarks: Bookmark[];\n};\n\nconst bookmarksField = StateField.define<BookmarkFieldState>({\n  create: (): BookmarkFieldState => ({\n    bookmarks: [],\n  }),\n\n  update: (value: BookmarkFieldState, tr): BookmarkFieldState => {\n    // Map bookmark positions through document changes.\n    let bookmarks = value.bookmarks.map((bookmark) => ({\n      ...bookmark,\n      pos: tr.changes.mapPos(bookmark.pos),\n    }));\n\n    // Process effects.\n    for (const effect of tr.effects) {\n      if (effect.is(addBookmark)) {\n        bookmarks = [...bookmarks, effect.value];\n      } else if (effect.is(removeBookmark)) {\n        bookmarks = bookmarks.filter((b) => b.id !== effect.value);\n      } else if (effect.is(clearBookmarks)) {\n        bookmarks = [];\n      }\n    }\n\n    // Sort bookmarks by position.\n    bookmarks.sort(({ pos: a }, { pos: b }) => a - b);\n    return { bookmarks };\n  },\n});\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Extension, StateEffect, StateField } from '@codemirror/state';\nimport { EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nexport type DropOptions = {\n  onDrop?: (view: EditorView, event: { files: FileList }) => void;\n};\n\n/**\n * Editor extension that accepts external file drops (drawing a drop cursor and invoking\n * `options.onDrop`) while leaving internal drags — e.g. an embed's resize handle — untouched.\n */\nexport const dropFile = (options: DropOptions = {}): Extension => {\n  return [\n    styles,\n    dropCursorPos,\n    drawDropCursor,\n    EditorView.domEventHandlers({\n      drop: (event, view) => {\n        // Leave non-file drops (e.g. an embed resize drag) to their own handlers.\n        if (!isFileDrag(event)) {\n          return false;\n        }\n\n        event.preventDefault();\n        const files = event.dataTransfer?.files;\n        const pos = view.posAtCoords(event);\n        if (files?.length && pos !== null) {\n          view.dispatch({ selection: { anchor: pos } });\n          options.onDrop?.(view, { files });\n        }\n        return true;\n      },\n    }),\n  ];\n};\n\n/**\n * Whether a drag carries external files. Internal element drags (e.g. an embed's resize handle using\n * native HTML5 DnD) carry custom data-transfer types, not `Files`, so this gates the file-drop UI to\n * real file drags and leaves other drags untouched (otherwise the drop cursor and drop handler would\n * fire for — and interfere with — an in-document resize drag).\n */\nconst isFileDrag = (event: DragEvent): boolean =>\n  !!event.dataTransfer && Array.from(event.dataTransfer.types).includes('Files');\n\nconst setDropCursorPos = StateEffect.define<number | null>();\n\nconst dropCursorPos = StateField.define<number | null>({\n  create: () => null,\n  update(pos, tr) {\n    if (pos != null && tr.docChanged) {\n      pos = tr.changes.mapPos(pos);\n    }\n    return tr.effects.reduce((value, effect) => (effect.is(setDropCursorPos) ? effect.value : value), pos);\n  },\n});\n\ntype CursorMeasure = { left: number; top: number; height: number };\n\n// Mirrors `@codemirror/view`'s built-in drop cursor, but the `dragover` observer only tracks file\n// drags (see `isFileDrag`) so non-file drags never render the cursor.\nconst drawDropCursor = ViewPlugin.fromClass(\n  class {\n    cursor: HTMLElement | null = null;\n    readonly measureReq: { read: () => CursorMeasure | null; write: (measure: CursorMeasure | null) => void };\n\n    constructor(readonly view: EditorView) {\n      this.measureReq = { read: this.readPos.bind(this), write: this.drawCursor.bind(this) };\n    }\n\n    update(update: ViewUpdate) {\n      const pos = update.state.field(dropCursorPos);\n      if (pos == null) {\n        this.cursor?.remove();\n        this.cursor = null;\n      } else {\n        if (!this.cursor) {\n          this.cursor = this.view.scrollDOM.appendChild(document.createElement('div'));\n          this.cursor.className = 'cm-dropCursor';\n        }\n        if (update.startState.field(dropCursorPos) !== pos || update.docChanged || update.geometryChanged) {\n          this.view.requestMeasure(this.measureReq);\n        }\n      }\n    }\n\n    readPos(): CursorMeasure | null {\n      const pos = this.view.state.field(dropCursorPos);\n      const rect = pos != null && this.view.coordsAtPos(pos);\n      if (!rect) {\n        return null;\n      }\n      const outer = this.view.scrollDOM.getBoundingClientRect();\n      return {\n        left: rect.left - outer.left + this.view.scrollDOM.scrollLeft * this.view.scaleX,\n        top: rect.top - outer.top + this.view.scrollDOM.scrollTop * this.view.scaleY,\n        height: rect.bottom - rect.top,\n      };\n    }\n\n    drawCursor(measure: CursorMeasure | null) {\n      if (!this.cursor) {\n        return;\n      }\n      const { scaleX, scaleY } = this.view;\n      if (measure) {\n        this.cursor.style.left = measure.left / scaleX + 'px';\n        this.cursor.style.top = measure.top / scaleY + 'px';\n        this.cursor.style.height = measure.height / scaleY + 'px';\n      } else {\n        this.cursor.style.left = '-100000px';\n      }\n    }\n\n    destroy() {\n      this.cursor?.remove();\n    }\n\n    setDropPos(pos: number | null) {\n      if (this.view.state.field(dropCursorPos) !== pos) {\n        this.view.dispatch({ effects: setDropCursorPos.of(pos) });\n      }\n    }\n  },\n  {\n    eventObservers: {\n      dragover(event) {\n        if (isFileDrag(event)) {\n          // Mark the editor a valid drop target so the `drop` event fires for external files.\n          event.preventDefault();\n          this.setDropPos(this.view.posAtCoords({ x: event.clientX, y: event.clientY }));\n        }\n      },\n      dragleave(event) {\n        const related = event.relatedTarget instanceof Node ? event.relatedTarget : null;\n        if (event.target === this.view.contentDOM || !this.view.contentDOM.contains(related)) {\n          this.setDropPos(null);\n        }\n      },\n      dragend() {\n        this.setDropPos(null);\n      },\n      drop() {\n        this.setDropPos(null);\n      },\n    },\n  },\n);\n\nconst styles = EditorView.theme({\n  '.cm-dropCursor': {\n    borderLeft: '2px solid var(--color-accent-text)',\n    color: 'var(--color-accent-text)',\n    padding: '0 4px',\n  },\n  '.cm-dropCursor:after': {\n    content: '\"←\"',\n  },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Extension, Prec } from '@codemirror/state';\nimport { EditorView, keymap } from '@codemirror/view';\n\nexport type InlineEditOptions = {\n  /** Enter, or a blur when `commitOnBlur`. Receives the document. */\n  onCommit?: (text: string) => void;\n  /** Escape, or a blur otherwise. */\n  onRevert?: () => void;\n  /** Whether leaving the editor keeps the edit or discards it. */\n  commitOnBlur?: boolean;\n  /**\n   * Whether `Enter` commits. False for a multi-line field, where `Enter` has to stay a newline and\n   * leaving the field is the only way to commit.\n   */\n  submitOnEnter?: boolean;\n};\n\n/**\n * Turns an editor into an inline-edit field: `Enter` commits rather than opening a line, `Escape`\n * reverts, and leaving does whichever the host asked for. `Shift-Enter` still inserts a newline,\n * so a multi-line value stays reachable.\n *\n * The document is one field's worth of text here, not a document, which is why `Enter` is free to\n * mean something else.\n */\nexport const inlineEdit = ({\n  onCommit,\n  onRevert,\n  commitOnBlur = true,\n  submitOnEnter = true,\n}: InlineEditOptions = {}): Extension => {\n  return [\n    // `submit` trims what it hands over, which would drop the leading indentation a markdown code\n    // block depends on — and blur commits the document as written, so the two would disagree. Enter\n    // is handled here instead, reading the document itself.\n    submitOnEnter\n      ? Prec.highest(\n          keymap.of([\n            {\n              key: 'Enter',\n              preventDefault: true,\n              run: (view) => {\n                onCommit?.(view.state.doc.toString());\n                return true;\n              },\n            },\n            {\n              key: 'Shift-Enter',\n              preventDefault: true,\n              run: (view) => {\n                view.dispatch(view.state.replaceSelection('\\n'));\n                return true;\n              },\n            },\n          ]),\n        )\n      : [],\n    Prec.highest(\n      keymap.of([\n        {\n          key: 'Escape',\n          preventDefault: true,\n          run: () => {\n            onRevert?.();\n            // Handled, so an editor inside a dialog does not also close it.\n            return true;\n          },\n        },\n      ]),\n    ),\n    EditorView.domEventHandlers({\n      blur: (_event, view) => {\n        if (commitOnBlur) {\n          onCommit?.(view.state.doc.toString());\n        } else {\n          onRevert?.();\n        }\n        return false;\n      },\n    }),\n  ];\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Range } from '@codemirror/state';\nimport { type Decoration, type DecorationSet } from '@codemirror/view';\n\nexport const decorationSetToArray = (deco: DecorationSet): readonly Range<Decoration>[] => {\n  const ranges: Range<Decoration>[] = [];\n  const iter = deco.iter();\n  while (iter.value) {\n    ranges.push({\n      from: iter.from,\n      to: iter.to,\n      value: iter.value,\n    });\n    iter.next();\n  }\n\n  return ranges;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { diff } from '@codemirror/merge';\n\n/** A document change, in the shape CodeMirror's `dispatch` takes. */\nexport type DocChange = {\n  from: number;\n  to: number;\n  insert: string;\n};\n\nexport type DocChangesOptions = {\n  /**\n   * How long the diff may run before it gives up on precision. Past this it falls back to a\n   * coarser diff, and a whole-document replacement is always the floor.\n   */\n  timeout?: number;\n  /** Caps the depth of the expensive diff, so a large, wholly different document stays linear. */\n  scanLimit?: number;\n  /** Above this, a document is replaced outright rather than diffed. */\n  maxLength?: number;\n};\n\nconst DEFAULT_TIMEOUT = 50;\nconst DEFAULT_SCAN_LIMIT = 500;\nconst DEFAULT_MAX_LENGTH = 4_000_000;\n\n/**\n * The changes that turn `current` into `next`, as narrowly as the diff can see them.\n *\n * Replacing a whole document discards everything anchored to positions in it — folds, the\n * selection, the scroll position — so a view that re-receives a value it mostly already has\n * dispatches only what moved. The result always produces `next` exactly; the worst case is the\n * single whole-document change that replacing it outright would have been.\n */\nexport const computeDocChanges = (\n  current: string,\n  next: string,\n  { timeout = DEFAULT_TIMEOUT, scanLimit = DEFAULT_SCAN_LIMIT, maxLength = DEFAULT_MAX_LENGTH }: DocChangesOptions = {},\n): DocChange[] => {\n  if (current === next) {\n    return [];\n  }\n\n  const replaceAll: DocChange[] = [{ from: 0, to: current.length, insert: next }];\n  if (current.length === 0 || next.length === 0 || current.length > maxLength || next.length > maxLength) {\n    return replaceAll;\n  }\n\n  const changes = diff(current, next, { timeout, scanLimit });\n  if (changes.length === 0) {\n    return [];\n  }\n\n  // A diff that rewrites everything is the replacement, and dispatching it as one change is cheaper.\n  if (changes.length === 1 && changes[0].fromA === 0 && changes[0].toA === current.length) {\n    return replaceAll;\n  }\n\n  return changes.map((change) => ({\n    from: change.fromA,\n    to: change.toA,\n    insert: next.slice(change.fromB, change.toB),\n  }));\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\n// TODO(burdon): Factor out to @dxos/ui\n\nexport type Rect = {\n  readonly left: number;\n  readonly right: number;\n  readonly top: number;\n  readonly bottom: number;\n};\n\nexport const flattenRect = (rect: Rect, left: boolean): Rect => {\n  const x = left ? rect.left : rect.right;\n  return { left: x, right: x, top: rect.top, bottom: rect.bottom };\n};\n\nlet scratchRange: Range | null;\n\nexport const textRange = (node: Text, from: number, to = from): Range => {\n  const range = scratchRange || (scratchRange = document.createRange());\n  range.setEnd(node, to);\n  range.setStart(node, from);\n  return range;\n};\n\nexport const clientRectsFor = (dom: Node): DOMRectList => {\n  if (dom.nodeType === 3) {\n    return textRange(dom as Text, 0, dom.nodeValue!.length).getClientRects();\n  } else if (dom.nodeType === 1) {\n    return (dom as HTMLElement).getClientRects();\n  } else {\n    return [] as any as DOMRectList;\n  }\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type EditorView } from '@codemirror/view';\n\n/** Escapes a string for safe embedding in RegExp source. */\nexport const escapeRegExpSource = (value: string): string => value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n\nexport const insertAtCursor = (view: EditorView, from: number, insert: string) => {\n  view.dispatch({\n    changes: { from, to: from, insert },\n    selection: { anchor: from + insert.length, head: from + insert.length },\n  });\n};\n\n/**\n * If the cursor is at the start of a line, insert the text at the cursor.\n * Otherwise, insert the text on a new line.\n */\nexport const insertAtLineStart = (view: EditorView, from: number, insert: string) => {\n  const line = view.state.doc.lineAt(from);\n  if (line.from === from) {\n    insertAtCursor(view, from, insert);\n  } else {\n    insert = '\\n' + insert;\n    view.dispatch({\n      changes: { from: line.to, to: line.to, insert },\n      selection: { anchor: line.to + insert.length, head: line.to + insert.length },\n    });\n  }\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\nimport { isNonNullable } from '@dxos/util';\n\nimport { documentId } from '../state/selection.ts';\n\nexport type ListenerOptions = {\n  onFocus?: (event: { id: string; focusing: boolean }) => void;\n  onChange?: (event: { id: string; text: string }) => void;\n};\n\n/**\n * Fires callbacks on focus change and document change.\n */\nexport const listener = ({ onFocus, onChange }: ListenerOptions): Extension => {\n  return [\n    onFocus &&\n      EditorView.focusChangeEffect.of((state, focusing) => {\n        onFocus({ id: state.facet(documentId), focusing });\n        return null;\n      }),\n\n    onChange &&\n      EditorView.updateListener.of(({ state, docChanged }) => {\n        if (docChanged) {\n          onChange({ id: state.facet(documentId), text: state.doc.toString() });\n        }\n      }),\n  ].filter(isNonNullable);\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\n/**\n * Adds vertical padding to each `.cm-line`.\n */\nexport function lineSpacing(verticalPadding = 2): Extension {\n  return lineSpacingTheme(verticalPadding);\n}\n\nconst lineSpacingTheme = (verticalPadding: number): Extension =>\n  EditorView.theme({\n    '.cm-line': {\n      paddingTop: `${verticalPadding}px`,\n      paddingBottom: `${verticalPadding}px`,\n    },\n  });\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension, Prec } from '@codemirror/state';\nimport { type EditorView, keymap } from '@codemirror/view';\n\nexport type SubmitOptions = {\n  fireIfEmpty?: boolean;\n  onSubmit?: (text: string) => boolean | void;\n};\n\n/**\n * Handles Enter and Shift-Enter.\n */\nexport const submit = ({ fireIfEmpty = false, onSubmit }: SubmitOptions = {}): Extension => {\n  return [\n    Prec.highest(\n      keymap.of([\n        {\n          key: 'Enter',\n          preventDefault: true,\n          run: (view) => {\n            const text = view.state.doc.toString().trim();\n            if (onSubmit && (fireIfEmpty || text.length > 0)) {\n              const reset = onSubmit(text);\n              if (reset) {\n                // Clear the document after calling onEnter.\n                view.dispatch({\n                  changes: {\n                    from: 0,\n                    to: view.state.doc.length,\n                    insert: '',\n                  },\n                });\n              }\n            }\n\n            return true;\n          },\n        },\n        {\n          key: 'Shift-Enter',\n          preventDefault: true,\n          run: (view) => {\n            // Replace the selection (handles ranges and multiple cursors), not just the main head.\n            view.dispatch(view.state.replaceSelection('\\n'));\n            return true;\n          },\n        },\n      ]),\n    ),\n  ];\n};\n\nexport type SubmitOnModEnterOptions = {\n  onSubmit: (text: string) => void;\n};\n\n/**\n * Handles Cmd-Enter or Ctrl-Enter, on any platform, as an explicit submit for a multi-line field\n * where Enter must stay a newline.\n */\nexport const submitOnModEnter = ({ onSubmit }: SubmitOnModEnterOptions): Extension => {\n  const run = (view: EditorView) => {\n    onSubmit(view.state.doc.toString());\n    return true;\n  };\n\n  // Highest precedence so the markdown keymap's own Mod-Enter never inserts a line first; both\n  // modifiers are bound explicitly since `Mod` means only one of them per platform.\n  return Prec.highest(\n    keymap.of([\n      { key: 'Meta-Enter', preventDefault: true, run },\n      { key: 'Ctrl-Enter', preventDefault: true, run },\n    ]),\n  );\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { fromCursor, toCursor } from '@dxos/echo-client';\nimport { Doc } from '@dxos/echo-doc';\nimport { log } from '@dxos/log';\n\nimport { type CursorConverter } from '../../../util/index.ts';\n\n/**\n * Maps between editor offsets and stable Automerge cursors for the text the {@link Doc.Accessor}\n * points at; falls back to empty/zero positions when the document is unavailable.\n */\nexport const cursorConverter = (accessor: Doc.Accessor): CursorConverter => ({\n  toCursor: (pos, assoc) => {\n    try {\n      return toCursor(accessor, pos, assoc);\n    } catch (err) {\n      log.catch(err);\n      return ''; // In case of invalid request (e.g., wrong document).\n    }\n  },\n\n  fromCursor: (cursor) => {\n    try {\n      return fromCursor(accessor, cursor);\n    } catch (err) {\n      log.catch(err);\n      return 0; // In case of invalid request (e.g., wrong document).\n    }\n  },\n});\n","//\n// Copyright 2023 DXOS.org\n// Copyright 2024 Automerge\n// Ref: https://github.com/automerge/automerge-codemirror\n//\n\nimport { type Heads, type Prop } from '@automerge/automerge';\nimport { Annotation, type EditorState, StateEffect, type StateField, Transaction } from '@codemirror/state';\n\n/** Marks the transaction that performs the initial editor/document reconciliation. */\nexport const initialSync = Transaction.userEvent.of('initial.sync');\n\nexport type State = {\n  path: Prop[];\n  lastHeads: Heads;\n  unreconciledTransactions: Transaction[];\n};\n\nexport const getPath = (state: EditorState, field: StateField<State>): Prop[] => state.field(field).path;\nexport const getLastHeads = (state: EditorState, field: StateField<State>): Heads => state.field(field).lastHeads;\n\nexport type UpdateHeads = {\n  newHeads: Heads;\n};\n\nexport const updateHeadsEffect = StateEffect.define<UpdateHeads>({});\n\nexport const updateHeads = (newHeads: Heads): StateEffect<UpdateHeads> => updateHeadsEffect.of({ newHeads });\n\nexport const reconcileAnnotation = Annotation.define<boolean>();\n\nexport const isReconcile = (tr: Transaction): boolean => {\n  return !!tr.annotation(reconcileAnnotation);\n};\n","//\n// Copyright 2023 DXOS.org\n// Copyright 2024 Automerge\n// Ref: https://github.com/automerge/automerge-codemirror\n//\n\nimport { next as A, type Heads } from '@automerge/automerge';\nimport { type EditorState, type StateField, type Text, type Transaction } from '@codemirror/state';\n\nimport { Doc } from '@dxos/echo-doc';\n\nimport { type State } from './defs.ts';\n\n/**\n * Applies CodeMirror transactions to the Automerge document (via `changeAt` from the last synced\n * heads) and returns the new heads, or undefined when there are no changes.\n */\nexport const updateAutomerge = (\n  field: StateField<State>,\n  handle: Doc.Handle,\n  transactions: Transaction[],\n  state: EditorState, // TODO(burdon): Just pass in the state field value?\n): Heads | undefined => {\n  const { lastHeads, path } = state.field(field);\n\n  // We don't want to call `automerge.updateAt` if there are no changes.\n  // Otherwise, later on `automerge.diff` will return empty patches that result in a no-op but still mess up the selection.\n  let hasChanges = false;\n  for (const tr of transactions) {\n    tr.changes.iterChanges(() => {\n      hasChanges = true;\n    });\n  }\n\n  if (!hasChanges) {\n    return undefined;\n  }\n\n  const newHeads = handle.changeAt(lastHeads, (doc: A.Doc<unknown>) => {\n    const invertedTransactions: { from: number; del: number; insert: Text }[] = [];\n    for (const tr of transactions) {\n      tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {\n        invertedTransactions.push({ from: fromA, del: toA - fromA, insert });\n      });\n    }\n\n    // TODO(burdon): Hack to apply in reverse order to properly apply range.\n    invertedTransactions.reverse().forEach(({ from, del, insert }) => {\n      A.splice(doc, path.slice(), from, del, insert.toString());\n    });\n  });\n\n  return newHeads ?? undefined;\n};\n","//\n// Copyright 2023 DXOS.org\n// Copyright 2024 Automerge\n// Ref: https://github.com/automerge/automerge-codemirror\n//\n\nimport {\n  type DelPatch,\n  type InsertPatch,\n  type Patch,\n  type Prop,\n  type PutPatch,\n  type SpliceTextPatch,\n} from '@automerge/automerge';\nimport { ChangeSet, type ChangeSpec, type EditorSelection, type EditorState } from '@codemirror/state';\nimport { type EditorView } from '@codemirror/view';\n\nimport { reconcileAnnotation } from './defs.ts';\n\n/**\n * Applies Automerge patches to the CodeMirror view as changes, keeping the given selection mapped\n * across them (marked with the reconcile annotation so the sync loop ignores the echoed transaction).\n */\nexport const updateCodeMirror = (view: EditorView, selection: EditorSelection, target: Prop[], patches: Patch[]) => {\n  for (const patch of patches) {\n    const changeSpec = handlePatch(patch, target, view.state);\n    if (changeSpec != null) {\n      const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, '\\n');\n      selection = selection.map(changeSet, 1);\n      view.dispatch({\n        changes: changeSet,\n        annotations: reconcileAnnotation.of(false),\n      });\n    }\n  }\n\n  view.dispatch({\n    selection,\n    annotations: reconcileAnnotation.of(false),\n  });\n};\n\nconst handlePatch = (patch: Patch, target: Prop[], state: EditorState): ChangeSpec | null => {\n  if (patch.action === 'insert') {\n    return handleInsert(target, patch);\n  } else if (patch.action === 'splice') {\n    return handleSplice(target, patch);\n  } else if (patch.action === 'del') {\n    return handleDel(target, patch);\n  } else if (patch.action === 'put') {\n    return handlePut(target, patch, state);\n  } else {\n    return null;\n  }\n};\n\nconst handleInsert = (target: Prop[], patch: InsertPatch): Array<ChangeSpec> => {\n  const index = charPath(target, patch.path);\n  if (index == null) {\n    return [];\n  }\n\n  const text = patch.values.map((value) => (value ? value.toString() : '')).join('');\n  return [{ from: index, to: index, insert: text }];\n};\n\nconst handleSplice = (target: Prop[], patch: SpliceTextPatch): Array<ChangeSpec> => {\n  const index = charPath(target, patch.path);\n  if (index == null) {\n    return [];\n  }\n\n  return [{ from: index, insert: patch.value }];\n};\n\nconst handleDel = (target: Prop[], patch: DelPatch): Array<ChangeSpec> => {\n  const index = charPath(target, patch.path);\n  if (index == null) {\n    return [];\n  }\n\n  const length = patch.length || 1;\n  return [{ from: index, to: index + length }];\n};\n\nconst handlePut = (target: Prop[], patch: PutPatch, state: EditorState): Array<ChangeSpec> => {\n  const index = charPath(target, [...patch.path, 0]);\n  if (index == null) {\n    return [];\n  }\n\n  const length = state.doc.length;\n  if (typeof patch.value !== 'string') {\n    return []; // TODO(dmaretskyi): How to handle non-string values?\n  }\n\n  return [{ from: 0, to: length, insert: patch.value }];\n};\n\n// If the path of the patch is of the form [path, <index>] then we know this is a path to a character\n// within the sequence given by path.\nconst charPath = (textPath: Prop[], candidatePath: Prop[]): number | null => {\n  if (candidatePath.length !== textPath.length + 1) {\n    return null;\n  }\n\n  for (let i = 0; i < textPath.length; i++) {\n    if (textPath[i] !== candidatePath[i]) {\n      return null;\n    }\n  }\n\n  const index = candidatePath[candidatePath.length - 1];\n  if (typeof index === 'number') {\n    return index;\n  }\n\n  return null;\n};\n","//\n// Copyright 2023 DXOS.org\n// Copyright 2024 Automerge\n// Ref: https://github.com/automerge/automerge-codemirror\n//\n\nimport { next as A } from '@automerge/automerge';\nimport { type StateField } from '@codemirror/state';\nimport { type EditorView } from '@codemirror/view';\n\nimport { Doc } from '@dxos/echo-doc';\nimport { log } from '@dxos/log';\n\nimport { type State, getLastHeads, getPath, isReconcile, reconcileAnnotation, updateHeads } from './defs.ts';\nimport { updateAutomerge } from './update-automerge.ts';\nimport { updateCodeMirror } from './update-codemirror.ts';\n\n/**\n * Implements three-way merge (on each mutation).\n */\nexport class Syncer {\n  private _pending = false;\n\n  constructor(\n    private readonly _handle: Doc.Handle,\n    private readonly _state: StateField<State>,\n  ) {}\n\n  reconcile(view: EditorView, editor: boolean): void {\n    if (this._pending) {\n      return;\n    }\n\n    this._pending = true;\n    if (editor) {\n      this.onEditorChange(view);\n    } else {\n      this.onAutomergeChange(view);\n    }\n    this._pending = false;\n  }\n\n  private onEditorChange(view: EditorView): void {\n    log('onEditorChange');\n\n    // Apply the unreconciled transactions to the document.\n    const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));\n    const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);\n\n    if (newHeads) {\n      view.dispatch({\n        effects: updateHeads(newHeads),\n        annotations: reconcileAnnotation.of(false),\n      });\n    }\n  }\n\n  private onAutomergeChange(view: EditorView): void {\n    log('onAutomergeChange');\n\n    // Get the diff between the updated state of the document and the heads and apply that to the codemirror doc.\n    const oldHeads = getLastHeads(view.state, this._state);\n    const newHeads = A.getHeads(this._handle.doc()!);\n    const diff = A.equals(oldHeads, newHeads) ? [] : A.diff(this._handle.doc()!, oldHeads, newHeads);\n\n    const selection = view.state.selection;\n    const path = getPath(view.state, this._state);\n    updateCodeMirror(view, selection, path, diff);\n\n    // TODO(burdon): Test conflicts?\n    // A.getConflicts(this._handle.doc()!, path[0]);\n\n    view.dispatch({\n      effects: updateHeads(newHeads),\n      annotations: reconcileAnnotation.of(false),\n    });\n  }\n}\n","//\n// Copyright 2024 DXOS.org\n// Copyright 2024 Automerge\n// Ref: https://github.com/automerge/automerge-codemirror\n//\n\nimport { next as A } from '@automerge/automerge';\nimport { type Extension, StateField, Transaction } from '@codemirror/state';\nimport { EditorView, ViewPlugin } from '@codemirror/view';\n\nimport { Doc } from '@dxos/echo-doc';\n\nimport { Cursor } from '../../../util/index.ts';\nimport { cursorConverter } from './cursor.ts';\nimport { type State, initialSync, isReconcile, reconcileAnnotation, updateHeadsEffect } from './defs.ts';\nimport { Syncer } from './sync.ts';\n\n/**\n * CodeMirror extension that two-way syncs the editor with the string the {@link Doc.Accessor} points\n * at, reconciling local edits and remote document mutations via Automerge.\n */\nexport const automerge = (accessor: Doc.Accessor): Extension => {\n  const syncState = StateField.define<State>({\n    create: () => {\n      return {\n        path: accessor.path.slice(),\n        lastHeads: A.getHeads(accessor.handle.doc()!),\n        unreconciledTransactions: [],\n      };\n    },\n\n    update: (value, tr) => {\n      const result: State = {\n        path: accessor.path.slice(),\n        lastHeads: value.lastHeads,\n        unreconciledTransactions: value.unreconciledTransactions.slice(),\n      };\n\n      let clearUnreconciled = false;\n      for (const effect of tr.effects) {\n        if (effect.is(updateHeadsEffect)) {\n          result.lastHeads = effect.value.newHeads;\n          clearUnreconciled = true;\n        }\n      }\n\n      if (clearUnreconciled) {\n        result.unreconciledTransactions = [];\n      } else {\n        if (!isReconcile(tr)) {\n          result.unreconciledTransactions.push(tr);\n        }\n      }\n\n      return result;\n    },\n  });\n\n  const syncer = new Syncer(accessor.handle, syncState);\n\n  return [\n    Cursor.converter.of(cursorConverter(accessor)),\n\n    // Track heads.\n    syncState,\n\n    // Reconcile external updates.\n    ViewPlugin.fromClass(\n      class {\n        constructor(private readonly _view: EditorView) {\n          accessor.handle.addListener('change', this._handleChange);\n\n          // Reconcile on attach: a compartment swap hands this extension a view whose content is the\n          // PREVIOUS document's. Deferred by a microtask only to escape the in-progress update cycle;\n          // rAF is not reliable for correctness (hidden/throttled frames never fire it), and until\n          // this replace runs every write maps view coordinates onto the wrong document.\n          queueMicrotask(() => {\n            const value = Doc.getValue<string>(accessor);\n            const current = this._view.state.doc.toString();\n            if (value !== current) {\n              this._view.dispatch({\n                changes: { from: 0, to: this._view.state.doc.length, insert: value },\n                annotations: [initialSync, reconcileAnnotation.of(true)],\n              });\n            }\n          });\n        }\n\n        destroy() {\n          accessor.handle.removeListener('change', this._handleChange);\n        }\n\n        readonly _handleChange = () => {\n          syncer.reconcile(this._view, false);\n        };\n      },\n    ),\n\n    // Reconcile local updates.\n    EditorView.updateListener.of(({ view, changes, transactions }) => {\n      if (!changes.empty) {\n        // Only reconcile if it's not an initial sync (to avoid loops)\n        const isInitialSync = transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value);\n        if (!isInitialSync) {\n          syncer.reconcile(view, true);\n        }\n      }\n    }),\n  ];\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { Annotation, type Extension, type Range, RangeSet, StateEffect, StateField } from '@codemirror/state';\nimport {\n  Decoration,\n  type DecorationSet,\n  EditorView,\n  type PluginValue,\n  type Tooltip,\n  ViewPlugin,\n  type ViewUpdate,\n  WidgetType,\n  showTooltip,\n} from '@codemirror/view';\n\nimport { Event } from '@dxos/async';\nimport { Context } from '@dxos/context';\n\nimport { Cursor, type CursorConverter, singleValueFacet } from '../../../util/index.ts';\n\nexport interface AwarenessProvider {\n  remoteStateChange: Event<void>;\n\n  open(): void;\n  close(): void;\n\n  getRemoteStates(): AwarenessState[];\n  update(position: AwarenessPosition | undefined): void;\n}\n\nexport type AwarenessPosition = {\n  anchor?: string;\n  head?: string;\n};\n\nexport type AwarenessInfo = {\n  displayName: string;\n  darkColor: string;\n  lightColor: string;\n};\n\nexport type AwarenessState = {\n  position?: AwarenessPosition;\n  peerId: string;\n  info: AwarenessInfo;\n};\n\n/**\n * Extension provides presence information about other peers.\n */\nexport const awareness = (provider = dummyProvider): Extension => {\n  return [\n    awarenessProvider.of(provider),\n    ViewPlugin.fromClass(RemoteSelectionsDecorator, {\n      decorations: (value) => value.decorations,\n    }),\n    hoveredCaret,\n    styles,\n  ];\n};\n\nconst dummyProvider: AwarenessProvider = {\n  remoteStateChange: new Event(),\n\n  open: () => {},\n  close: () => {},\n\n  getRemoteStates: () => [],\n  update: () => {},\n};\n\n/**\n * Single-value facet supplying the AwarenessProvider consumed by the awareness extension.\n */\nexport const awarenessProvider = singleValueFacet<AwarenessProvider>(dummyProvider);\n\n// TODO(dmaretskyi): Specify the users that actually changed. Currently, we recalculate positions for every user.\nconst RemoteSelectionChangedAnnotation = Annotation.define();\n\n/**\n * Generates selection decorations from remote peers.\n */\nexport class RemoteSelectionsDecorator implements PluginValue {\n  private readonly _ctx = new Context();\n  private readonly _cursorConverter: CursorConverter;\n  private readonly _provider: AwarenessProvider;\n  private readonly _view: EditorView;\n\n  private _lastAnchor?: number;\n  private _lastHead?: number;\n\n  public decorations: DecorationSet = RangeSet.of([]);\n\n  constructor(view: EditorView) {\n    this._view = view;\n    this._cursorConverter = view.state.facet(Cursor.converter);\n    this._provider = view.state.facet(awarenessProvider);\n    this._provider.open();\n    this._provider.remoteStateChange.on(this._ctx, () => {\n      view.dispatch({ annotations: [RemoteSelectionChangedAnnotation.of([])] });\n    });\n  }\n\n  destroy(): void {\n    cancelHide(this._view);\n    void this._ctx.dispose();\n    this._provider.close();\n  }\n\n  update(update: ViewUpdate): void {\n    this._updateLocalSelection(update.view);\n    this._updateRemoteSelections(update.view);\n  }\n\n  private _updateLocalSelection(view: EditorView): void {\n    const hasFocus = view.hasFocus && view.dom.ownerDocument.hasFocus();\n    const { anchor = undefined, head = undefined } = hasFocus ? view.state.selection.main : {};\n    if (this._lastAnchor === anchor && this._lastHead === head) {\n      return;\n    }\n\n    this._lastAnchor = anchor;\n    this._lastHead = head;\n\n    this._provider.update(\n      anchor !== undefined && head !== undefined\n        ? {\n            anchor: this._cursorConverter.toCursor(anchor),\n            head: this._cursorConverter.toCursor(head, -1),\n          }\n        : undefined,\n    );\n  }\n\n  private _updateRemoteSelections(view: EditorView): void {\n    const decorations: Range<Decoration>[] = [\n      // TODO(burdon): Factor out for testing.\n      // {\n      //   from: 0,\n      //   to: 0,\n      //   value: Decoration.widget({ side: 0, block: false, widget: new RemoteCaretWidget('test', 'Test', 'red') }),\n      // },\n    ];\n\n    const hovered = view.state.field(hoveredCaret, false);\n    let hoveredRendered = false;\n    const awarenessStates = this._provider.getRemoteStates();\n    for (const state of awarenessStates) {\n      const anchor = state.position?.anchor ? this._cursorConverter.fromCursor(state.position.anchor) : null;\n      const head = state.position?.head ? this._cursorConverter.fromCursor(state.position.head) : null;\n      if (anchor == null || head == null) {\n        continue;\n      }\n\n      const start = Math.min(Math.min(anchor, head), view.state.doc.length);\n      const end = Math.min(Math.max(anchor, head), view.state.doc.length);\n\n      const startLine = view.state.doc.lineAt(start);\n      const endLine = view.state.doc.lineAt(end);\n\n      const darkColor = state.info.darkColor;\n      const lightColor = state.info.lightColor;\n\n      if (startLine.number === endLine.number) {\n        // Selected content in a single line.\n        decorations.push({\n          from: start,\n          to: end,\n          value: Decoration.mark({\n            attributes: { style: `background-color: ${lightColor}` },\n            class: 'cm-collab-selection',\n          }),\n        });\n      } else {\n        // Selected content in multiple lines; first, render text-selection in the first line.\n        decorations.push({\n          from: start,\n          to: startLine.from + startLine.length,\n          value: Decoration.mark({\n            attributes: { style: `background-color: ${lightColor}` },\n            class: 'cm-collab-selection',\n          }),\n        });\n\n        // Render text-selection in the last line.\n        decorations.push({\n          from: endLine.from,\n          to: end,\n          value: Decoration.mark({\n            attributes: { style: `background-color: ${lightColor}` },\n            class: 'cm-collab-selection',\n          }),\n        });\n\n        for (let i = startLine.number + 1; i < endLine.number; i++) {\n          const linePos = view.state.doc.line(i).from;\n          decorations.push({\n            from: linePos,\n            to: linePos,\n            value: Decoration.line({\n              attributes: { style: `background-color: ${lightColor}`, class: 'cm-collab-selectionLine' },\n            }),\n          });\n        }\n      }\n\n      const name = state.info.displayName ?? 'Anonymous';\n      const isHovered = hovered?.pos === head && hovered.peerId === state.peerId;\n      hoveredRendered ||= isHovered;\n      decorations.push({\n        from: head,\n        to: head,\n        value: Decoration.widget({\n          side: head - anchor > 0 ? -1 : 1, // The local cursor should be rendered outside the remote selection.\n          block: false,\n          widget: new RemoteCaretWidget(state.peerId, name, darkColor, isHovered),\n        }),\n      });\n    }\n\n    // The peer moved or left: its caret element went without a `mouseleave`, so nothing else would hide the name.\n    if (hovered && !hoveredRendered) {\n      scheduleHide(view);\n    }\n\n    this.decorations = Decoration.set(decorations, true);\n  }\n}\n\n/** Keyed by peer, not name: two peers may share a display name and a position. */\ntype HoveredCaret = { pos: number; peerId: string; name: string; color: string };\n\nconst setHoveredCaret = StateEffect.define<HoveredCaret | null>();\n\n/** How long a caret's name stays up once shown, in ms. */\nconst MIN_TOOLTIP_DURATION = 1_000;\n\n// Per view, so a pending hide is cancelled when the pointer returns to any caret in the same editor.\nconst hoveredAt = new WeakMap<EditorView, number>();\nconst hideTimers = new WeakMap<EditorView, ReturnType<typeof setTimeout>>();\n\n/** Hides the name once it has been up for {@link MIN_TOOLTIP_DURATION}; a hide already pending is kept. */\nconst scheduleHide = (view: EditorView) => {\n  if (hideTimers.has(view)) {\n    return;\n  }\n  const remaining = MIN_TOOLTIP_DURATION - (Date.now() - (hoveredAt.get(view) ?? 0));\n  hideTimers.set(\n    view,\n    setTimeout(\n      () => {\n        hideTimers.delete(view);\n        if (view.dom.isConnected) {\n          view.dispatch({ effects: setHoveredCaret.of(null) });\n        }\n      },\n      Math.max(0, remaining),\n    ),\n  );\n};\n\nconst cancelHide = (view: EditorView) => {\n  clearTimeout(hideTimers.get(view));\n  hideTimers.delete(view);\n};\n\n/**\n * The name of the remote caret under the pointer, shown as a tooltip: CodeMirror draws tooltips outside the\n * scroller, so the name is not clipped above the first line, and flips below only when the window has no room.\n */\nconst hoveredCaret = StateField.define<HoveredCaret | null>({\n  create: () => null,\n  update: (value, tr) => {\n    for (const effect of tr.effects) {\n      if (effect.is(setHoveredCaret)) {\n        return effect.value;\n      }\n    }\n    return value && tr.docChanged ? { ...value, pos: tr.changes.mapPos(value.pos) } : value;\n  },\n  provide: (field) =>\n    showTooltip.from(field, (caret): Tooltip | null =>\n      caret\n        ? {\n            pos: caret.pos,\n            above: true,\n            create: () => {\n              const dom = document.createElement('div');\n              dom.className = 'cm-collab-selectionInfo';\n              dom.style.backgroundColor = caret.color;\n              dom.textContent = caret.name;\n              return { dom };\n            },\n          }\n        : null,\n    ),\n});\n\nclass RemoteCaretWidget extends WidgetType {\n  constructor(\n    private readonly _peerId: string,\n    private readonly _name: string,\n    private readonly _color: string,\n    /** Its name tooltip is showing, which outlasts the pointer; the dot hides for as long. */\n    private readonly _hovered = false,\n  ) {\n    super();\n  }\n\n  override toDOM(view: EditorView): HTMLElement {\n    const span = document.createElement('span');\n    span.className = 'cm-collab-selectionCaret';\n    span.dataset.peer = this._peerId;\n    span.dataset.name = this._name;\n    span.dataset.color = this._color;\n    span.toggleAttribute('data-hovered', this._hovered);\n    span.style.backgroundColor = this._color;\n    span.style.borderColor = this._color;\n\n    const dot = document.createElement('div');\n    dot.className = 'cm-collab-selectionCaretDot';\n\n    // The name for assistive tech; sighted readers get it from the hover tooltip.\n    const name = document.createElement('span');\n    name.className = 'cm-collab-selectionName';\n    name.textContent = this._name;\n\n    span.appendChild(document.createTextNode('\\u2060'));\n    span.appendChild(dot);\n    span.appendChild(document.createTextNode('\\u2060'));\n    span.appendChild(name);\n    span.addEventListener('mouseenter', () => {\n      cancelHide(view);\n      hoveredAt.set(view, Date.now());\n      const pos = view.posAtDOM(span);\n      view.dispatch({\n        effects: setHoveredCaret.of({ pos, peerId: this._peerId, name: this._name, color: this._color }),\n      });\n    });\n    // A 2px caret is easy to leave by accident; a name that vanished at once could not be read.\n    span.addEventListener('mouseleave', () => scheduleHide(view));\n    return span;\n  }\n\n  override updateDOM(dom: HTMLElement): boolean {\n    // Only the hover state changes in place: replacing the element under the pointer would drop its hover.\n    if (dom.dataset.peer !== this._peerId || dom.dataset.name !== this._name || dom.dataset.color !== this._color) {\n      return false;\n    }\n    dom.toggleAttribute('data-hovered', this._hovered);\n    return true;\n  }\n\n  override eq(widget: this): boolean {\n    return (\n      widget._peerId === this._peerId &&\n      widget._color === this._color &&\n      widget._name === this._name &&\n      widget._hovered === this._hovered\n    );\n  }\n\n  override get estimatedHeight() {\n    return -1;\n  }\n\n  override ignoreEvent(): boolean {\n    return true;\n  }\n}\n\nconst styles = EditorView.theme({\n  '.cm-collab-selection': {},\n  '.cm-collab-selectionLine': {\n    padding: 0,\n    margin: '0px 2px 0px 4px',\n  },\n  '.cm-collab-selectionCaret': {\n    position: 'relative',\n    borderLeft: '1px solid black',\n    borderRight: '1px solid black',\n    marginLeft: '-1px',\n    marginRight: '-1px',\n    boxSizing: 'border-box',\n    display: 'inline',\n    cursor: 'pointer',\n  },\n  '.cm-collab-selectionCaretDot': {\n    borderRadius: '50%',\n    position: 'absolute',\n    width: '.5em',\n    height: '.5em',\n    top: '-.25em',\n    left: '-.25em',\n    backgroundColor: 'inherit',\n    transition: 'transform .3s ease-in-out',\n    boxSizing: 'border-box',\n  },\n  '.cm-collab-selectionCaret[data-hovered] > .cm-collab-selectionCaretDot': {\n    transform: 'scale(0)',\n    transformOrigin: 'center',\n  },\n  // Visually hidden, read by assistive tech.\n  '.cm-collab-selectionName': {\n    position: 'absolute',\n    width: '1px',\n    height: '1px',\n    overflow: 'hidden',\n    clipPath: 'inset(50%)',\n    whiteSpace: 'nowrap',\n  },\n  // Inside a tooltip, which takes the editor's font rather than the line's.\n  '.cm-tooltip.cm-collab-selectionInfo': {\n    fontSize: '.75em',\n    fontFamily: 'sans-serif',\n    lineHeight: 'normal',\n    userSelect: 'none',\n    color: 'white',\n    padding: '2px 6px',\n    border: 'none',\n    borderRadius: '2px',\n    whiteSpace: 'nowrap',\n    pointerEvents: 'none',\n  },\n  // Square where it meets the caret, so the name reads as the caret's flag.\n  '.cm-tooltip-above.cm-collab-selectionInfo': {\n    borderBottomLeftRadius: 0,\n  },\n  '.cm-tooltip-below.cm-collab-selectionInfo': {\n    borderTopLeftRadius: 0,\n  },\n});\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type JsonObject, type JsonValue } from '@bufbuild/protobuf';\n\nimport { DeferredTask, Event, sleep } from '@dxos/async';\nimport { Context } from '@dxos/context';\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { type Messenger } from '@dxos/protocols';\nimport { unpackJson } from '@dxos/protocols/buf';\nimport { type GossipMessage } from '@dxos/protocols/buf/dxos/mesh/teleport/gossip_pb';\n\nimport {\n  type AwarenessInfo,\n  type AwarenessPosition,\n  type AwarenessProvider,\n  type AwarenessState,\n} from './awareness.ts';\n\ntype ProtocolMessage =\n  | {\n      kind: 'query';\n    }\n  | {\n      kind: 'post';\n      state: AwarenessState;\n    };\n\n/**\n * The channel's protocol on the wire, where the envelope carries an opaque `Struct`.\n *\n * Written and read field by field: the payload is JSON of unknown provenance, so its shape is\n * established here rather than assumed of it.\n */\nconst toJson = (message: ProtocolMessage): JsonObject =>\n  message.kind === 'query'\n    ? { kind: 'query' }\n    : {\n        kind: 'post',\n        state: {\n          peerId: message.state.peerId,\n          info: { ...message.state.info },\n          ...(message.state.position ? { position: { ...message.state.position } } : {}),\n        },\n      };\n\nconst fromJson = (payload: JsonObject | undefined): ProtocolMessage | undefined => {\n  if (payload?.kind === 'query') {\n    return { kind: 'query' };\n  }\n  if (payload?.kind !== 'post') {\n    return undefined;\n  }\n\n  const state = readObject(payload.state);\n  const info = readObject(state?.info);\n  const position = readObject(state?.position);\n  if (typeof state?.peerId !== 'string' || !info) {\n    return undefined;\n  }\n\n  return {\n    kind: 'post',\n    state: {\n      peerId: state.peerId,\n      info: {\n        displayName: String(info.displayName ?? ''),\n        darkColor: String(info.darkColor ?? ''),\n        lightColor: String(info.lightColor ?? ''),\n      },\n      position: position && {\n        anchor: typeof position.anchor === 'string' ? position.anchor : undefined,\n        head: typeof position.head === 'string' ? position.head : undefined,\n      },\n    },\n  };\n};\n\nconst readObject = (value: JsonValue | undefined): JsonObject | undefined =>\n  typeof value === 'object' && value !== null && !Array.isArray(value) ? value : undefined;\n\nconst DEBOUNCE_INTERVAL = 100; // ms\n\nexport type AwarenessProviderProps = {\n  messenger: Messenger;\n  channel: string;\n  peerId: string;\n  info: AwarenessInfo;\n};\n\n/**\n * Receives and broadcasts profile and cursor position.\n */\nexport class SpaceAwarenessProvider implements AwarenessProvider {\n  private readonly _remoteStates = new Map<string, AwarenessState>();\n\n  private readonly _messenger: Messenger;\n  private readonly _channel: string;\n  private readonly _peerId: string;\n  private readonly _info: AwarenessInfo;\n\n  private _ctx?: Context;\n  private _postTask?: DeferredTask;\n  private _localState?: AwarenessState;\n\n  public readonly remoteStateChange = new Event<void>();\n\n  constructor({ messenger, channel, peerId, info }: AwarenessProviderProps) {\n    this._messenger = messenger;\n    this._channel = channel;\n    this._peerId = peerId;\n    this._info = info;\n  }\n\n  open(): void {\n    this._ctx = new Context();\n    this._postTask = new DeferredTask(this._ctx, async () => {\n      if (this._localState) {\n        await this._messenger.postMessage(this._channel, toJson({ kind: 'post', state: this._localState }));\n\n        // TODO(burdon): Replace with throttle.\n        // TODO(burdon): Send heads?\n        await sleep(DEBOUNCE_INTERVAL);\n      }\n    });\n\n    this._ctx.onDispose(\n      this._messenger.listen(this._channel, (message: GossipMessage) => {\n        const payload = fromJson(unpackJson(message.payload));\n        switch (payload?.kind) {\n          case 'query': {\n            this._handleQueryMessage();\n            break;\n          }\n          case 'post': {\n            this._handlePostMessage(payload);\n            break;\n          }\n        }\n      }),\n    );\n\n    void this._messenger.postMessage(this._channel, toJson({ kind: 'query' })).catch((err) => {\n      log.debug('failed to query awareness', { err });\n    });\n  }\n\n  close(): void {\n    void this._ctx?.dispose();\n    this._ctx = undefined;\n    this._postTask = undefined;\n  }\n\n  getRemoteStates(): AwarenessState[] {\n    return Array.from(this._remoteStates.values());\n  }\n\n  update(position: AwarenessPosition | undefined): void {\n    this._localState = {\n      peerId: this._peerId,\n      position,\n      info: this._info,\n    };\n\n    // Safe no-op if called before open() or during async teardown after close().\n    this._postTask?.schedule();\n  }\n\n  private _handleQueryMessage(): void {\n    this._postTask?.schedule();\n  }\n\n  private _handlePostMessage(message: ProtocolMessage): void {\n    invariant(message.kind === 'post');\n    // TODO(wittjosiah): Is it helpful or confusing to show cursors for self on other devices?\n    this._remoteStates.set(message.state.peerId, message.state);\n    this.remoteStateChange.emit();\n  }\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension, Prec } from '@codemirror/state';\nimport {\n  Decoration,\n  type DecorationSet,\n  EditorView,\n  ViewPlugin,\n  type ViewUpdate,\n  WidgetType,\n  keymap,\n} from '@codemirror/view';\n\nexport type AutocompleteOptions = {\n  /**\n   * Function that returns a list of suggestions based on the current text.\n   * @param text The current text before the cursor\n   * @returns Array of suggestion strings\n   */\n  onSuggest?: (text: string) => string[];\n};\n\n/**\n * Creates an autocomplete extension that shows inline suggestions.\n * Pressing Tab will complete the suggestion.\n *\n * @deprecated Use typeahead.\n */\nexport const autocomplete = ({ onSuggest }: AutocompleteOptions = {}): Extension => {\n  const suggest = ViewPlugin.fromClass(\n    class {\n      _decorations: DecorationSet;\n      _currentSuggestion: string | null = null;\n\n      constructor(view: EditorView) {\n        this._decorations = this.computeDecorations(view);\n      }\n\n      update(update: ViewUpdate) {\n        if (update.docChanged || update.selectionSet) {\n          this._decorations = this.computeDecorations(update.view);\n        }\n      }\n\n      private computeDecorations(view: EditorView): DecorationSet {\n        const text = view.state.doc.toString();\n        const suggestions = onSuggest?.(text) ?? [];\n        if (!suggestions.length) {\n          this._currentSuggestion = null;\n          return Decoration.none;\n        }\n\n        // Get the first suggestion.\n        this._currentSuggestion = suggestions[0];\n        const suffix = this._currentSuggestion.slice(text.length);\n        if (!suffix) {\n          return Decoration.none;\n        }\n\n        // Always show ghost text at the end of the document.\n        return Decoration.set([\n          Decoration.widget({\n            widget: new InlineSuggestionWidget(suffix),\n            side: 1,\n          }).range(view.state.doc.length),\n        ]);\n      }\n\n      completeSuggestion(view: EditorView): boolean {\n        if (!this._currentSuggestion) {\n          return false;\n        }\n\n        const text = view.state.doc.toString();\n        const suffix = this._currentSuggestion.slice(text.length);\n        if (!suffix) {\n          return false;\n        }\n\n        view.dispatch({\n          changes: {\n            from: view.state.doc.length,\n            insert: suffix,\n          },\n          selection: {\n            anchor: view.state.doc.length + suffix.length,\n          },\n        });\n\n        return true;\n      }\n    },\n    {\n      decorations: (v) => v._decorations,\n    },\n  );\n\n  return [\n    suggest,\n    styles,\n\n    Prec.highest(\n      keymap.of([\n        {\n          key: 'Tab',\n          preventDefault: true,\n          run: (view) => {\n            const plugin = view.plugin(suggest);\n            return plugin?.completeSuggestion(view) ?? false;\n          },\n        },\n        {\n          key: 'ArrowRight',\n          preventDefault: true,\n          run: (view) => {\n            // Only complete if cursor is at the end\n            if (view.state.selection.main.head !== view.state.doc.length) {\n              return false;\n            }\n\n            const plugin = view.plugin(suggest);\n            return plugin?.completeSuggestion(view) ?? false;\n          },\n        },\n      ]),\n    ),\n  ];\n};\n\nclass InlineSuggestionWidget extends WidgetType {\n  constructor(private suffix: string) {\n    super();\n  }\n\n  override eq(other: this) {\n    return this.suffix === other.suffix;\n  }\n\n  override toDOM() {\n    const span = document.createElement('span');\n    span.textContent = this.suffix;\n    span.className = 'cm-inline-suggestion';\n    return span;\n  }\n}\n\nconst styles = EditorView.theme({\n  '.cm-inline-suggestion': {\n    opacity: 0.4,\n  },\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nexport type CompletionContext = { line: string };\n\nexport type CompletionOptions = {\n  default?: string;\n  minLength?: number;\n};\n\n/**\n * Util to match current line to a static list of completions.\n */\nexport const staticCompletion =\n  (completions: string[], options: CompletionOptions = {}) =>\n  ({ line }: CompletionContext) => {\n    if (line.length === 0 && options.default) {\n      return options.default;\n    }\n\n    const parts = line.split(/\\s+/).filter(Boolean);\n    if (parts.length) {\n      const str = parts.at(-1)!;\n      if (str.length >= (options.minLength ?? 0)) {\n        for (const completion of completions) {\n          const match = matchCompletion(completion, str);\n          if (match) {\n            return match;\n          }\n        }\n      }\n    }\n  };\n\nexport const matchCompletion = (completion: string, str: string, minLength = 0): string | undefined => {\n  if (\n    str.length >= minLength &&\n    completion.length > str.length &&\n    completion.startsWith(str)\n    // TODO(burdon): If case insensitive, need to replace existing chars.\n    // completion.toLowerCase().startsWith(str.toLowerCase())\n  ) {\n    return completion.slice(str.length);\n  }\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { type CompletionContext, type CompletionResult, autocompletion } from '@codemirror/autocomplete';\nimport type { Extension } from '@codemirror/state';\n\nimport { log } from '@dxos/log';\n\nexport type MentionOptions = {\n  debug?: boolean;\n  onSearch: (text: string) => string[];\n};\n\n// TODO(burdon): Can only have a single autocompletion. Merge configuration with autocomplete.\n/**\n * `@`-mention autocomplete completion source.\n */\nexport const mention = ({ debug, onSearch }: MentionOptions): Extension => {\n  return autocompletion({\n    // TODO(burdon): Not working.\n    activateOnTyping: true,\n    // activateOnTypingDelay: 100,\n    // selectOnOpen: true,\n    closeOnBlur: !debug,\n    // defaultKeymap: false,\n    icons: false,\n    override: [\n      (context: CompletionContext): CompletionResult | null => {\n        log.info('completion context', { context });\n\n        const match = context.matchBefore(/@(\\w+)?/);\n        if (!match || (match.from === match.to && !context.explicit)) {\n          return null;\n        }\n\n        return {\n          from: match.from,\n          options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({ label: `@${value}` })),\n        };\n      },\n    ],\n  });\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, StateEffect, StateField } from '@codemirror/state';\n\n/**\n * Generic shared \"busy\" flag that extensions use to coordinate exclusive use of the cursor region.\n * One extension sets it (e.g. transcription while a pending block is active); others read it (e.g. a\n * placeholder hint) to suppress themselves while something else owns the caret.\n */\nexport const setBusy = StateEffect.define<boolean>();\n\n/** Shared busy StateField. Included by {@link busy}; exported for direct reads (see {@link isBusy}). */\nexport const busyState = StateField.define<boolean>({\n  create: () => false,\n  update: (value, tr) => {\n    let next = value;\n    for (const effect of tr.effects) {\n      if (effect.is(setBusy)) {\n        next = effect.value;\n      }\n    }\n\n    return next;\n  },\n});\n\n/** Whether any extension has flagged the editor busy. Safe when the field is absent (returns false). */\nexport const isBusy = (state: EditorState): boolean => state.field(busyState, false) ?? false;\n\n/** Contributes the shared busy flag. Include once; setters/readers reference the same field. */\nexport const busy = (): Extension => busyState;\n","//\n// Copyright 2025 DXOS.org\n// Based on https://github.com/codemirror/view/blob/main/src/placeholder.ts\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { Decoration, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';\n\nimport { clientRectsFor, flattenRect } from '../../util/index.ts';\nimport { isBusy } from '../state/busy.ts';\n\ntype Content = string | HTMLElement | ((view: EditorView) => HTMLElement);\n\nexport type PlaceholderOptions = {\n  content: Content;\n  delay?: number;\n  focusOnly?: boolean;\n};\n\n/**\n * Shows a transient placeholder at the current cursor position. When\n * `focusOnly` is set the placeholder is suppressed unless the editor has DOM\n * focus, and is hidden again when focus is lost.\n */\nexport const placeholder = ({ content, delay = 3_000, focusOnly = false }: PlaceholderOptions): Extension => {\n  const plugin = ViewPlugin.fromClass(\n    class {\n      _timeout: ReturnType<typeof setTimeout> | undefined;\n      _decorations = Decoration.none;\n\n      update(update: ViewUpdate) {\n        // A busy-state toggle (e.g. transcription starting) carries no doc/selection/focus change,\n        // so it must be detected explicitly to clear an already-scheduled placeholder.\n        const busyChanged = isBusy(update.startState) !== isBusy(update.view.state);\n\n        // React to actual user activity only. The empty `view.update([])`\n        // dispatched from the timeout below carries no doc/selection/focus\n        // change, so it falls through here as a no-op — that's how the\n        // freshly-set widget survives long enough for the decoration\n        // provider to read it. Without this gate, the unconditional reset\n        // would clobber the decoration in the same tick and the placeholder\n        // would never visibly render.\n        if (!update.docChanged && !update.selectionSet && !update.focusChanged && !busyChanged) {\n          return;\n        }\n\n        if (this._timeout) {\n          window.clearTimeout(this._timeout);\n          this._timeout = undefined;\n        }\n        this._decorations = Decoration.none;\n\n        // Honour `focusOnly`: when the option is set and the editor isn't\n        // focused, leave the placeholder hidden and skip rescheduling. The\n        // next `focusChanged` update reschedules once focus returns.\n        if (focusOnly && !update.view.hasFocus) {\n          return;\n        }\n\n        // Suppress the hint while another extension owns the caret region (e.g. transcription).\n        if (isBusy(update.view.state)) {\n          return;\n        }\n\n        const activeLine = update.view.state.doc.lineAt(update.view.state.selection.main.head);\n        if (activeLine.text.trim() !== '') {\n          return;\n        }\n\n        const lineStart = activeLine.from;\n        const view = update.view;\n        this._timeout = setTimeout(() => {\n          this._decorations = Decoration.set([\n            Decoration.widget({\n              widget: new PlaceholderWidget(content),\n              side: 1,\n            }).range(lineStart),\n          ]);\n          view.update([]);\n        }, delay);\n      }\n\n      destroy() {\n        if (this._timeout) {\n          clearTimeout(this._timeout);\n        }\n      }\n    },\n    {\n      provide: (plugin) => {\n        return [EditorView.decorations.of((view) => view.plugin(plugin)?._decorations ?? Decoration.none)];\n      },\n    },\n  );\n\n  return typeof content === 'string'\n    ? [plugin, EditorView.contentAttributes.of({ 'aria-placeholder': content })]\n    : plugin;\n};\n\nexport class PlaceholderWidget extends WidgetType {\n  constructor(readonly content: Content) {\n    super();\n  }\n\n  toDOM(view: EditorView) {\n    const wrap = document.createElement('span');\n    wrap.className = 'cm-placeholder';\n    wrap.style.pointerEvents = 'none';\n    // An inline-block starts a line box of its own, which would inherit the host line's hanging\n    // indent (a list item's negative `text-indent`) and paint the text under the marker.\n    wrap.style.textIndent = '0';\n    wrap.setAttribute('aria-hidden', 'true');\n    wrap.appendChild(\n      typeof this.content === 'string'\n        ? document.createTextNode(this.content)\n        : typeof this.content === 'function'\n          ? this.content(view)\n          : this.content.cloneNode(true),\n    );\n\n    return wrap;\n  }\n\n  override coordsAt(dom: HTMLElement) {\n    const rects = dom.firstChild ? clientRectsFor(dom.firstChild) : [];\n    if (!rects.length) {\n      return null;\n    }\n\n    const style = getComputedStyle(dom.parentNode as HTMLElement);\n    const rect = flattenRect(rects[0], style.direction !== 'rtl');\n    const lineHeight = parseInt(style.lineHeight);\n    if (rect.bottom - rect.top > lineHeight * 1.5) {\n      return {\n        left: rect.left,\n        right: rect.right,\n        top: rect.top,\n        bottom: rect.top + lineHeight,\n      };\n    }\n\n    return rect;\n  }\n\n  override ignoreEvent() {\n    return false;\n  }\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { EditorSelection, type Extension, Prec, RangeSetBuilder } from '@codemirror/state';\nimport {\n  type Command,\n  Decoration,\n  type DecorationSet,\n  type EditorView,\n  ViewPlugin,\n  type ViewUpdate,\n  keymap,\n} from '@codemirror/view';\n\nimport { type CompletionContext } from './match.ts';\nimport { PlaceholderWidget } from './placeholder.ts';\n\n// TODO(burdon): Option to complete only at end of line.\nexport type TypeaheadOptions = {\n  onComplete?: (context: CompletionContext) => string | undefined;\n};\n\n/**\n * Shows a completion placeholder.\n */\nexport const typeahead = ({ onComplete }: TypeaheadOptions = {}): Extension => {\n  // Kept per-view (not in the factory closure) so a single extension instance shared across\n  // multiple editors does not leak completion state between them.\n  const typeaheadPlugin = ViewPlugin.fromClass(\n    class {\n      decorations: DecorationSet = Decoration.none;\n      hint: string | undefined;\n      update(update: ViewUpdate) {\n        // Only recompute on doc/selection changes; skip unrelated updates on this per-keystroke path.\n        if (!update.docChanged && !update.selectionSet) {\n          return;\n        }\n\n        const builder = new RangeSetBuilder<Decoration>();\n        const selection = update.view.state.selection.main;\n        const line = update.view.state.doc.lineAt(selection.from);\n\n        // TODO(burdon): Check at end of line and matches start of previous word.\n        // TODO(burdon): Context grammar.\n        this.hint = undefined;\n        if (selection.from === selection.to && selection.from === line.to) {\n          const str = update.state.sliceDoc(line.from, selection.from);\n          this.hint = onComplete?.({ line: str });\n          if (this.hint) {\n            builder.add(selection.from, selection.to, Decoration.widget({ widget: new PlaceholderWidget(this.hint) }));\n          }\n        }\n\n        this.decorations = builder.finish();\n      }\n    },\n    {\n      decorations: (value) => value.decorations,\n    },\n  );\n\n  const complete: Command = (view: EditorView) => {\n    const hint = view.plugin(typeaheadPlugin)?.hint;\n    if (!hint) {\n      return false;\n    }\n\n    const selection = view.state.selection.main;\n    view.dispatch({\n      changes: [{ from: selection.from, to: selection.to, insert: hint }],\n      selection: EditorSelection.cursor(selection.from + hint.length),\n    });\n\n    return true;\n  };\n\n  return [\n    typeaheadPlugin,\n\n    // Keys.\n    Prec.highest(\n      keymap.of([\n        {\n          key: 'Tab',\n          preventDefault: true,\n          run: complete,\n        },\n        {\n          key: 'ArrowRight',\n          preventDefault: true,\n          run: complete,\n        },\n      ]),\n    ),\n  ];\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\nimport { mx } from '@dxos/ui-theme';\n\nexport type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;\n\n// https://tailwindcss.com/docs/font-weight\nconst headings: Record<HeadingLevel, { className: string; fontSize: string; lineHeight: string }> = {\n  1: {\n    className: 'text-3xl',\n    fontSize: 'var(--text-3xl)',\n    lineHeight: 'var(--text-4xl--line-height)',\n  },\n  2: {\n    className: 'text-2xl',\n    fontSize: 'var(--text-2xl)',\n    lineHeight: 'var(--text-3xl--line-height)',\n  },\n  3: {\n    className: 'text-xl',\n    fontSize: 'var(--text-xl)',\n    lineHeight: 'var(--text-2xl--line-height)',\n  },\n  4: {\n    className: 'text-lg',\n    fontSize: 'var(--text-lg)',\n    lineHeight: 'var(--text-xl--line-height)',\n  },\n  5: {\n    className: 'text-base',\n    fontSize: 'var(--text-base)',\n    lineHeight: 'var(--text-lg--line-height)',\n  },\n  6: {\n    className: 'text-base',\n    fontSize: 'var(--text-base)',\n    lineHeight: 'var(--text-base--line-height)',\n  },\n};\n\n// Font families matching --font-body and --font-mono in theme.css.\nexport const fontBody = '\"Nunito Variable\", \"Inter Variable\", ui-sans-serif, system-ui, sans-serif';\nexport const fontMono = '\"JetBrains Mono Variable\", ui-monospace, \"Cascadia Code\", \"Source Code Pro\", monospace';\n\nexport const markdownTheme = {\n  code: 'font-mono! cm-code-inline',\n  codeMark: 'font-mono! cm-code-mark',\n  mark: 'font-mono!',\n  heading: (level: HeadingLevel) => ({\n    className: mx(headings[level].className, 'font-light text-(--color-cm-heading-number)'),\n    color: 'var(--color-cm-heading) !important',\n    lineHeight: headings[level].lineHeight,\n    fontSize: headings[level].fontSize,\n    fontWeight: '100 !important',\n  }),\n};\n\n/**\n * Global base theme.\n *\n * NOTE: The base theme is GLOBAL and is applied to ALL editors.\n * NOTE: `light` and `dark` selectors are preprocessed by CodeMirror and can only be in the base theme.\n * NOTE: Use 'unset' to remove default CM style.\n *\n * Examples:\n * - https://codemirror.net/examples/styling\n * - https://github.com/codemirror/view/blob/main/src/theme.ts\n * - https://github.com/codemirror/theme-one-dark/blob/main/src/one-dark.ts\n *\n * Main layout:\n * - https://codemirror.net/examples/styling\n * - https://codemirror.net/docs/guide (DOM Structure).\n *\n * <div class=\"cm-editor [cm-focused] [generated classes]\">\n *   <div class=\"cm-scroller\">\n *     <div class=\"cm-gutters\">\n *       <div class=\"cm-gutter [...]\">\n *         <div class=\"cm-gutterElement\">...</div>\n *       </div>\n *     </div>\n *     <div class=\"cm-content\" role=\"textbox\" contenteditable=\"true\">\n *       <div class=\"cm-line\"></div>\n *     </div>\n *     <div class=\"cm-selectionLayer\">\n *       <div class=\"cm-selectionBackground\"></div>\n *     </div>\n *     <div class=\"cm-cursorLayer\">\n *       <div class=\"cm-cursor\"></div>\n *     </div>\n *   </div>\n * </div>\n */\nexport const baseTheme = EditorView.baseTheme({\n  /**\n   * Outer frame.\n   */\n  '&': {},\n  '&.cm-focused': {\n    outline: 'none',\n  },\n\n  /**\n   * Scroller\n   */\n  '.cm-scroller': {\n    // Browser scroll-anchoring: see comment in `scrolling/crawler.ts`. `auto` lets the browser pin a\n    // stable element near the viewport top so widget resizes (e.g. tool-block TogglePanel\n    // open/close) don't jump the user's view.\n    overflowAnchor: 'auto',\n  },\n  // The strip is the thumb plus its inset at both ends, matching the ScrollArea overlay thumb\n  // (8px thumb, 2px inset) so the two read identically side by side.\n  '.cm-scroller::-webkit-scrollbar': {\n    width: 'var(--scrollbar-size,12px)',\n    height: 'var(--scrollbar-size,12px)',\n  },\n  '.cm-scroller::-webkit-scrollbar-corner': {\n    background: 'transparent',\n  },\n  '.cm-scroller::-webkit-scrollbar-track': {\n    background: 'transparent',\n  },\n  // Matches the ScrollArea overlay thumb: a pill inset from the edge. The inset is a transparent\n  // border with `background-clip: content-box`, since a scrollbar pseudo-element cannot be offset.\n  '.cm-scroller::-webkit-scrollbar-thumb': {\n    backgroundColor: 'transparent',\n    borderRadius: 'var(--scrollbar-radius, 9999px)',\n    border: 'var(--scrollbar-inset, 2px) solid transparent',\n    backgroundClip: 'content-box',\n    transition: 'background-color 0.15s',\n  },\n  // `backgroundColor`, not the `background` shorthand, which would reset `background-clip` to\n  // `border-box` and paint over the inset exactly when the thumb becomes visible.\n  '&:hover .cm-scroller::-webkit-scrollbar-thumb': {\n    backgroundColor: 'var(--color-scrollbar-thumb)',\n  },\n\n  /**\n   * Content\n   * NOTE: Apply margins to content so that scrollbar is at the edge of the container.\n   */\n  '.cm-content': {\n    padding: 'unset',\n    color: 'unset',\n  },\n\n  /**\n   * Gutters\n   * NOTE: Gutters should have the same top margin as the content.\n   */\n  '.cm-gutters': {\n    background: 'transparent',\n    borderRight: 'none',\n  },\n  '.cm-gutter': {},\n  '.cm-gutter.cm-lineNumbers': {\n    paddingRight: '4px',\n    color: 'var(--color-fg-subtle)',\n  },\n  '.cm-gutter.cm-lineNumbers .cm-gutterElement': {\n    minWidth: '40px',\n  },\n  /**\n   * Height is set to match the corresponding line (which may have wrapped).\n   */\n  '.cm-gutterElement': {\n    lineHeight: '24px',\n    fontSize: '12px',\n  },\n\n  /**\n   * Line.\n   */\n  '.cm-line': {\n    lineHeight: 1.5,\n    paddingInline: 0,\n  },\n  /**\n   * Force all inline children to inherit line-height to prevent monospace font metrics\n   * (JetBrains Mono ascent/descent) from inflating the line box beyond 24px.\n   */\n  '.cm-line *': {\n    lineHeight: 'inherit',\n  },\n  '.cm-activeLine': {\n    background: 'var(--color-cm-active-line)',\n  },\n\n  /**\n   * Cursor (layer).\n   */\n  '.cm-cursor, .cm-dropCursor': {\n    borderLeft: '2px solid var(--color-cm-cursor)',\n  },\n  '.cm-placeholder': {\n    color: 'var(--color-placeholder)',\n  },\n\n  /**\n   * Selection (layer).\n   * NOTE: A browser dark-mode/reader extension can override `::selection` and reveal a second\n   * (native) selection over this layer; that is an extension artifact, not an editor bug.\n   */\n  '.cm-selectionBackground': {\n    background: 'var(--color-cm-selection)',\n  },\n  '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {\n    background: 'var(--color-cm-focused-selection)',\n  },\n\n  /**\n   * Search.\n   * NOTE: Matches comment.\n   */\n  '.cm-searchMatch': {\n    margin: '0 -3px',\n    padding: '3px',\n    borderRadius: '3px',\n    background: 'var(--color-cm-highlight-surface)',\n    color: 'var(--color-cm-highlight)',\n  },\n  '.cm-searchMatch-selected': {\n    textDecoration: 'underline',\n  },\n\n  /**\n   * Link.\n   */\n  '.cm-link': {\n    textDecorationLine: 'underline',\n    textDecorationThickness: '1px',\n    textDecorationColor: 'var(--color-separator)',\n    textUnderlineOffset: '2px',\n    borderRadius: '.125rem',\n    cursor: 'pointer',\n  },\n  '.cm-link > span': {\n    color: 'var(--color-accent-text)',\n  },\n  '.cm-link > span:hover': {\n    color: 'var(--color-accent-text-hover)',\n  },\n\n  /**\n   * Tooltip.\n   */\n  '.cm-tooltip': {\n    background: 'var(--color-modal-surface)',\n  },\n  '.cm-tooltip-below': {},\n  '.cm-tooltip-hover': {\n    background: 'var(--color-modal-surface)',\n    border: '1px solid var(--color-separator)',\n    borderRadius: '4px',\n    overflow: 'hidden',\n  },\n\n  /**\n   * Autocomplete.\n   * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts\n   */\n  '.cm-tooltip.cm-tooltip-autocomplete': {\n    marginTop: '6px',\n    marginLeft: '-10px',\n    border: '2px solid var(--color-separator)',\n    borderRadius: '4px',\n  },\n  '.cm-tooltip.cm-tooltip-autocomplete > ul': {\n    maxHeight: '20em',\n  },\n  '.cm-tooltip.cm-tooltip-autocomplete > ul > li': {\n    padding: '4px',\n  },\n  '.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]': {\n    background: 'var(--color-current-surface)',\n    color: 'var(--color-fg)',\n  },\n  '.cm-tooltip.cm-tooltip-autocomplete > ul > completion-section': {\n    paddingLeft: '4px !important',\n    color: 'var(--color-fg)',\n  },\n\n  /**\n   * Completion info.\n   */\n  '.cm-completionInfo': {\n    width: '360px !important',\n    margin: '-10px 1px 0 1px',\n    padding: '8px !important',\n    borderColor: 'var(--color-separator)',\n  },\n  '.cm-completionIcon': {\n    display: 'none',\n  },\n  '.cm-completionLabel': {\n    color: 'var(--color-fg-muted)',\n    padding: '0 4px',\n  },\n  '.cm-completionMatchedText': {\n    color: 'var(--color-fg)',\n    textDecoration: 'none !important',\n  },\n\n  /**\n   * Panels\n   * https://github.com/codemirror/search/blob/main/src/search.ts#L745\n   *\n   * Find/replace panel.\n   * <div class=\"cm-announced\">...</div>\n   * <div class=\"cm-scroller\">...</div>\n   * <div class=\"cm-panels cm-panels-bottom\">\n   *   <div class=\"cm-search cm-panel\">\n   *     <input class=\"cm-textfield\" />\n   *     <button class=\"cm-button\">...</button>\n   *     <label><input type=\"checkbox\" />...</label>\n   *   </div>\n   * </div\n   */\n  // TODO(burdon): Implement custom panel (with icon buttons).\n  '.cm-panels': {},\n  '.cm-panel': {\n    backgroundColor: 'var(--surface-bg)',\n  },\n  '.cm-panel input, .cm-panel button, .cm-panel label': {\n    color: 'var(--color-fg-subtle)',\n    fontSize: '14px',\n    all: 'unset',\n    margin: '3px !important',\n    padding: '2px 6px !important',\n    outline: '1px solid transparent',\n  },\n  '.cm-panel input, .cm-panel button': {\n    backgroundColor: 'var(--color-input-surface)',\n  },\n  '.cm-panel input:focus, .cm-panel button:focus': {\n    outline: '1px solid var(--color-focus-ring-subtle)',\n  },\n  '.cm-panel label': {\n    display: 'inline-flex',\n    alignItems: 'center',\n    cursor: 'pointer',\n  },\n  '.cm-panel input.cm-textfield': {},\n  '.cm-panel input[type=checkbox]': {\n    width: '8px',\n    height: '8px',\n    marginRight: '6px !important',\n    padding: '2px !important',\n    color: 'var(--color-focus-ring-subtle)',\n  },\n  '.cm-panel button': {\n    '&:hover': {\n      // TODO(burdon): Replace with layer and @apply bg-accent-bg-hover\n      backgroundColor: 'var(--color-accent-bg-hover) !important',\n    },\n    '&:active': {\n      backgroundColor: 'var(--color-accent-bg-hover)',\n    },\n  },\n  '.cm-panel.cm-search': {\n    padding: '4px',\n    borderTop: '1px solid var(--color-separator)',\n  },\n});\n\nexport const editorGutter: Extension = EditorView.theme({\n  '.cm-gutters': {\n    // NOTE: Non-transparent background required to cover content if scrolling horizontally.\n    background: 'var(--color-base-surface) !important',\n    paddingRight: '1rem',\n  },\n});\n\nexport type FontOptions = {\n  monospace?: boolean;\n};\n\nexport const createFontTheme = ({ monospace }: FontOptions = {}) =>\n  EditorView.theme({\n    // Main content.\n    '.cm-scroller': {\n      fontFamily: monospace ? fontMono : fontBody,\n    },\n\n    // Maintain defaults for UI components.\n    '.cm-content, .cm-gutters, .cm-panel': {\n      fontFamily: 'inherit',\n      fontSize: 'inherit',\n    },\n  });\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { StateEffect, StateField } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\nconst focusEffect = StateEffect.define<boolean>();\n\n/**\n * Boolean StateField holding the editor's current focus state.\n * Included by {@link focus}; exported for direct reads.\n */\nexport const focusField = StateField.define<boolean>({\n  create: () => false,\n  update: (value, tr) => {\n    for (const effect of tr.effects) {\n      if (effect.is(focusEffect)) {\n        return effect.value;\n      }\n    }\n\n    return value;\n  },\n});\n\n/**\n * Manage focus.\n */\nexport const focus = [\n  focusField,\n  EditorView.domEventHandlers({\n    focus: (_event, view) => {\n      requestAnimationFrame(() => view.dispatch({ effects: focusEffect.of(true) }));\n    },\n    blur: (_event, view) => {\n      requestAnimationFrame(() => view.dispatch({ effects: focusEffect.of(false) }));\n    },\n  }),\n];\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { StateEffect } from '@codemirror/state';\nimport { EditorView, ViewPlugin } from '@codemirror/view';\n\nimport { log } from '@dxos/log';\n\n/**\n * Parameters for the scroll to line effect.\n */\nexport type ScrollToProps = {\n  /**\n   * Zero-based line number; -1 for end of document.\n   * NOTE: view.state.doc.lineAt() uses 1-based line numbers.\n   */\n  line: number;\n\n  /**\n   * Additional offset from the target line in pixels.\n   * Positive values scroll past the line, negative values stop before it.\n   * @default 0\n   */\n  offset?: number;\n\n  /**\n   * Position of the target line in the viewport.\n   * - 'start': Line appears at the start (top) of the screen\n   * - 'end': Line appears at the end (bottom) of the screen\n   * @default 'start'\n   */\n  position?: 'start' | 'end';\n\n  /**\n   * Whether to use smooth scrolling.\n   * @default 'instant'\n   */\n  behavior?: ScrollBehavior;\n};\n\n/** Scroll to a specific line. */\nexport const crawlerLineEffect = StateEffect.define<ScrollToProps>();\n\n/**\n * Start/stop crawling the end of the document.\n * `instant: true` snaps to the bottom each frame (no easing) — used for non-streaming height\n * growth (e.g. widgets measuring when a populated thread is first opened); the default eases\n * with the spring for live streaming.\n */\nexport const crawlerActiveEffect = StateEffect.define<{ active: boolean; instant?: boolean }>();\n\n/**\n * Helper function to scroll to a specific line.\n * This is a convenience function that can be used directly with an EditorView.\n */\nexport const scrollToLine = (view: EditorView, options: ScrollToProps) => {\n  view.dispatch({\n    effects: crawlerLineEffect.of(options),\n  });\n};\n\nexport type CrawlerOptions = {\n  /** Threshold in px to trigger scroll from bottom. */\n  overScroll?: number;\n};\n\n/**\n * Imperative scroll-control primitive for streaming editor views.\n *\n * Owns the scroll-related effects (`crawlerLineEffect`, `crawlerActiveEffect`), the spring\n * crawler that follows the bottom of the document, and the `.cm-scroller` theme (overflow,\n * scrollbar styling, and the `::after` overscroll spacer).\n *\n * Use directly for jump-to-line navigation, or pair with `autoScroll` for a pin-to-bottom\n * streaming policy. The composite `streamScroll` bundles both for the common case.\n */\nexport const crawler = ({ overScroll = 0 }: CrawlerOptions = {}) => {\n  // ViewPlugin to manage scroll animations.\n  const crawlerPlugin = ViewPlugin.fromClass(\n    class CrawlerPlugin {\n      private readonly crawler: ReturnType<typeof createCrawler>;\n      constructor(private readonly view: EditorView) {\n        this.crawler = createCrawler(this.view);\n      }\n\n      // No-op.\n      destroy() {\n        this.crawler.cancel();\n      }\n\n      cancel() {\n        this.crawler.cancel();\n      }\n\n      crawl({ active, instant }: { active: boolean; instant?: boolean } = { active: false }) {\n        if (active) {\n          this.crawler.scroll(instant);\n        } else {\n          this.crawler.cancel();\n        }\n      }\n\n      scroll({ line, offset = 0, position, behavior = 'instant' }: ScrollToProps) {\n        const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;\n        const scrollerRect = this.view.scrollDOM.getBoundingClientRect();\n        const doc = this.view.state.doc;\n\n        let targetScrollTop = scrollHeight - clientHeight + offset;\n        if (line >= 0 && line <= doc.lines - 1) {\n          const lineStart = doc.line(line + 1).from;\n          const coords = this.view.coordsAtPos(lineStart);\n          if (coords) {\n            // Calculate target scroll position based on position option.\n            const currentScrollTop = scrollTop;\n            const maxScrollTop = scrollHeight - clientHeight;\n\n            if (position === 'end') {\n              // Position line at end (bottom) of viewport.\n              // Calculate how far down we need to scroll so the line's bottom aligns with viewport bottom.\n              targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;\n            } else {\n              // Default: position line at start (top) of viewport.\n              targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;\n            }\n\n            // Clamp to valid scroll range.\n            targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));\n          }\n        }\n\n        // TODO(burdon): Smooth scrolling doesn't work when the document is being streamed into.\n        requestAnimationFrame(() => {\n          this.view.scrollDOM.scrollTo({ top: targetScrollTop }); //, behavior });\n        });\n      }\n    },\n  );\n\n  return [\n    crawlerPlugin,\n\n    // Listen for effect.\n    EditorView.updateListener.of((update) => {\n      update.transactions.forEach((transaction) => {\n        try {\n          const plugin = update.view.plugin(crawlerPlugin);\n          if (plugin) {\n            for (const effect of transaction.effects) {\n              if (effect.is(crawlerActiveEffect)) {\n                plugin.crawl(effect.value);\n              } else if (effect.is(crawlerLineEffect)) {\n                plugin.scroll(effect.value);\n              }\n            }\n          }\n        } catch (err) {\n          log.catch(err);\n        }\n      });\n    }),\n\n    // Styles.\n    EditorView.theme({\n      '.cm-scroller': {\n        overflowY: 'scroll',\n        // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks\n        // expanding their TogglePanel), the browser picks a stable element near the viewport\n        // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning\n        // logic still has the final word when pinned (forces scrollTop to scrollHeight).\n        overflowAnchor: 'auto',\n      },\n      '.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {\n        display: 'none',\n      },\n      '.cm-scroller::-webkit-scrollbar-thumb': {\n        background: 'transparent',\n        transition: 'background 0.15s',\n      },\n      '&:hover .cm-scroller::-webkit-scrollbar-thumb': {\n        background: 'var(--color-scrollbar-thumb)',\n      },\n      // Spacer below the last text line. Implemented as a real block pseudo-element\n      // (rather than `padding-bottom` on `.cm-content`) so it materializes in the\n      // scroller's `scrollHeight` regardless of how `padding` is reset by the base\n      // theme or downstream classes — this is what gives auto-scroll its head-room\n      // so the last line stays `overScroll` px above the viewport bottom.\n      '.cm-content::after': {\n        content: '\"\"',\n        display: 'block',\n        height: `${overScroll}px`,\n      },\n      '.cm-scroll-button': {\n        position: 'absolute',\n        bottom: '0.5rem',\n        right: '1rem',\n      },\n    }),\n  ];\n};\n\n/**\n * Creates a smooth crawler that follows the live bottom of a CodeMirror 6 EditorView.\n *\n * Uses a critically-damped spring: each frame applies a restoring force toward the\n * target and a damping force opposing current velocity. With damping = 2·ω, the\n * system is critically damped — fastest approach without overshoot. The spring\n * naturally sprints when far behind and eases as it approaches, so streaming\n * content is followed tightly without the jerk of explicit accel/decel state.\n *\n * Integration uses real elapsed wall-clock time so the perceived speed stays\n * constant when requestAnimationFrame is throttled (e.g. low-power mode dropping\n * from 60Hz to 30Hz).\n *\n * @param omega Spring stiffness in rad/s. Higher = snappier follow. ~12–18 feels good.\n * @param snapThreshold Snap-to-target distance threshold in px.\n * @param snapVelocity Snap-to-target velocity threshold in px/s.\n */\nexport function createCrawler(view: EditorView, omega = 5, snapThreshold = 5, snapVelocity = 50) {\n  const el = view.scrollDOM;\n\n  let currentTop = 0;\n  let velocity = 0;\n  let rafId: number | null = null;\n  let lastTime = 0;\n  let instant = false;\n\n  function frame(now: number) {\n    // Clamp dt to handle long pauses (tab backgrounded) and the first frame.\n    const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1000);\n    lastTime = now;\n\n    const targetTop = el.scrollHeight - el.clientHeight;\n    const delta = targetTop - currentTop;\n\n    // Instant mode: snap to the bottom each frame (no easing) and keep following while the\n    // height is still growing (e.g. widgets measuring on open), stopping once it stabilizes.\n    if (instant) {\n      el.scrollTop = targetTop;\n      currentTop = targetTop;\n      velocity = 0;\n      if (Math.abs(delta) < snapThreshold) {\n        rafId = null;\n        lastTime = 0;\n        return;\n      }\n      rafId = requestAnimationFrame(frame);\n      return;\n    }\n\n    if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {\n      el.scrollTop = targetTop;\n      currentTop = targetTop;\n      velocity = 0;\n      rafId = null;\n      lastTime = 0;\n      return;\n    }\n\n    // Critically-damped spring: a = ω²·delta − 2ω·v.\n    const accel = omega * omega * delta - 2 * omega * velocity;\n    velocity += accel * dt;\n    currentTop += velocity * dt;\n    el.scrollTop = currentTop;\n    rafId = requestAnimationFrame(frame);\n  }\n\n  return {\n    scroll: (useInstant = false) => {\n      instant = useInstant;\n      if (rafId === null) {\n        currentTop = el.scrollTop;\n        lastTime = 0;\n        rafId = requestAnimationFrame(frame);\n      }\n    },\n    cancel: () => {\n      if (rafId !== null) {\n        cancelAnimationFrame(rafId);\n        velocity = 0;\n        lastTime = 0;\n        rafId = null;\n      }\n    },\n  };\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { StateEffect } from '@codemirror/state';\nimport { EditorView, ViewPlugin } from '@codemirror/view';\n\nimport { addEventListener, combine, debounceAndThrottle, throttle } from '@dxos/async';\nimport { Domino } from '@dxos/ui';\nimport { getSize } from '@dxos/ui-theme';\n\nimport { crawlerActiveEffect, crawlerLineEffect } from './crawler.ts';\n\n/** Enable or disable autoscroll. */\nexport const autoScrollEffect = StateEffect.define<boolean>();\n\nexport type AutoScrollProps = {\n  /**\n   * If true, immediately jump to the bottom and re-pin whenever the size of the\n   * document view (the scroll container) changes — e.g. when a sidebar toggles\n   * or the window is resized. This avoids the visible \"stuck near bottom\" gap\n   * that otherwise appears because the previous `scrollTop` no longer reaches\n   * the new content height.\n   * @default true\n   */\n  scrollOnResize?: boolean;\n};\n\n/**\n * Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.\n */\nexport const autoScroll = ({ scrollOnResize = true }: AutoScrollProps = {}) => {\n  let buttonContainer: HTMLDivElement | undefined;\n  let isPinned = true;\n  let jumpPending = false;\n  let enabled = true;\n  let firstUpdate = true;\n  // Latches once the thread streams real content. Until then (initial open of a populated\n  // thread, whose height grows only as widgets measure) we snap instantly; afterwards we follow\n  // the bottom smoothly — so the first paint jumps to the end, then streaming eases.\n  let streamed = false;\n\n  const setPinned = (pinned: boolean) => {\n    buttonContainer?.classList.toggle('opacity-0', pinned);\n    isPinned = pinned;\n  };\n\n  return [\n    // Update listener for scrolling when content changes.\n    EditorView.updateListener.of((update) => {\n      const { view, heightChanged, state, startState } = update;\n\n      // Handle enable/disable effect.\n      for (const tr of update.transactions) {\n        for (const effect of tr.effects) {\n          if (effect.is(autoScrollEffect)) {\n            enabled = effect.value;\n            if (enabled) {\n              setPinned(true);\n              view.dispatch({\n                effects: crawlerActiveEffect.of({ active: true }),\n              });\n            } else {\n              view.dispatch({\n                effects: crawlerActiveEffect.of({ active: false }),\n              });\n            }\n          }\n        }\n      }\n\n      if (!enabled) {\n        return;\n      }\n\n      // Jump to bottom instantly when content first appears (either inserted into\n      // an empty doc, or present as initialValue when the editor is created).\n      if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {\n        firstUpdate = false;\n        jumpPending = true;\n        requestAnimationFrame(() => {\n          view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;\n          jumpPending = false;\n        });\n        return;\n      }\n      firstUpdate = false;\n\n      // Suppress crawl while the initial jump is pending.\n      if (jumpPending) {\n        return;\n      }\n\n      // Maybe scroll if doc changed and pinned.\n      // NOTE: Geometry changed is triggered when widgets change height (e.g., toggle tool block).\n      if (heightChanged) {\n        if (isPinned) {\n          // NOTE: Use scroll geometry instead of coordsAtPos to avoid forced layout/scroll side-effects.\n          const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;\n          const delta = scrollHeight - scrollTop - clientHeight;\n          if (update.docChanged) {\n            streamed = true;\n          }\n          if (delta > 0) {\n            setPinned(true);\n            // Before the thread has streamed any real content, height grows only from layout\n            // (widgets measuring on open) — snap instantly so we don't animate through the\n            // backlog. Once content has streamed, follow the bottom smoothly with the spring,\n            // including reflow frames interleaved with token edits. Both keep following until\n            // the height stabilizes.\n            view.dispatch({ effects: crawlerActiveEffect.of({ active: true, instant: !streamed }) });\n          } else if (delta < -1) {\n            setPinned(false);\n          }\n        } else {\n          // TODO(burdon): Should re-pin if content shrinks.\n          if (state.doc.length === 0) {\n            setPinned(true);\n          }\n        }\n      }\n    }),\n\n    // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,\n    // window resize). Doc-driven height changes are handled by the updateListener above; this\n    // observer covers the case where the viewport changes while the doc length is unchanged.\n    scrollOnResize\n      ? ViewPlugin.fromClass(\n          class {\n            private readonly observer: ResizeObserver;\n            private firstObservation = true;\n            private destroyed = false;\n            constructor(view: EditorView) {\n              // Throttle so a continuous drag-resize (or a flurry of layout changes) coalesces\n              // into a single re-pin per ~50ms instead of dispatching every frame.\n              const onResize = throttle(() => {\n                if (this.destroyed || !enabled) {\n                  return;\n                }\n\n                setPinned(true);\n                requestAnimationFrame(() => {\n                  if (this.destroyed) {\n                    return;\n                  }\n\n                  view.scrollDOM.scrollTo({ top: view.scrollDOM.scrollHeight, behavior: 'instant' });\n                  view.dispatch({ effects: crawlerActiveEffect.of({ active: false }) });\n                });\n              }, 50);\n\n              this.observer = new ResizeObserver(() => {\n                // Skip the initial fire that ResizeObserver emits on `observe()`.\n                if (this.firstObservation) {\n                  this.firstObservation = false;\n                  return;\n                }\n                onResize();\n              });\n\n              this.observer.observe(view.scrollDOM);\n            }\n            destroy() {\n              this.destroyed = true;\n              this.observer.disconnect();\n            }\n          },\n        )\n      : [],\n\n    // Detect user scroll and unpin (or re-pin if scrolled to the bottom).\n    ViewPlugin.fromClass(\n      class {\n        private readonly cleanup: () => void;\n        constructor(view: EditorView) {\n          // Re-pin check is throttled so the listener doesn't thrash while scrolling, but\n          // unpinning must be immediate — otherwise content arriving during the throttle\n          // window re-applies the crawl effect and yanks the viewport back to the bottom.\n          // `debounceAndThrottle` (not bare `throttle`) guarantees a trailing call after the\n          // gesture stops; a leading-only throttle drops the final at-bottom sample, leaving the\n          // scroll-to-bottom button visible even though the viewport settled at the bottom.\n          const onUserScroll = debounceAndThrottle(() => {\n            requestAnimationFrame(() => {\n              const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;\n              const delta = scrollHeight - scrollTop - clientHeight;\n              // Sub-pixel tolerance: fractional scroll positions can leave delta at e.g. 0.5\n              // even when the user is visually at the bottom.\n              const pinned = Math.abs(delta) <= 1;\n              setPinned(pinned);\n              if (!pinned) {\n                view.dispatch({ effects: crawlerActiveEffect.of({ active: false }) });\n              }\n            });\n          }, 500);\n\n          this.cleanup = createUserScrollDetector(view.scrollDOM, () => {\n            if (isPinned) {\n              setPinned(false);\n              view.dispatch({ effects: crawlerActiveEffect.of({ active: false }) });\n            }\n            onUserScroll();\n          });\n        }\n        destroy() {\n          this.cleanup();\n        }\n      },\n    ),\n\n    // Scroll button.\n    ViewPlugin.fromClass(\n      class {\n        constructor(view: EditorView) {\n          const button = Domino.of('button')\n            .classNames('dx-control dx-button dx-button-square')\n            .attributes({ 'data-size': 'sm', 'data-variant': 'primary' })\n            .append(Domino.of('dx-icon').classNames(getSize(4)).attributes({ icon: 'ph--arrow-down--regular' }))\n            .on('click', () => {\n              setPinned(true);\n              view.dispatch({\n                effects: [\n                  crawlerLineEffect.of({ line: -1, position: 'end', behavior: 'smooth' }),\n                  // Re-engage the follower so it keeps tracking the bottom as content continues\n                  // to stream after the catch-up jump.\n                  crawlerActiveEffect.of({ active: true, instant: !streamed }),\n                ],\n              });\n            });\n\n          buttonContainer = Domino.of('div')\n            .classNames('cm-scroll-button transition-opacity duration-300 opacity-0 z-1')\n            .append(button).root as HTMLDivElement;\n\n          // The button lives outside CM's managed DOM (the scroller's parent), so remove it on\n          // teardown; otherwise it is orphaned and duplicated when the editor is recreated.\n          view.scrollDOM.parentElement?.appendChild(buttonContainer);\n        }\n\n        destroy() {\n          buttonContainer?.remove();\n          buttonContainer = undefined;\n        }\n      },\n    ),\n  ];\n};\n\n/**\n * Attaches listeners to detect genuine user-initiated scrolling on an element.\n * Two sources are covered:\n *   - `wheel`: fires only from physical mouse wheel / trackpad gestures.\n *   - `pointerdown` on the scrollbar gutter: detected by comparing clientX to\n *     the element's clientWidth (the content area, excluding the scrollbar).\n * Returns a cleanup function that removes the listeners.\n */\n// TODO(burdon): Still jumps when widgets are rendered.\n// - Track position of specific element/line in document and scroll relative to that.\nfunction createUserScrollDetector(element: HTMLElement, onUserScroll: () => void): () => void {\n  return combine(\n    addEventListener(element, 'wheel', () => onUserScroll(), { passive: true }),\n    addEventListener(element, 'pointerdown', (event) => {\n      // If the pointer lands beyond the content width it hit the scrollbar gutter.\n      if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {\n        onUserScroll();\n      }\n    }),\n  );\n}\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView, ViewPlugin } from '@codemirror/view';\n\n/**\n * Custom scroll-past-end extension that accounts for the actual height of the last line.\n * The built-in CodeMirror `scrollPastEnd` uses `defaultLineHeight` which doesn't account\n * for taller elements like headings, block widgets, etc.\n */\nconst scrollPastEndPlugin = ViewPlugin.fromClass(\n  class {\n    _height = 1_000;\n    _attrs: { style: string } | null = { style: `padding-bottom: ${this._height}px` };\n\n    update({ view }: { view: EditorView }) {\n      const lastLineBlock = view.lineBlockAt(view.state.doc.length);\n      const height = view.dom.clientHeight - lastLineBlock.height - view.documentPadding.top - 0.5;\n      if (height >= 0 && height !== this._height) {\n        this._height = height;\n        this._attrs = { style: `padding-bottom: ${height}px` };\n      }\n    }\n  },\n);\n\n/** Adds bottom padding so the last line can scroll to the top of the viewport, accounting for the actual last-line height. */\nexport const scrollPastEnd = (): Extension => [\n  scrollPastEndPlugin,\n  EditorView.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?._attrs ?? null),\n];\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView, ViewPlugin } from '@codemirror/view';\n\nexport type ScrollbarAutohideOptions = {\n  /**\n   * Milliseconds of scroll inactivity before the scrollbar fades out again.\n   * @default 800\n   */\n  timeout?: number;\n};\n\n/**\n * macOS-style overlay scrollbar: the thumb is hidden until the user scrolls, then fades out after a\n * short idle `timeout`. Opt-in — include it in the editor's extension list. Independent of the\n * default hover behaviour in the base theme (which still reveals the thumb on hover).\n */\nexport const scrollbarAutohide = ({ timeout = 800 }: ScrollbarAutohideOptions = {}): Extension => [\n  ViewPlugin.fromClass(\n    // NOTE: Uses TS `private`/plain fields rather than ES `#private`. CodeMirror's plugin lifecycle\n    // (and the source/prebundled double-load in dev) can invoke `destroy` with a `this` that the\n    // WeakMap-based `#private` transpilation rejects (\"private field on non-instance\"), crashing\n    // editor teardown. Plain fields avoid the membership check.\n    class {\n      private readonly _scroller: HTMLElement;\n      private _timer?: ReturnType<typeof setTimeout>;\n\n      constructor(view: EditorView) {\n        this._scroller = view.scrollDOM;\n        this._scroller.addEventListener('scroll', this._handleScroll, { passive: true });\n      }\n\n      destroy(): void {\n        this._scroller.removeEventListener('scroll', this._handleScroll);\n        clearTimeout(this._timer);\n        this._scroller.classList.remove('cm-scrolling');\n      }\n\n      // Show the thumb while scrolling; remove the class once scrolling has been idle for `timeout`.\n      private readonly _handleScroll = (): void => {\n        this._scroller.classList.add('cm-scrolling');\n        clearTimeout(this._timer);\n        this._timer = setTimeout(() => this._scroller.classList.remove('cm-scrolling'), timeout);\n      };\n    },\n  ),\n  styles,\n];\n\nconst styles = EditorView.theme({\n  // Reveal the thumb only while actively scrolling.\n  '.cm-scroller.cm-scrolling::-webkit-scrollbar-thumb': {\n    backgroundColor: 'var(--color-scrollbar-thumb)',\n  },\n  // Suppress the base theme's hover-reveal (higher specificity via `:not(.cm-scrolling)`), so a\n  // hovered/focused-but-idle editor keeps the thumb hidden.\n  '&:hover .cm-scroller:not(.cm-scrolling)::-webkit-scrollbar-thumb': {\n    backgroundColor: 'transparent',\n  },\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\n\nimport { isTruthy } from '@dxos/util';\n\nimport { type AutoScrollProps, autoScroll as autoScrollExtension } from './auto-scroll.ts';\nimport { type CrawlerOptions, crawler } from './crawler.ts';\n\nexport type ScrollerOptions = CrawlerOptions &\n  AutoScrollProps & {\n    /**\n     * Include the auto-scroll policy (pin-to-bottom, user-scroll unpin, scroll-to-bottom button).\n     * Set to `false` to get only the crawler primitive (line jumps, theme, overscroll spacer).\n     * @default true\n     */\n    autoScroll?: boolean;\n  };\n\n/**\n * Composite scroll extension for streaming editor views (chat threads, transcripts, logs).\n */\nexport const scroller = ({ overScroll, scrollOnResize, autoScroll = true }: ScrollerOptions = {}): Extension[] => {\n  return [crawler({ overScroll }), autoScroll && autoScrollExtension({ scrollOnResize })].filter(isTruthy);\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { closeBrackets, closeBracketsKeymap } from '@codemirror/autocomplete';\nimport { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from '@codemirror/commands';\nimport { HighlightStyle, bracketMatching, syntaxHighlighting } from '@codemirror/language';\nimport { searchKeymap } from '@codemirror/search';\nimport { type ChangeSpec, EditorState, type Extension, type TransactionSpec } from '@codemirror/state';\nimport {\n  EditorView,\n  type KeyBinding,\n  ViewPlugin,\n  drawSelection,\n  dropCursor,\n  highlightActiveLine,\n  keymap,\n  lineNumbers,\n  placeholder,\n} from '@codemirror/view';\nimport { vscodeDarkStyle, vscodeLightStyle } from '@uiw/codemirror-theme-vscode';\nimport defaultsDeep from 'lodash.defaultsdeep';\n\nimport { generateName } from '@dxos/display-name';\nimport { Doc } from '@dxos/echo-doc';\nimport { log } from '@dxos/log';\nimport { type Messenger } from '@dxos/protocols';\nimport { type ChromaticPalette, type ThemeMode } from '@dxos/ui-types';\nimport { hexToHue, isTruthy } from '@dxos/util';\n\nimport { baseTheme, createFontTheme, editorGutter } from '../../styles/index.ts';\nimport { automerge } from '../collab/automerge/index.ts';\nimport { SpaceAwarenessProvider, awareness } from '../collab/awareness/index.ts';\nimport { focus } from '../state/focus.ts';\nimport { scrollPastEnd } from '../streaming/scrolling/index.ts';\n\n//\n// Basic\n//\n\n/**\n * Enable tabbing into editor (required for focus-group navigation to reach it).\n */\nexport const tabbable = EditorView.contentAttributes.of({ tabindex: '0' });\n\n/**\n * Transaction filter that strips characters matching the given pattern from inserted text.\n */\nexport const filterChars = (chars: RegExp) => {\n  return EditorState.transactionFilter.of((transaction) => {\n    if (!transaction.docChanged) {\n      return transaction;\n    }\n\n    const changes: ChangeSpec[] = [];\n    transaction.changes.iterChanges((fromA, toA, fromB, toB, text) => {\n      const inserted = text.toString();\n      const filtered = inserted.replace(chars, '');\n      if (inserted !== filtered) {\n        changes.push({\n          from: fromB,\n          to: toB,\n          insert: filtered,\n        });\n      }\n    });\n\n    if (changes.length) {\n      return [transaction, { changes, sequential: true } as TransactionSpec];\n    }\n\n    return transaction;\n  });\n};\n\n/**\n * https://codemirror.net/docs/extensions\n * https://github.com/codemirror/basic-setup\n * https://github.com/codemirror/basic-setup/blob/main/src/codemirror.ts\n * https://github.com/codemirror/theme-one-dark\n */\nexport type BasicExtensionsOptions = {\n  allowMultipleSelections?: boolean;\n  bracketMatching?: boolean;\n  closeBrackets?: boolean;\n  dropCursor?: boolean;\n  drawSelection?: boolean;\n  editable?: boolean;\n  focus?: boolean;\n  highlightActiveLine?: boolean;\n  history?: boolean;\n  indentWithTab?: boolean;\n  keymap?: null | 'default' | 'standard';\n  lineNumbers?: boolean;\n  /** If false then do not set a max-width or side margin on the editor. */\n  lineWrapping?: boolean;\n  placeholder?: string;\n  /** If true user cannot edit the text, but they can still select and copy it. */\n  readOnly?: boolean;\n  search?: boolean;\n  /** NOTE: Do not use with stack sections. */\n  scrollPastEnd?: boolean;\n  standardKeymap?: boolean;\n  tabbable?: boolean;\n  tabSize?: number;\n};\n\nconst defaultBasicOptions: BasicExtensionsOptions = {\n  allowMultipleSelections: true,\n  bracketMatching: true,\n  closeBrackets: true,\n  drawSelection: true,\n  focus: true,\n  history: true,\n  keymap: 'standard',\n  lineWrapping: true,\n  search: false,\n} as const;\n\nconst keymaps: { [key: string]: readonly KeyBinding[] } = {\n  // https://codemirror.net/docs/ref/#commands.standardKeymap\n  standard: standardKeymap,\n  // https://codemirror.net/docs/ref/#commands.defaultKeymap\n  default: defaultKeymap,\n};\n\n/**\n * Assembles the base editor setup: keymaps, history, bracket matching/closing, drop cursor,\n * read-only handling, line wrapping, and tab handling.\n */\nexport const createBasicExtensions = (propsProp?: BasicExtensionsOptions): Extension => {\n  const props = defaultsDeep({}, propsProp, defaultBasicOptions);\n  return [\n    // NOTE: Doesn't catch errors in keymap functions.\n    EditorView.exceptionSink.of((err) => {\n      log.catch(err);\n    }),\n\n    props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),\n    props.bracketMatching && bracketMatching(),\n    props.closeBrackets && closeBrackets(),\n    props.dropCursor && dropCursor(),\n    props.drawSelection && drawSelection({ cursorBlinkRate: 1_200 }),\n    props.editable !== undefined && EditorView.editable.of(props.editable),\n    props.focus && focus,\n    props.highlightActiveLine && highlightActiveLine(),\n    props.history && history(),\n    props.lineNumbers && [lineNumbers(), editorGutter],\n    props.lineWrapping && EditorView.lineWrapping,\n    props.placeholder && placeholder(props.placeholder),\n    props.readOnly !== undefined && EditorState.readOnly.of(props.readOnly),\n    // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing\n    // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that\n    // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits\n    // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)\n    // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers\n    // depend on being able to populate the doc themselves.\n    props.readOnly &&\n      EditorState.transactionFilter.of((tr) =>\n        tr.docChanged &&\n        (tr.isUserEvent('input') || tr.isUserEvent('delete') || tr.isUserEvent('undo') || tr.isUserEvent('redo'))\n          ? []\n          : tr,\n      ),\n    props.scrollPastEnd && scrollPastEnd(),\n    props.tabbable && tabbable,\n    props.tabSize && EditorState.tabSize.of(props.tabSize),\n\n    // https://codemirror.net/docs/ref/#view.KeyBinding\n    keymap.of(\n      [\n        ...((props.keymap && keymaps[props.keymap]) ?? []),\n        // NOTE: Tabs are also configured by markdown extension.\n        // https://codemirror.net/docs/ref/#commands.indentWithTab\n        ...(props.indentWithTab ? [indentWithTab] : []),\n        // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap\n        ...(props.closeBrackets ? closeBracketsKeymap : []),\n        // https://codemirror.net/docs/ref/#commands.historyKeymap\n        ...(props.history ? historyKeymap : []),\n        // https://codemirror.net/docs/ref/#search.searchKeymap\n        ...(props.search ? searchKeymap : []),\n        // Disable bindings that conflict with system shortcuts.\n        // TODO(burdon): Catalog global shortcuts.\n        {\n          key: 'Mod-Shift-k',\n          preventDefault: true,\n          run: () => true,\n        },\n      ].filter(isTruthy),\n    ),\n  ].filter(isTruthy);\n};\n\n//\n// Theme\n//\n\nexport type ThemeExtensionsOptions = {\n  monospace?: boolean;\n  themeMode?: ThemeMode;\n  scrollbarThin?: boolean;\n  slots?: {\n    editor?: {\n      className?: string;\n    };\n    scroller?: {\n      className?: string;\n    };\n    content?: {\n      className?: string;\n    };\n  };\n  syntaxHighlighting?: boolean;\n};\n\nexport const grow: ThemeExtensionsOptions['slots'] = {\n  editor: {\n    className: 'dx-fill',\n  },\n} as const;\n\nexport const fullWidth: ThemeExtensionsOptions['slots'] = {\n  editor: {\n    className: 'w-full',\n  },\n} as const;\n\nexport const defaultThemeSlots = grow;\n\nexport const defaultStyles = {\n  dark: vscodeDarkStyle,\n  light: vscodeLightStyle,\n};\n\n/**\n * https://codemirror.net/examples/styling\n */\nexport const createThemeExtensions = ({\n  monospace,\n  scrollbarThin,\n  slots: slotsProp,\n  syntaxHighlighting: syntaxHighlightingProp,\n  themeMode,\n}: ThemeExtensionsOptions = {}): Extension => {\n  const slots: NonNullable<ThemeExtensionsOptions['slots']> = defaultsDeep({}, slotsProp, defaultThemeSlots);\n  return [\n    baseTheme,\n    EditorView.darkTheme.of(themeMode === 'dark'),\n    createFontTheme({ monospace }),\n    syntaxHighlightingProp &&\n      syntaxHighlighting(HighlightStyle.define(themeMode === 'dark' ? defaultStyles.dark : defaultStyles.light)),\n    slots.editor?.className && EditorView.editorAttributes.of({ class: slots.editor.className }),\n    slots.content?.className && EditorView.contentAttributes.of({ class: slots.content.className }),\n    (slots.scroller?.className || scrollbarThin) &&\n      ViewPlugin.fromClass(\n        class {\n          constructor(view: EditorView) {\n            if (slots.scroller?.className) {\n              view.scrollDOM.classList.add(...slots.scroller.className.split(/\\s+/));\n            }\n            if (scrollbarThin) {\n              view.scrollDOM.style.setProperty('--scrollbar-size', '4px');\n            }\n          }\n        },\n      ),\n  ].filter(isTruthy);\n};\n\n//\n// Data\n//\n\n/**\n * Minimal identity shape consumed by awareness. Kept structural so both the\n * client identity and the `@dxos/halo` `Identity.Info` can be passed without\n * this foundational editor package depending on either.\n */\nexport type DataExtensionsIdentity = {\n  /** Hex-encoded identity key, used as the awareness peer id. */\n  identityKey?: string;\n  displayName?: string;\n  /** Free-form profile metadata (e.g. `hue`). */\n  data?: Record<string, unknown>;\n};\n\nexport type DataExtensionsProps<T> = {\n  id: string;\n  text?: Doc.Accessor<T>;\n  messenger?: Messenger;\n  identity?: DataExtensionsIdentity | null;\n};\n\n/**\n * Wires document sync (automerge) and presence (awareness) for a text accessor.\n */\nexport const createDataExtensions = <T>({ id, text, messenger, identity }: DataExtensionsProps<T>): Extension[] => {\n  const extensions: Extension[] = [];\n  if (text) {\n    extensions.push(automerge(text));\n  }\n\n  if (messenger && identity?.identityKey) {\n    const peerId = identity.identityKey;\n    const hue = (identity.data?.hue as ChromaticPalette | undefined) ?? hexToHue(peerId);\n    extensions.push(\n      awareness(\n        new SpaceAwarenessProvider({\n          messenger,\n          channel: `awareness.${id}`,\n          peerId,\n          info: {\n            darkColor: `var(--color-${hue}-border)`,\n            lightColor: `var(--color-${hue}-border)`,\n            displayName: identity.displayName ?? generateName(peerId),\n          },\n        }),\n      ),\n    );\n  }\n\n  return extensions;\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { keymap } from '@codemirror/view';\nimport { vim } from '@replit/codemirror-vim';\nimport { vscodeKeymap } from '@replit/codemirror-vscode-keymap';\n\nimport { singleValueFacet } from '../../util/index.ts';\n\nexport type EditorInputConfig = {\n  type?: string;\n  ignoreEscape?: boolean;\n};\n\nexport const editorInputMode = singleValueFacet<EditorInputConfig>({});\n\n/**\n * Map of input-mode keymap bundles (default, vscode, vim).\n */\nexport const InputModeExtensions: { [mode: string]: Extension } = {\n  default: [],\n  vscode: [\n    // https://github.com/replit/codemirror-vscode-keymap\n    editorInputMode.of({ type: 'vscode' }),\n    keymap.of(vscodeKeymap),\n  ],\n  vim: [\n    // https://github.com/replit/codemirror-vim\n    vim(),\n    editorInputMode.of({ type: 'vim', ignoreEscape: true }),\n    keymap.of([\n      {\n        key: 'Alt-Escape',\n        run: (view) => {\n          // Focus container for tab navigation.\n          view.dom.parentElement?.focus();\n          return true;\n        },\n      },\n    ]),\n  ],\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type EditorState, type Extension, type RangeSet, StateField, type Transaction } from '@codemirror/state';\n\n/**\n * Logs every syntax-tree node type on each editor update (debug only).\n */\n// eslint-disable-next-line no-console\nexport const debugNodeLogger = (log: (...args: any[]) => void = console.log): Extension => {\n  const logTokens = (state: EditorState) => syntaxTree(state).iterate({ enter: (node) => log(node.type) });\n  return StateField.define<any>({\n    create: (state) => logTokens(state),\n    update: (_: RangeSet<any>, tr: Transaction) => logTokens(tr.state),\n  });\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Extension, RangeSetBuilder } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nexport type AnnotationOptions = {\n  match?: RegExp; // TODO(burdon): Update via hook (e.g., for search).\n};\n\n/**\n * Wavy-underline decoration over regex matches in the viewport.\n */\nexport const annotations = ({ match }: AnnotationOptions = {}): Extension => {\n  return [\n    ViewPlugin.fromClass(\n      class {\n        decorations: DecorationSet = Decoration.none;\n        update(update: ViewUpdate) {\n          const builder = new RangeSetBuilder<Decoration>();\n          if (match) {\n            // Only process visible lines.\n            const { from, to } = update.view.viewport;\n            const text = update.state.doc.sliceString(from, to);\n            const matches = text.matchAll(match);\n            for (const m of matches) {\n              if (m.index !== undefined) {\n                // Adjust match position relative to viewport.\n                const start = from + m.index;\n                const end = start + m[0].length;\n                builder.add(start, end, annotationMark);\n              }\n            }\n          }\n\n          this.decorations = builder.finish();\n        }\n      },\n      {\n        decorations: (v) => v.decorations,\n      },\n    ),\n\n    styles,\n  ];\n};\n\nconst annotationMark = Decoration.mark({ class: 'cm-annotation' });\n\nconst styles = EditorView.theme({\n  '.cm-annotation': {\n    textDecoration: 'underline',\n    textDecorationStyle: 'wavy',\n    textDecorationColor: 'var(--color-error-text)',\n  },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Diagnostic, forEachDiagnostic, linter, setDiagnostics } from '@codemirror/lint';\nimport { ChangeSet, type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\nimport { log } from '@dxos/log';\nimport { safeParseJson, trim } from '@dxos/util';\n\nexport type AssistantOptions = {\n  /**\n   * Instructions to use for the language model.\n   */\n  instructions?: string;\n  /**\n   * Show panel automatically.\n   */\n  autoPanel?: boolean;\n  /**\n   * Debounce delay.\n   */\n  delay?: number;\n  /**\n   * Invoke the language model with the given prompt and return the raw text response.\n   */\n  generate: (request: { instructions: string; content: string }) => Promise<string>;\n};\n\n/**\n * LLM-backed proofreading linter that draws colored underlines and populates a lint panel.\n */\nexport const assistant = (options: AssistantOptions): Extension[] => {\n  const styles = getComputedStyle(document.documentElement);\n  const style = {\n    info: styles.getPropertyValue('--color-green-500').trim(),\n    warning: styles.getPropertyValue('--color-orange-500').trim(),\n    error: styles.getPropertyValue('--color-rose-500').trim(),\n  };\n\n  return [\n    assistantLinter(options),\n    EditorView.baseTheme({\n      '.cm-lintRange-info': {\n        backgroundImage: underline(style.info),\n      },\n      '.cm-lintRange-warning': {\n        backgroundImage: underline(style.warning),\n      },\n      '.cm-lintRange-error': {\n        backgroundImage: underline(style.error),\n      },\n\n      '.cm-panels-bottom': {\n        borderTop: '1px solid var(--color-separator) !important',\n      },\n      '.cm-panel-lint .cm-panel': {\n        outline: 'none !important',\n      },\n      '.cm-panel button': {\n        color: 'var(--color-fg) !important',\n      },\n      '.cm-panel.cm-panel-lint ul': {\n        color: 'var(--color-fg) !important',\n        backgroundColor: 'var(--color-base-surface) !important',\n        marginRight: '2rem !important',\n      },\n      '.cm-panel.cm-panel-lint ul [aria-selected]': {\n        color: 'var(--color-fg) !important',\n        backgroundColor: 'var(--color-base-surface) !important',\n      },\n      '.cm-panel.cm-panel-lint ul li': {\n        display: 'grid',\n        gridTemplateColumns: '1fr auto',\n        alignItems: 'center',\n      },\n      '.cm-panel.cm-panel-lint ul li .cm-diagnosticText': {\n        paddingRight: '8px !important',\n      },\n      '.cm-panel.cm-panel-lint ul li button.cm-diagnosticAction': {\n        margin: 'none !important',\n      },\n      '.cm-diagnostic': {\n        padding: '0px 8px !important',\n        whiteSpace: 'pre-wrap !important',\n      },\n      '.cm-diagnostic-info': {\n        border: 'none !important',\n      },\n    }),\n  ];\n};\n\nconst underline = (color: string) => {\n  const svg = trim`\n    <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"6\" height=\"3\">\n      <path d=\"m0 3 l2 -2 l1 0 l2 2 l1 0\" stroke=\"${color}\" fill=\"none\" stroke-width=\"1\"/>\n    </svg>\n  `;\n\n  return `url('data:image/svg+xml;base64,${btoa(svg)}') !important`;\n};\n\n//\n// Linter\n//\n\ntype Suggestion = {\n  original: string;\n  replacement: string;\n  context: string;\n};\n\nconst isSuggestion = (value: unknown): value is Suggestion =>\n  typeof value === 'object' &&\n  value !== null &&\n  typeof (value as Suggestion).original === 'string' &&\n  typeof (value as Suggestion).replacement === 'string' &&\n  typeof (value as Suggestion).context === 'string';\n\n/**\n * Find the index of `original` within `content`, using `context` to disambiguate when there are multiple occurrences.\n */\nconst findSuggestionIndex = (content: string, suggestion: Suggestion): number => {\n  const firstIdx = content.indexOf(suggestion.original);\n  if (firstIdx === -1) {\n    return -1;\n  }\n\n  // Check for duplicate occurrences; use context to disambiguate.\n  const secondIdx = content.indexOf(suggestion.original, firstIdx + 1);\n  if (secondIdx === -1) {\n    return firstIdx;\n  }\n\n  // Find the occurrence whose surrounding text best matches the context.\n  const contextIdx = content.indexOf(suggestion.context);\n  if (contextIdx !== -1) {\n    const contextEnd = contextIdx + suggestion.context.length;\n    if (secondIdx >= contextIdx && secondIdx <= contextEnd) {\n      return secondIdx;\n    }\n  }\n\n  return firstIdx;\n};\n\nconst replaceTextAndDropLintAtRange = (view: EditorView, from: number, to: number, insert: string) => {\n  const kept: Diagnostic[] = [];\n  forEachDiagnostic(view.state, (diagnostic, diagnosticFrom, diagnosticTo) => {\n    if (diagnosticFrom < to && diagnosticTo > from) {\n      return;\n    }\n    kept.push({ ...diagnostic, from: diagnosticFrom, to: diagnosticTo });\n  });\n  const changeSet = ChangeSet.of({ from, to, insert }, view.state.doc.length);\n  const next = kept.map((d) => ({\n    ...d,\n    from: changeSet.mapPos(d.from, 1),\n    to: changeSet.mapPos(d.to, -1),\n  }));\n  view.dispatch({\n    changes: { from, to, insert },\n    ...setDiagnostics(view.state, next),\n  });\n};\n\nconst DEFAULT_INSTRUCTIONS = trim`\n  Proofread the input text below.\n  Identify typos and grammatical errors. \n  Return ONLY a valid JSON array of objects with fields: \"original\" (string), \"replacement\" (string), \"context\" (string, 3-5 words around match).\n  --\n`;\n\nconst assistantLinter = ({\n  generate,\n  instructions = DEFAULT_INSTRUCTIONS,\n  autoPanel = true,\n  delay = 2_000,\n}: AssistantOptions) =>\n  linter(\n    async (view) => {\n      try {\n        const content = view.state.doc.toString();\n        const result = await generate({ instructions, content });\n\n        const [match] = result.match(/\\[.*\\]/s) ?? [];\n        const parsed = match ? safeParseJson<unknown[]>(match, []) : [];\n        const suggestions = Array.isArray(parsed) ? parsed.filter(isSuggestion) : [];\n        if (suggestions && suggestions.length > 0) {\n          const diagnostics: Diagnostic[] = [];\n          for (const suggestion of suggestions) {\n            const idx = findSuggestionIndex(content, suggestion);\n            if (idx !== -1) {\n              diagnostics.push({\n                from: idx,\n                to: idx + suggestion.original.length,\n                severity: 'info',\n                message: `Suggestion: ${suggestion.replacement}`,\n                actions: [\n                  {\n                    name: 'Apply',\n                    apply: (view, from, to) => {\n                      replaceTextAndDropLintAtRange(view, from, to, suggestion.replacement);\n                    },\n                  },\n                ],\n              });\n            }\n          }\n\n          return diagnostics;\n        }\n      } catch (err) {\n        log.catch(err);\n      }\n\n      return [];\n    },\n    {\n      delay,\n      autoPanel,\n    },\n  );\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension, StateEffect, StateField } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin } from '@codemirror/view';\n\nexport type FaderOptions = {\n  /** Time window in ms to coalesce adjacent appends into a single decoration. */\n  coalesce?: number;\n};\n\n/**\n * Extension that decorates appended text with a fade-in effect.\n * Adjacent appends within the coalesce window are merged into a single decoration.\n * Decorations are queued with an expiry and removed by a timer.\n */\nexport const fader = (options: FaderOptions = {}): Extension => {\n  const removalDelay = DEFAULT_REMOVAL_DELAY;\n  const coalesceWindow = options.coalesce ?? DEFAULT_COALESCE_WINDOW;\n\n  let lastCount = -1;\n  const log = (expiries: number[]) => {\n    if (expiries.length !== lastCount) {\n      lastCount = expiries.length;\n      // console.log('[fader] decorations:', lastCount);\n    }\n  };\n\n  // Effect to remove all expired decorations.\n  const dequeue = StateEffect.define<number>();\n\n  // FIFO of expiry timestamps paired with a decoration set.\n  // Tracks `batchStart` — the time the current coalesce batch began.\n  type FaderState = {\n    decorations: DecorationSet;\n    expiries: number[];\n    batchStart: number;\n  };\n\n  const fadeField = StateField.define<FaderState>({\n    create: () => ({ decorations: Decoration.none, expiries: [], batchStart: 0 }),\n    update: ({ decorations, expiries, batchStart }, tr) => {\n      // Remove expired entries by count (FIFO order matches decoration insertion order).\n      for (const effect of tr.effects) {\n        if (effect.is(dequeue)) {\n          const now = effect.value;\n          let removeCount = 0;\n          while (removeCount < expiries.length && expiries[removeCount] <= now) {\n            removeCount++;\n          }\n          if (removeCount > 0) {\n            expiries = expiries.slice(removeCount);\n            let skipped = 0;\n            decorations = decorations.update({\n              filter: () => {\n                if (skipped < removeCount) {\n                  skipped++;\n                  return false;\n                }\n                return true;\n              },\n            });\n          }\n        }\n      }\n\n      if (!tr.docChanged) {\n        log(expiries);\n        return { decorations, expiries, batchStart };\n      }\n\n      // Reset when document is cleared or fully replaced.\n      let isReset = tr.state.doc.length === 0;\n      if (!isReset && tr.startState.doc.length > 0) {\n        tr.changes.iterChanges((fromA, toA) => {\n          if (fromA === 0 && toA === tr.startState.doc.length) {\n            isReset = true;\n          }\n        });\n      }\n      if (isReset) {\n        log([]);\n        return { decorations: Decoration.none, expiries: [], batchStart: 0 };\n      }\n\n      // Collect appended ranges.\n      const now = Date.now();\n      const add: { from: number; to: number }[] = [];\n      tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {\n        if (toA === tr.startState.doc.length && inserted.length > 0) {\n          add.push({ from: fromB, to: toB });\n        }\n      });\n\n      if (add.length > 0) {\n        const canCoalesce = expiries.length > 0 && batchStart > 0 && now - batchStart < coalesceWindow;\n        if (canCoalesce) {\n          // Extend the last decoration: remove it, then add a merged one.\n          let lastFrom = -1;\n          let lastTo = -1;\n          decorations.between(0, tr.state.doc.length, (from, to) => {\n            lastFrom = from;\n            lastTo = to;\n          });\n\n          if (lastFrom >= 0) {\n            decorations = decorations.update({\n              filter: (from, to) => !(from === lastFrom && to === lastTo),\n            });\n\n            const mergedFrom = Math.min(lastFrom, add[0].from);\n            const mergedTo = add[add.length - 1].to;\n            decorations = decorations.update({\n              add: [Decoration.mark({ class: 'cm-fader' }).range(mergedFrom, mergedTo)],\n            });\n\n            // Update the last expiry rather than adding a new one.\n            expiries = [...expiries.slice(0, -1), now + removalDelay];\n          }\n        } else {\n          // New batch — record when it started.\n          batchStart = now;\n          expiries = [...expiries, now + removalDelay];\n          decorations = decorations.update({\n            add: add.map(({ from, to }) => Decoration.mark({ class: 'cm-fader' }).range(from, to)),\n          });\n        }\n      }\n\n      log(expiries);\n      return { decorations, expiries, batchStart };\n    },\n    provide: (f) => EditorView.decorations.from(f, (value) => value.decorations),\n  });\n\n  // Timer that schedules removal based on the next pending expiry.\n  const cleanup = ViewPlugin.fromClass(\n    class {\n      #timer: ReturnType<typeof setTimeout> | undefined;\n\n      constructor(private view: EditorView) {\n        this.#schedule();\n      }\n\n      update() {\n        this.#schedule();\n      }\n\n      #schedule() {\n        const { expiries } = this.view.state.field(fadeField);\n        if (expiries.length === 0) {\n          clearTimeout(this.#timer);\n          this.#timer = undefined;\n          return;\n        }\n\n        if (this.#timer !== undefined) {\n          return;\n        }\n\n        // Wait at least CLEANUP_INTERVAL after the oldest expiry to batch removals.\n        const delay = Math.max(CLEANUP_INTERVAL, expiries[0] - Date.now());\n        this.#timer = setTimeout(() => {\n          this.#timer = undefined;\n          this.view.dispatch({ effects: dequeue.of(Date.now()) });\n        }, delay);\n      }\n\n      destroy() {\n        clearTimeout(this.#timer);\n      }\n    },\n  );\n\n  return [fadeField, cleanup, styles];\n};\n\nconst DEFAULT_REMOVAL_DELAY = 5_000; // ms.\nconst DEFAULT_COALESCE_WINDOW = 100; // ms.\nconst CLEANUP_INTERVAL = 1_000; // ms.\n\nconst styles = EditorView.theme({\n  '.cm-fader': {\n    animation: 'fader 1s ease-out forwards',\n  },\n  '@keyframes fader': {\n    '0%': {\n      textShadow: '0 0 16px rgba(100, 200, 255, 1), 0 0 32px rgba(100, 200, 255, 0.6)',\n    },\n    '100%': {},\n  },\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport {\n  Decoration,\n  type DecorationSet,\n  EditorView,\n  MatchDecorator,\n  ViewPlugin,\n  type ViewUpdate,\n  WidgetType,\n} from '@codemirror/view';\n\nimport { getHashStyles, mx } from '@dxos/ui-theme';\n\n// TODO(burdon): Autocomplete from existing tags?\n/**\n * Replaces `#tag` occurrences with a styled atomic widget.\n */\nexport const hashtag = (): Extension => [\n  ViewPlugin.fromClass(\n    class {\n      tags: DecorationSet;\n      constructor(view: EditorView) {\n        this.tags = tagMatcher.createDeco(view);\n      }\n\n      update(update: ViewUpdate) {\n        this.tags = tagMatcher.updateDeco(update, this.tags);\n      }\n    },\n    {\n      decorations: (instance) => instance.tags,\n      provide: (plugin) =>\n        EditorView.atomicRanges.of((view) => {\n          return view.plugin(plugin)?.tags || Decoration.none;\n        }),\n    },\n  ),\n\n  styles,\n];\n\nclass TagWidget extends WidgetType {\n  constructor(private _text: string) {\n    super();\n  }\n\n  toDOM(): HTMLSpanElement {\n    const span = document.createElement('span');\n    const { surface, fg } = getHashStyles(this._text);\n    span.className = mx('cm-tag', surface, fg);\n    span.textContent = this._text;\n    return span;\n  }\n}\n\nconst tagMatcher = new MatchDecorator({\n  // `\\w+` stops at the first non-word char, so no trailing boundary is needed; requiring one\n  // (e.g. `\\W`) would consume that char and fail to match a tag at the end of the document.\n  regexp: /#(\\w+)/g,\n  // A bare number is not a tag: `#123` is an issue or pull-request reference, which another\n  // extension resolves and links. Replacing it with a tag widget would hide it from that one,\n  // since the widget is atomic.\n  decoration: (match) => (/^\\d+$/.test(match[1]) ? null : Decoration.replace({ widget: new TagWidget(match[1]) })),\n});\n\nconst styles = EditorView.theme({\n  '.cm-tag': {\n    borderRadius: '4px',\n    marginRight: '6px',\n    padding: '2px 6px',\n  },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { Decoration, EditorView } from '@codemirror/view';\n\nimport { Domino } from '@dxos/ui';\nimport { hues } from '@dxos/ui-types';\nimport { type ChromaticPalette } from '@dxos/ui-types';\n\n/**\n * Reusable, hue-tinted text markers for CodeMirror decorations (shared by comments and pending-text).\n * The tint is driven by a `data-hue` attribute holding a standard ui-theme hue, mapped to the\n * `--color-<hue>-*` tokens — rather than hardcoded per-feature CSS variables.\n */\nexport type MarkerHue = ChromaticPalette;\n\n/**\n * Shared theme for hue-tinted markers and their inline button affordances. Include once per editor;\n * both `markerMark` (range marks) and `markerText` (widget spans) read from it.\n */\nexport const markerTheme = (): Extension =>\n  EditorView.theme({\n    ...hueVars,\n    // The comment-style surface \"padding\" is a box-shadow of the same colour so adjacent lines join.\n    // No `border-radius`: with `box-decoration-break: clone` every wrapped fragment would round its\n    // own corners, pinching the left/right edges of a multi-line span into jagged notches. Square\n    // corners let the per-line boxes stack into one continuous block with straight vertical edges.\n    '.cm-marker > span, .cm-marker-text': {\n      boxDecorationBreak: 'clone',\n      backgroundColor: 'var(--cm-marker-surface)',\n      boxShadow: '0 0 0 3px var(--cm-marker-surface)',\n      color: 'var(--cm-marker-text) !important',\n    },\n    // `inline-block` (not flex) so the finalized + interim spans flow as one continuous block.\n    '.cm-marker-text': {\n      display: 'inline-block',\n    },\n    // Align a trailing icon (e.g. the recording spinner) inline with the text — the global reset\n    // sets `svg { display: block }`, which would otherwise push it onto its own line.\n    '.cm-marker-text svg': {\n      display: 'inline-block',\n      verticalAlign: '-0.15em',\n      marginInlineStart: '0.25rem',\n    },\n    '.cm-marker-buttons': {\n      display: 'inline-flex',\n      alignItems: 'center',\n      gap: '0.25rem',\n    },\n    '.cm-marker-button': {\n      display: 'inline-flex',\n      padding: '4px',\n      borderRadius: '4px',\n      cursor: 'pointer',\n    },\n    '.cm-marker-button:hover': {\n      backgroundColor: 'var(--color-hover-bg)',\n    },\n    '.cm-marker-button-success:hover': {\n      backgroundColor: 'var(--color-success-bg)',\n    },\n    '.cm-marker-button-error:hover': {\n      backgroundColor: 'var(--color-error-bg)',\n    },\n  });\n\n/** Range mark tinted by hue (used by comments to highlight a selection). */\nexport const markerMark = (hue: MarkerHue, options: { class?: string; attributes?: Record<string, string> } = {}) =>\n  Decoration.mark({\n    class: ['cm-marker', options.class].filter(Boolean).join(' '),\n    attributes: { 'data-hue': hue, ...options.attributes },\n  });\n\nexport type MarkerTextOptions = {\n  hue: MarkerHue;\n  className?: string;\n  /** Optional trailing icon (e.g. a recording indicator). */\n  icon?: string;\n  /** Extra classes for the trailing icon (e.g. `animate-spin`). */\n  iconClassNames?: string;\n};\n\n/** A hue-tinted text span for use inside widgets; optionally trailed by an icon. */\nexport const markerText = (\n  text: string,\n  { hue, className, icon, iconClassNames }: MarkerTextOptions,\n): Domino<HTMLElement> => {\n  const span = Domino.of('span')\n    .classNames('cm-marker-text', className)\n    .attributes({ 'data-hue': hue })\n    .append(Domino.of('span').text(text));\n  if (icon) {\n    // Preserve the sprite sizing (`Domino.svg` sets `h-[1em] w-[1em]`) since `classNames` replaces.\n    span.append(Domino.svg(icon).classNames('shrink-0 h-[1em] w-[1em]', iconClassNames));\n  }\n\n  return span;\n};\n\nexport type MarkerButton = {\n  icon: string;\n  label: string;\n  className?: string;\n  testId?: string;\n  onClick: () => void;\n};\n\n/** Inline group of icon buttons (e.g. confirm/cancel affordances). */\nexport const markerButtons = (buttons: MarkerButton[]): Domino<HTMLElement> =>\n  Domino.of('span')\n    .classNames('cm-marker-buttons')\n    .append(\n      ...buttons.map((button) =>\n        Domino.of('button')\n          .classNames('cm-marker-button', button.className)\n          .attributes({\n            'type': 'button',\n            'aria-label': button.label,\n            ...(button.testId ? { 'data-testid': button.testId } : {}),\n          })\n          .append(Domino.svg(button.icon))\n          // `mousedown` + preventDefault so clicking the affordance does not steal the editor selection.\n          .on('mousedown', (event) => {\n            event.preventDefault();\n            button.onClick();\n          }),\n      ),\n    );\n\n// Map `data-hue` to the standard ui-theme surface/foreground tokens (one rule per hue).\nconst hueVars = Object.fromEntries(\n  hues.flatMap((hue) =>\n    ['.cm-marker', '.cm-marker-text'].map((selector) => [\n      `${selector}[data-hue=\"${hue}\"]`,\n      {\n        '--cm-marker-surface': `var(--color-${hue}-bg)`,\n        '--cm-marker-text': `var(--color-${hue}-fg)`,\n      },\n    ]),\n  ),\n);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, RangeSetBuilder, StateEffect, StateField } from '@codemirror/state';\nimport { Decoration, EditorView, ViewPlugin, type ViewUpdate, hoverTooltip } from '@codemirror/view';\n\nimport { debounce } from '@dxos/async';\nimport { type Document, type Upos, sourceHash } from '@dxos/nlp';\n\nexport type PosOptions = {\n  /** When set, the extension self-parses on idle and dispatches `setAnalysis`. */\n  parse?: (text: string) => Promise<Document>;\n  /** Idle debounce before reactive parsing (ms). */\n  debounceMs?: number;\n  /** Colored underline per UPOS tag (default true). */\n  underline?: boolean;\n  /** Hover popover naming the part of speech (default false). */\n  popover?: boolean;\n};\n\n/**\n * Part-of-speech decoration extension.\n * Renders per-word UPOS marks from analysis state held in a span-oriented state field, with\n * independently switchable presentations: a colored underline per tag ({@link posTheme}) and a\n * hover popover naming the part of speech ({@link posPopover}). State can be set externally via\n * `setAnalysis`/`clearAnalysis`, or — when `options.parse` is supplied — self-driven on idle\n * (reactive mode).\n */\nexport const pos = ({ parse, debounceMs = 500, underline = true, popover = false }: PosOptions = {}): Extension => {\n  const extensions: Extension[] = [posAnalysisField, posDecorations, staleTheme];\n  if (underline) {\n    extensions.push(posTheme());\n  }\n  if (popover) {\n    extensions.push(posPopover());\n  }\n  if (parse) {\n    extensions.push(reactiveDriver(parse, debounceMs));\n  }\n\n  return extensions;\n};\n\n/** One analyzed region of the document. `document` offsets are relative to the span's own text. */\nexport type PosSpan = {\n  from: number;\n  to: number;\n  /** Hash of the source text the analysis covers; compared to live text to detect divergence. */\n  sourceHash: string;\n  document: Document;\n  /** True once the underlying text diverged from `sourceHash` (decorations render dimmed). */\n  stale: boolean;\n};\n\n/** Replace/add the analysis for a region. */\nexport const setAnalysis = StateEffect.define<{ from: number; to: number; document: Document }>();\n\n/** Remove all analysis spans. */\nexport const clearAnalysis = StateEffect.define<null>();\n\n/** Internal: mark a span stale (dispatched by the reactive driver on divergence). */\nexport const markStale = StateEffect.define<{ from: number; to: number }>();\n\n/**\n * StateField holding the span-oriented part-of-speech analysis.\n * Included by {@link pos}; exported for reads via {@link posSpans}.\n */\nexport const posAnalysisField = StateField.define<PosSpan[]>({\n  create: () => [],\n  update: (spans, tr) => {\n    let next = spans;\n\n    // Map existing span ranges through document changes so anchors follow edits.\n    if (tr.docChanged) {\n      next = next.map((span) => ({\n        ...span,\n        from: tr.changes.mapPos(span.from, 1),\n        to: tr.changes.mapPos(span.to, -1),\n      }));\n    }\n\n    for (const effect of tr.effects) {\n      if (effect.is(setAnalysis)) {\n        const { from, to, document } = effect.value;\n        // Replace any span at the same anchor; otherwise append.\n        const without = next.filter((span) => !(span.from === from && span.to === to));\n        next = [...without, { from, to, sourceHash: document.sourceHash, document, stale: false }];\n      } else if (effect.is(clearAnalysis)) {\n        next = [];\n      } else if (effect.is(markStale)) {\n        const { from, to } = effect.value;\n        next = next.map((span) => (span.from === from && span.to === to ? { ...span, stale: true } : span));\n      }\n    }\n\n    return next;\n  },\n});\n\n/** Read the current analysis spans from editor state. */\nexport const posSpans = (state: EditorState): PosSpan[] => state.field(posAnalysisField);\n\n/** One mark per UPOS tag; CSS class drives color (see `posTheme`). Stale spans add `cm-pos-stale`. */\nconst posMark = (upos: Upos, stale: boolean) =>\n  Decoration.mark({ class: ['cm-pos', `cm-pos-${upos}`, stale && 'cm-pos-stale'].filter(Boolean).join(' ') });\n\n/**\n * Computes UPOS decoration marks from the analysis field.\n * Included by {@link pos}.\n */\nexport const posDecorations = EditorView.decorations.compute([posAnalysisField], (state) => {\n  const builder = new RangeSetBuilder<Decoration>();\n  const spans = state.field(posAnalysisField);\n  // Tokens are span-relative; absolute offset = span.from + token.start. Sort by absolute start so\n  // the RangeSetBuilder receives ranges in non-decreasing order.\n  const marks = spans\n    .flatMap((span) =>\n      span.document.sentences.flatMap((sentence) =>\n        sentence.tokens.map((token) => ({\n          from: span.from + token.start,\n          to: span.from + token.end,\n          deco: posMark(token.upos, span.stale),\n        })),\n      ),\n    )\n    .filter((mark) => mark.to <= state.doc.length && mark.from < mark.to)\n    .sort((markA, markB) => markA.from - markB.from || markA.to - markB.to);\n  for (const mark of marks) {\n    builder.add(mark.from, mark.to, mark.deco);\n  }\n  return builder.finish();\n});\n\n// UPOS → ui-theme hue. Content classes (NOUN/VERB/ADJ/ADV/PROPN/NUM) get distinct hues; function\n// words share a muted neutral; PUNCT/SYM/X are unstyled.\nconst POS_HUE: Partial<Record<Upos, string>> = {\n  NOUN: 'blue',\n  PROPN: 'indigo',\n  VERB: 'red',\n  ADJ: 'green',\n  ADV: 'amber',\n  NUM: 'cyan',\n  INTJ: 'pink',\n  PRON: 'neutral',\n  DET: 'neutral',\n  ADP: 'neutral',\n  AUX: 'neutral',\n  CCONJ: 'neutral',\n  SCONJ: 'neutral',\n  PART: 'neutral',\n};\n\n/** Human-readable UPOS names (https://universaldependencies.org/u/pos/), shown in the hover popover. */\nconst UPOS_LABEL: Record<Upos, string> = {\n  ADJ: 'adjective',\n  ADP: 'adposition',\n  ADV: 'adverb',\n  AUX: 'auxiliary',\n  CCONJ: 'coordinating conjunction',\n  DET: 'determiner',\n  INTJ: 'interjection',\n  NOUN: 'noun',\n  NUM: 'numeral',\n  PART: 'particle',\n  PRON: 'pronoun',\n  PROPN: 'proper noun',\n  PUNCT: 'punctuation',\n  SCONJ: 'subordinating conjunction',\n  SYM: 'symbol',\n  VERB: 'verb',\n  X: 'other',\n};\n\nexport type PosToken = {\n  from: number;\n  to: number;\n  upos: Upos;\n  stale: boolean;\n};\n\n/** Resolve the analyzed token covering an absolute document position, if any. */\nexport const posTokenAt = (state: EditorState, position: number): PosToken | undefined => {\n  const spans = state.field(posAnalysisField, false) ?? [];\n  for (const span of spans) {\n    if (position < span.from || position > span.to) {\n      continue;\n    }\n    for (const sentence of span.document.sentences) {\n      for (const token of sentence.tokens) {\n        const from = span.from + token.start;\n        const to = span.from + token.end;\n        if (position >= from && position < to && to <= state.doc.length) {\n          return { from, to, upos: token.upos, stale: span.stale };\n        }\n      }\n    }\n  }\n  return undefined;\n};\n\n/** Hover popover naming the part of speech of the token under the pointer. */\nexport const posPopover = (): Extension => [\n  hoverTooltip((view, position) => {\n    const token = posTokenAt(view.state, position);\n    if (!token) {\n      return null;\n    }\n    return {\n      pos: token.from,\n      end: token.to,\n      above: true,\n      create: () => {\n        const dom = document.createElement('div');\n        dom.className = 'cm-pos-popover';\n        const swatch = document.createElement('span');\n        swatch.className = 'cm-pos-swatch';\n        swatch.style.background = `var(--color-${POS_HUE[token.upos] ?? 'neutral'}-500)`;\n        const tag = document.createElement('span');\n        tag.className = 'cm-pos-popover-tag';\n        tag.textContent = token.upos;\n        const label = document.createElement('span');\n        label.className = 'cm-pos-popover-label';\n        label.textContent = UPOS_LABEL[token.upos];\n        dom.append(swatch, tag, label);\n        return { dom };\n      },\n    };\n  }),\n  popoverTheme,\n];\n\n/** A span has diverged when the current text under its (mapped) range no longer matches its hash. */\nexport const spanDiverged = (docText: string, span: { from: number; to: number; sourceHash: string }): boolean =>\n  sourceHash(docText.slice(span.from, span.to)) !== span.sourceHash;\n\n/**\n * Reactive driver: on doc change, mark edit-touched spans stale (immediate visual feedback), then\n * after idle re-parse and dispatch fresh analysis. Re-hashing is bounded to spans overlapping the\n * edit. This driver intentionally treats the whole document as a single span; per-span/sub-document\n * analysis is left to external drivers dispatching `setAnalysis` (e.g. the transcription pipeline).\n */\nconst reactiveDriver = (parse: NonNullable<PosOptions['parse']>, debounceMs: number): Extension => {\n  // Monotonic id so a slow parse that resolves after a newer one (or after the doc moved on) is\n  // discarded rather than overwriting current analysis with stale spans. Parser rejections are\n  // swallowed so they don't surface as unhandled rejections and the prior analysis is preserved.\n  let latest = 0;\n  const applyParse = (view: EditorView) => {\n    const text = view.state.doc.toString();\n    const id = ++latest;\n    void parse(text)\n      .then((document) => {\n        if (id !== latest || view.state.doc.toString() !== text) {\n          return;\n        }\n        view.dispatch({ effects: setAnalysis.of({ from: 0, to: text.length, document }) });\n      })\n      .catch(() => {});\n  };\n\n  const run = debounce((view: EditorView) => applyParse(view), debounceMs);\n\n  // Parse existing content on mount so reactive mode decorates immediately, not just after an edit.\n  // Dispatch is deferred via the parse promise's microtask; CodeMirror forbids dispatching during construction.\n  const initial = ViewPlugin.define((view) => {\n    if (view.state.doc.length > 0) {\n      applyParse(view);\n    }\n    return {};\n  });\n\n  const onEdit = EditorView.updateListener.of((update: ViewUpdate) => {\n    if (!update.docChanged) {\n      return;\n    }\n    // Collect the post-edit ranges this transaction touched; re-hashing is bounded to spans that\n    // overlap them, so cost scales with the edit, not the total analyzed text.\n    const touched: Array<[number, number]> = [];\n    update.changes.iterChangedRanges((_fromA, _toA, fromB, toB) => touched.push([fromB, toB]));\n    const overlapsEdit = (span: PosSpan) => touched.some(([from, to]) => from <= span.to && to >= span.from);\n\n    // Immediate: mark any diverged touched span stale so its decorations dim until the re-parse lands.\n    const text = update.state.doc.toString();\n    const effects = update.state\n      .field(posAnalysisField)\n      .filter((span) => !span.stale && overlapsEdit(span) && spanDiverged(text, span))\n      .map((span) => markStale.of({ from: span.from, to: span.to }));\n    if (effects.length > 0) {\n      update.view.dispatch({ effects });\n    }\n    run(update.view);\n  });\n\n  return [initial, onEdit];\n};\n\n/** Colored underline per UPOS tag (stale dimming lives in the always-on base theme). */\nexport const posTheme = (): Extension =>\n  EditorView.theme({\n    '.cm-pos': {\n      borderRadius: '0.125rem',\n    },\n    ...Object.fromEntries(\n      Object.entries(POS_HUE).map(([upos, hue]) => [\n        `.cm-pos-${upos}`,\n        {\n          borderBottom: `2px solid var(--color-${hue}-500)`,\n        },\n      ]),\n    ),\n  });\n\n// Stale dimming must apply even when the underline theme is disabled (e.g. popover-only mode), so a\n// diverged span never presents confident decorations.\nconst staleTheme = EditorView.theme({\n  '.cm-pos-stale': {\n    opacity: '0.4',\n  },\n});\n\nconst popoverTheme = EditorView.theme({\n  '.cm-pos-popover': {\n    display: 'flex',\n    alignItems: 'center',\n    gap: '6px',\n    padding: '2px 6px',\n  },\n  '.cm-pos-swatch': {\n    width: '8px',\n    height: '8px',\n    borderRadius: '2px',\n    flexShrink: '0',\n  },\n  '.cm-pos-popover-tag': {\n    fontWeight: '600',\n  },\n  '.cm-pos-popover-label': {\n    opacity: '0.7',\n  },\n});\n","//\n// Copyright 2023 DXOS.org\n// Based on a demo by Joel Besada and Kushagra Gour.\n// https://twitter.com/JoelBesada/status/670343885655293952\n// https://github.com/chinchang/code-blast-codemirror/blob/master/code-blast.js\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView, keymap } from '@codemirror/view';\nimport defaultsDeep from 'lodash.defaultsdeep';\n\nimport { throttle } from '@dxos/async';\nimport { invariant } from '@dxos/invariant';\n\nexport type BlastOptions = {\n  effect?: number;\n  maxParticles: number;\n  particleGravity: number;\n  particleAlphaFadeout: number;\n  particleSize: { min: number; max: number };\n  particleNumRange: { min: number; max: number };\n  particleVelocityRange: { x: [number, number]; y: [number, number] };\n  particleShrinkRate: number;\n  shakeIntensity: number;\n  color?: () => number[];\n};\n\nexport const defaultOptions: BlastOptions = {\n  effect: 2,\n  maxParticles: 200,\n  particleGravity: 0.08,\n  particleAlphaFadeout: 0.996,\n  particleSize: { min: 2, max: 8 },\n  particleNumRange: { min: 5, max: 10 },\n  particleVelocityRange: { x: [-1, 1], y: [-3.5, -1.5] },\n  particleShrinkRate: 0.95,\n  shakeIntensity: 5,\n};\n\n/**\n * Particle 'code blast' cursor effect (demo/novelty).\n */\nexport const blast = (options: Partial<BlastOptions> = defaultOptions): Extension => {\n  let blaster: Blaster | undefined;\n  let last = 0;\n\n  const getPoint = (view: EditorView, pos: number) => {\n    const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};\n    const element = document.elementFromPoint(x, y);\n\n    const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};\n    const point = { x: x - left, y: y - top };\n\n    return { element, point };\n  };\n\n  return [\n    // Cursor moved.\n    EditorView.updateListener.of((update) => {\n      // NOTE: The MarkdownEditor may recreated the EditorView.\n      if (blaster?.node !== update.view.scrollDOM) {\n        if (blaster) {\n          blaster.destroy();\n        }\n\n        blaster = new Blaster(\n          update.view.scrollDOM,\n          defaultsDeep(\n            {\n              particleGravity: 0.2,\n              particleShrinkRate: 0.995,\n              color: () => [100 + Math.random() * 100, 0, 0],\n            },\n            options,\n            defaultOptions,\n          ),\n        );\n        blaster.initialize();\n        blaster.start(); // TODO(burdon): Stop/clean-up.\n      } else {\n        blaster.resize();\n      }\n\n      const current = update.state.selection.main.head;\n      if (current !== last) {\n        last = current;\n        // TODO(burdon): Null if end of line.\n        const { element, point } = getPoint(update.view, current - 1);\n        if (element && point) {\n          blaster.spawn({ element, point });\n        }\n      }\n    }),\n\n    keymap.of([\n      {\n        any: (_, event) => {\n          if (blaster) {\n            if (event.key === 'Enter' && event.shiftKey) {\n              blaster.shake({ time: 0.8 });\n              return true;\n            }\n          }\n\n          return false;\n        },\n      },\n    ]),\n  ];\n};\n\n//\n// Blaster (no CM deps).\n//\n\nclass Blaster {\n  private readonly _effect: Effect;\n\n  _canvas: HTMLCanvasElement | undefined;\n  _ctx: CanvasRenderingContext2D | undefined | null;\n\n  _running = false;\n  _lastTime: number | undefined;\n  _shakeTime = 0;\n  _shakeTimeMax = 0;\n\n  _particles: Particle[] = [];\n  _particlePointer = 0;\n\n  _lastPoint = { x: 0, y: 0 };\n\n  constructor(\n    private readonly _node: HTMLElement,\n    private readonly _options: BlastOptions,\n  ) {\n    this._effect = this._options.effect === 1 ? new Effect1(_options) : new Effect2(_options);\n  }\n\n  get node() {\n    return this._node;\n  }\n\n  initialize(): void {\n    // console.log('initialize');\n    invariant(!this._canvas && !this._ctx);\n\n    this._canvas = document.createElement('canvas');\n    this._canvas.id = 'code-blast-canvas';\n    this._canvas.style.position = 'absolute';\n    this._canvas.style.zIndex = '0';\n    this._canvas.style.pointerEvents = 'none';\n    // this._canvas.style.border = '2px dashed red';\n\n    this._ctx = this._canvas.getContext('2d');\n\n    const parent = this._node.parentElement?.parentElement;\n    parent?.appendChild(this._canvas);\n\n    this.resize();\n  }\n\n  destroy(): void {\n    this.stop();\n    // console.log('destroy');\n    if (this._canvas) {\n      this._canvas.remove();\n      this._canvas = undefined;\n      this._ctx = undefined;\n    }\n  }\n\n  resize(): void {\n    if (this._node.parentElement && this._canvas) {\n      const { offsetLeft: x, offsetTop: y, offsetWidth: width, offsetHeight: height } = this._node.parentElement;\n      this._canvas.style.top = `${y}px`;\n      this._canvas.style.left = `${x}px`;\n      this._canvas.width = width;\n      this._canvas.height = height;\n    }\n  }\n\n  start(): void {\n    // console.log('start');\n    invariant(this._canvas && this._ctx);\n    this._running = true;\n    this.loop();\n  }\n\n  stop(): void {\n    // console.log('stop');\n    this._running = false;\n    this._node.style.transform = 'translate(0px, 0px)';\n  }\n\n  loop(): void {\n    if (!this._running || !this._canvas || !this._ctx) {\n      return;\n    }\n\n    this._ctx.clearRect(0, 0, this._canvas.width ?? 0, this._canvas.height ?? 0);\n\n    // Calculate the delta from the previous frame.\n    const now = new Date().getTime();\n    this._lastTime ??= now;\n    const dt = (now - this._lastTime) / 1000;\n    this._lastTime = now;\n\n    if (this._shakeTime > 0) {\n      this._shakeTime -= dt;\n      const magnitude = (this._shakeTime / this._shakeTimeMax) * this._options.shakeIntensity;\n      const shakeX = random(-magnitude, magnitude);\n      const shakeY = random(-magnitude, magnitude);\n      this._node!.style.transform = `translate(${shakeX}px,${shakeY}px)`;\n    }\n\n    this.drawParticles();\n\n    requestAnimationFrame(this.loop.bind(this));\n  }\n\n  shake = throttle(({ time }: { time: number }) => {\n    this._shakeTime = this._shakeTimeMax || time;\n    this._shakeTimeMax = time;\n  }, 100);\n\n  spawn = throttle(({ element, point }: { element: Element; point: { x: number; y: number } }) => {\n    const color = getRGBComponents(element, this._options.color);\n    const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);\n    const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;\n    this._lastPoint = point;\n    for (let i = numParticles; i--; i > 0) {\n      this._particles[this._particlePointer] = this._effect.create(point.x - dir * 16, point.y, color);\n      this._particlePointer = (this._particlePointer + 1) % this._options.maxParticles;\n    }\n  }, 100);\n\n  drawParticles(): void {\n    for (let i = this._particles.length; i--; i > 0) {\n      const particle = this._particles[i];\n      if (!particle) {\n        continue;\n      }\n\n      if (particle.alpha < 0.01 || particle.size <= 0.5) {\n        continue;\n      }\n\n      this._effect.update(this._ctx!, particle);\n    }\n  }\n}\n\n//\n// Effects\n//\n\ntype Particle = {\n  x: number;\n  y: number;\n  vx: number;\n  vy: number;\n  size: number;\n  color: (string | number)[];\n  alpha: number;\n  theta?: number;\n  drag?: number;\n  wander?: number;\n};\n\nabstract class Effect {\n  constructor(protected readonly _options: BlastOptions) {}\n  abstract create(x: number, y: number, color: Particle['color']): Particle;\n  abstract update(ctx: CanvasRenderingContext2D, particle: Particle): void;\n}\n\nclass Effect1 extends Effect {\n  create(x: number, y: number, color: Particle['color']) {\n    return {\n      x,\n      y: y + 10,\n      vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),\n      vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),\n      size: random(this._options.particleSize.min, this._options.particleSize.max),\n      color,\n      alpha: 1,\n    };\n  }\n\n  update(ctx: CanvasRenderingContext2D, particle: Particle): void {\n    particle.vy += this._options.particleGravity;\n    particle.x += particle.vx;\n    particle.y += particle.vy;\n    particle.alpha *= this._options.particleAlphaFadeout;\n\n    ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;\n    ctx.fillRect(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, particle.size);\n  }\n}\n\n/**\n * Based on Soulwire's demo.\n * http://codepen.io/soulwire/pen/foktm\n */\nclass Effect2 extends Effect {\n  create(x: number, y: number, color: Particle['color']) {\n    return {\n      x,\n      y: y + 10,\n      vx: random(this._options.particleVelocityRange.x[0], this._options.particleVelocityRange.x[1]),\n      vy: random(this._options.particleVelocityRange.y[0], this._options.particleVelocityRange.y[1]),\n      size: random(this._options.particleSize.min, this._options.particleSize.max),\n      color,\n      alpha: 1,\n      theta: (random(0, 360) * Math.PI) / 180,\n      drag: 0.92,\n      wander: 0.15,\n    };\n  }\n\n  update(ctx: CanvasRenderingContext2D, particle: Particle): void {\n    particle.vy += this._options.particleGravity;\n    particle.x += particle.vx;\n    particle.y += particle.vy;\n    particle.vx *= particle.drag!;\n    particle.vy *= particle.drag!;\n    particle.theta! += random(-0.5, 0.5);\n    particle.vx += Math.sin(particle.theta!) * 0.1;\n    particle.vy += Math.cos(particle.theta!) * 0.1;\n    particle.size *= this._options.particleShrinkRate;\n    particle.alpha *= this._options.particleAlphaFadeout;\n\n    ctx.fillStyle = `rgba(${particle.color[0]},${particle.color[1]},${particle.color[2]},${particle.alpha})`;\n    ctx.beginPath();\n    ctx.arc(Math.round(particle.x - 1), Math.round(particle.y - 1), particle.size, 0, 2 * Math.PI);\n    ctx.fill();\n  }\n}\n\n//\n// Utils\n//\n\nconst getRGBComponents = (node: Element, color: BlastOptions['color']): Particle['color'] => {\n  if (typeof color === 'function') {\n    return color();\n  }\n\n  const bgColor = getComputedStyle(node).color;\n  if (bgColor) {\n    const x = bgColor.match(/(\\d+), (\\d+), (\\d+)/)?.slice(1);\n    if (x) {\n      return x;\n    }\n  }\n\n  return [50, 50, 50];\n};\n\nconst random = (min: number, max: number) => {\n  if (!max) {\n    max = min;\n    min = 0;\n  }\n\n  return min + ~~(Math.random() * (max - min + 1));\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { keymap } from '@codemirror/view';\n\nconst defaultItems = ['hello world!', 'this is a test.', 'this is [DXOS](https://dxos.org)'];\n\nexport type SnippetsOptions = {\n  delay?: number;\n  items?: string[];\n};\n\n/**\n * Configurable plugin that lets users cycle through pre-configured input snippets.\n */\n// TODO(burdon): Review https://github.com/sergeche/codemirror-movie?tab=readme-ov-file\nexport const snippets = ({ delay = 75, items = defaultItems }: SnippetsOptions = {}): Extension => {\n  let timer: ReturnType<typeof setTimeout> | undefined;\n  let index = 0; // TODO(burdon): Make global.\n\n  return [\n    keymap.of([\n      {\n        // Reset.\n        key: \"alt-meta-'\",\n        run: () => {\n          clearTimeout(timer);\n          index = 0;\n          return true;\n        },\n      },\n      {\n        // Next snippet.\n        // TODO(burdon): Press 1-9 to select snippet?\n        key: \"Shift-Meta-'\",\n        run: (view) => {\n          clearTimeout(timer);\n          // TODO(burdon): Add space if needed.\n          if (items.length === 0) {\n            return false;\n          }\n\n          const text = items[index++];\n          if (index >= items.length) {\n            index = 0;\n          }\n\n          let offset = 0;\n          const insert = (delayMs = 0) => {\n            timer = setTimeout(() => {\n              const pos = view.state.selection.main.head;\n              view.dispatch({\n                changes: { from: pos, insert: text[offset++] },\n                selection: { anchor: pos + 1 },\n              });\n\n              if (offset < text.length) {\n                insert(Math.random() * delay * (text[offset] === ' ' ? 2 : 1));\n              }\n            }, delayMs);\n          };\n\n          insert();\n          return true;\n        },\n      },\n    ]),\n  ];\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { LanguageSupport, StreamLanguage, type StringStream } from '@codemirror/language';\nimport { type Tag, tags } from '@lezer/highlight';\n\n//\n// Token rules mirror `@dxos/datalog`'s lexer (`packages/common/datalog/src/internal/lexer.ts`) so the editor colours\n// exactly what the parser reads; that package stays free of CodeMirror, so the tokenizer is restated here.\n//\n\n/** Token names emitted by the tokenizer, mapped to standard tags so the editor's highlight style colours them. */\nconst tokenTable = {\n  lineComment: tags.lineComment,\n  blockComment: tags.blockComment,\n  string: tags.string,\n  number: tags.number,\n  // A digit run glued to letters (`2d`) is a symbol to the lexer, but reads as a quantity.\n  duration: tags.special(tags.number),\n  variable: tags.variableName,\n  // The predicate a clause defines.\n  head: tags.definition(tags.function(tags.variableName)),\n  predicate: tags.function(tags.variableName),\n  atom: tags.atom,\n  negation: tags.operatorKeyword,\n  aggregate: tags.keyword,\n  definition: tags.definitionOperator,\n  compare: tags.compareOperator,\n  separator: tags.separator,\n  terminator: tags.punctuation,\n  paren: tags.paren,\n  brace: tags.brace,\n  invalid: tags.invalid,\n} satisfies Record<string, Tag>;\n\nexport type DatalogToken = keyof typeof tokenTable;\n\ntype DatalogState = {\n  /** Inside a `/* … *\\/` comment that spans lines. */\n  comment: boolean;\n  /** No predicate yet in the current clause, so the next one is its head. */\n  head: boolean;\n};\n\nconst AGGREGATES = new Set(['count', 'min', 'max', 'sum']);\n\nconst PUNCTUATION: Record<string, DatalogToken | undefined> = {\n  ',': 'separator',\n  '.': 'terminator',\n  '(': 'paren',\n  ')': 'paren',\n  '{': 'brace',\n  '}': 'brace',\n  '!': 'negation',\n  ':': 'aggregate',\n};\n\nconst skipBlockComment = (stream: StringStream, state: DatalogState): DatalogToken => {\n  state.comment = !stream.skipTo('*/');\n  if (state.comment) {\n    stream.skipToEnd();\n  } else {\n    stream.pos += 2;\n  }\n  return 'blockComment';\n};\n\nconst token = (stream: StringStream, state: DatalogState): DatalogToken | null => {\n  if (state.comment) {\n    return skipBlockComment(stream, state);\n  }\n  if (stream.eatSpace()) {\n    return null;\n  }\n  if (stream.match('%') || stream.match('//')) {\n    stream.skipToEnd();\n    return 'lineComment';\n  }\n  if (stream.match('/*')) {\n    return skipBlockComment(stream, state);\n  }\n\n  const quote = stream.peek();\n  if (quote === '\"' || quote === \"'\") {\n    stream.next();\n    let escaped = false;\n    let char: string | void;\n    while ((char = stream.next()) != null) {\n      if (char === quote && !escaped) {\n        return 'string';\n      }\n      escaped = !escaped && char === '\\\\';\n    }\n    // The lexer rejects a string left open at the end of the line.\n    return 'invalid';\n  }\n\n  if (stream.match(/^-?\\d+(\\.\\d+)?(?![A-Za-z_\\d])/)) {\n    return 'number';\n  }\n  const word = stream.match(/^[A-Za-z0-9_][A-Za-z0-9_]*/);\n  if (word && typeof word !== 'boolean') {\n    const text = word[0];\n    if (/^\\d/.test(text)) {\n      return 'duration';\n    }\n    if (/^[A-Z_]/.test(text)) {\n      return 'variable';\n    }\n    // The parser reads `not` as negation unless it is itself a rule head (`not :- …`).\n    if (text === 'not' && !stream.match(/^\\s*:-/, false)) {\n      return 'negation';\n    }\n    // `N = count : { … }`: an aggregate name is followed by its `:`, which a predicate never is.\n    if (AGGREGATES.has(text) && stream.match(/^\\s*:(?!-)/, false)) {\n      return 'aggregate';\n    }\n    if (stream.match(/^\\s*\\(/, false)) {\n      if (state.head) {\n        state.head = false;\n        return 'head';\n      }\n      return 'predicate';\n    }\n    return 'atom';\n  }\n\n  if (stream.match(/^(<=|>=|!=|=|<|>)/)) {\n    return 'compare';\n  }\n  if (stream.match(':-')) {\n    state.head = false;\n    return 'definition';\n  }\n  const char = stream.next();\n  if (char === '.') {\n    state.head = true;\n  }\n  return (char ? PUNCTUATION[char] : undefined) ?? 'invalid';\n};\n\n/** Datalog in the `@dxos/datalog` dialect (Soufflé-like rules, `not`/`!` negation, `count : { … }` aggregates). */\nexport const datalogLanguage = StreamLanguage.define<DatalogState>({\n  name: 'datalog',\n  startState: () => ({ comment: false, head: true }),\n  copyState: (state) => ({ ...state }),\n  token,\n  tokenTable,\n  languageData: {\n    commentTokens: { line: '%', block: { open: '/*', close: '*/' } },\n    closeBrackets: { brackets: ['(', '{', '\"', \"'\"] },\n  },\n});\n\n/** Datalog language support; pair with the theme's syntax highlighting to colour it. */\nexport const datalog = (): LanguageSupport => new LanguageSupport(datalogLanguage);\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { json as jsonLanguage, jsonParseLinter } from '@codemirror/lang-json';\nimport { type LintSource, linter } from '@codemirror/lint';\nimport { type Extension } from '@codemirror/state';\nimport Ajv, { type ValidateFunction } from 'ajv';\n\nimport type * as JsonSchema from '@dxos/echo/JsonSchema';\n\nexport type JsonOptions = {\n  schema?: JsonSchema.JsonSchema;\n};\n\n/**\n * JSON editing extension: the JSON language (syntax highlighting, folding, bracket matching) plus a\n * linter. When `schema` (a JSON Schema) is given the linter validates against it; otherwise it reports\n * only parse errors.\n */\nexport const json = ({ schema }: JsonOptions = {}): Extension => {\n  let lintSource: LintSource = jsonParseLinter();\n  if (schema) {\n    // NOTE: Relaxing strict mode to allow additional custom schema properties.\n    const ajv = new Ajv({ allErrors: false, strict: false });\n    const validate = ajv.compile(schema);\n    lintSource = schemaLinter(validate);\n  }\n\n  return [jsonLanguage(), linter(lintSource)];\n};\n\nconst schemaLinter =\n  (validate: ValidateFunction): LintSource =>\n  (view) => {\n    try {\n      const jsonText = view.state.doc.toString();\n      const jsonData = JSON.parse(jsonText);\n      const valid = validate(jsonData);\n      if (valid) {\n        return [];\n      }\n\n      return (\n        validate.errors?.map((err) => ({\n          from: 0,\n          to: jsonText.length,\n          severity: 'error',\n          message: `${err.instancePath || '(root)'} ${err.message}`,\n        })) ?? []\n      );\n    } catch (err: unknown) {\n      return [\n        {\n          from: 0,\n          to: view.state.doc.length,\n          severity: 'error',\n          message: 'Invalid JSON: ' + (err instanceof Error ? err.message : String(err)),\n        },\n      ];\n    }\n  };\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { snippet } from '@codemirror/autocomplete';\nimport { syntaxTree } from '@codemirror/language';\nimport {\n  type ChangeSpec,\n  EditorSelection,\n  type EditorState,\n  type Extension,\n  type Line,\n  type StateCommand,\n  type Text,\n} from '@codemirror/state';\nimport { EditorView, type ViewUpdate, keymap } from '@codemirror/view';\nimport { type SyntaxNode, type SyntaxNodeRef } from '@lezer/common';\n\nimport { debounceAndThrottle } from '@dxos/async';\nimport { trim } from '@dxos/util';\n\n// Markdown refs:\n// https://github.github.com/gfm\n// https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax\n\n// Describes the formatting situation of the selection in an editor state.\n// For inline styles `strong`, `emphasis`, `strikethrough`, and `code`,\n// the field only holds true when *all* selected text has the style,\n// or when the selection is a cursor inside such a style.\nexport type Formatting = Partial<{\n  blankLine: boolean;\n  // The type of the block at the selection.\n  // If multiple different block types are selected, this will hold null.\n  blockType:\n    | 'codeblock'\n    | 'heading1'\n    | 'heading2'\n    | 'heading3'\n    | 'heading4'\n    | 'heading5'\n    | 'heading6'\n    | 'paragraph'\n    | 'tablecell'\n    | null;\n  // Whether all selected text is wrapped in a blockquote.\n  blockQuote: boolean;\n  // Whether the selected text is strong.\n  strong: boolean;\n  // Whether the selected text is emphasized.\n  emphasis: boolean;\n  // Whether the selected text is stricken through.\n  strikethrough: boolean;\n  // Whether the selected text is inline code.\n  code: boolean;\n  // Whether there are links in the selected text.\n  link: boolean;\n  // If all selected blocks have the same (innermost) list style, that is indicated here.\n  listStyle: null | 'ordered' | 'bullet' | 'task';\n}>;\n\nexport const formattingEquals = (a: Formatting, b: Formatting) =>\n  a.blockType === b.blockType &&\n  a.strong === b.strong &&\n  a.emphasis === b.emphasis &&\n  a.strikethrough === b.strikethrough &&\n  a.code === b.code &&\n  a.link === b.link &&\n  a.listStyle === b.listStyle &&\n  a.blockQuote === b.blockQuote;\n\nexport enum Inline {\n  Strong = 0,\n  Emphasis = 1,\n  Strikethrough = 2,\n  Code = 3,\n}\n\nexport enum List {\n  Ordered,\n  Bullet,\n  Task,\n}\n\n//\n// Formatting extension.\n//\n\nexport type FormattingOptions = {};\n\n/**\n * Keymap binding meta-b and meta-i to toggle strong and emphasis.\n */\nexport const formattingKeymap = (_options: FormattingOptions = {}): Extension => {\n  return [\n    keymap.of([\n      {\n        key: 'meta-b',\n        run: toggleStrong,\n      },\n      {\n        key: 'meta-i',\n        run: toggleEmphasis,\n      },\n    ]),\n  ];\n};\n\n//\n// Headings\n//\n\nexport const setHeading = (level: number): StateCommand => {\n  return ({ state, dispatch }) => {\n    const {\n      selection: { ranges },\n      doc,\n    } = state;\n    const changes: ChangeSpec[] = [];\n    let prevBlock = -1;\n    for (const range of ranges) {\n      let sawBlock = false;\n      syntaxTree(state).iterate({\n        from: range.from,\n        to: range.to,\n        enter: (node) => {\n          if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {\n            return;\n          }\n          sawBlock = true;\n          prevBlock = node.from;\n          const blockType = Textblocks[node.name];\n          const isHeading = /heading(\\d)/.exec(blockType);\n          const curLevel = isHeading ? +isHeading[1] : node.name === 'Paragraph' ? 0 : -1;\n          if (curLevel < 0 || curLevel === level) {\n            return;\n          }\n          if (curLevel === 0) {\n            changes.push({ from: node.from, insert: '#'.repeat(level) + ' ' });\n          } else if (node.name === 'SetextHeading1' || node.name === 'SetextHeading2') {\n            // Change Setext heading to regular one.\n            const nextLine = doc.lineAt(node.to);\n            if (level) {\n              changes.push({ from: node.from, insert: '#'.repeat(level) + ' ' });\n            }\n            changes.push({ from: nextLine.from - 1, to: nextLine.to });\n          } else {\n            // Adjust the level of an ATX heading.\n            if (level === 0) {\n              changes.push({ from: node.from, to: Math.min(node.to, node.from + curLevel + 1) });\n            } else if (level < curLevel) {\n              changes.push({ from: node.from, to: node.from + (curLevel - level) });\n            } else {\n              changes.push({ from: node.from, insert: '#'.repeat(level - curLevel) });\n            }\n          }\n        },\n      });\n      let line;\n      if (!sawBlock && range.empty && level > 0 && !/\\S/.test((line = state.doc.lineAt(range.from)).text)) {\n        changes.push({ from: line.from, to: line.to, insert: '#'.repeat(level) + ' ' });\n      }\n    }\n\n    if (!changes.length) {\n      return false;\n    }\n\n    const changeSet = state.changes(changes);\n    dispatch(\n      state.update({\n        changes: changeSet,\n        selection: state.selection.map(changeSet, 1),\n        userEvent: 'format.setHeading',\n        scrollIntoView: true,\n      }),\n    );\n    return true;\n  };\n};\n\n//\n// Styles\n//\n\nexport const setStyle = (type: Inline, enable: boolean): StateCommand => {\n  return ({ state, dispatch }) => {\n    const marker = inlineMarkerText(type);\n    const changes = state.changeByRange((range) => {\n      // Special case for markers directly around the cursor, which will often not be parsed as valid styling.\n      if (!enable && range.empty) {\n        const after = state.doc.sliceString(range.head, range.head + 6);\n        const found = after.indexOf(marker);\n        if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {\n          const before = state.doc.sliceString(range.head - 6, range.head);\n          if (\n            before.slice(before.length - found - marker.length, before.length - found) === marker &&\n            [...before.slice(before.length - found)].reverse().join('') === after.slice(0, found)\n          ) {\n            return {\n              changes: [\n                { from: range.head - marker.length - found, to: range.head - found },\n                { from: range.head + found, to: range.head + found + marker.length },\n              ],\n              range: EditorSelection.cursor(range.from - marker.length),\n            };\n          }\n        }\n      }\n\n      const changes: ChangeSpec[] = [];\n      // Used to add insertions that should happen *after* any other insertions at the same position.\n      const changesAtEnd: ChangeSpec[] = [];\n      let blockStart = -1;\n      let blockEnd = -1;\n      let startCovered: boolean | { from: number; to: number } = false;\n      let endCovered: boolean | { from: number; to: number } = false;\n      let { from, to } = range;\n\n      // Iterate the selected range. For each textblock, determine a start and end position,\n      // the overlap of the selected range and the block's extent, that should be styled/unstyled.\n      syntaxTree(state).iterate({\n        from,\n        to,\n        enter: (node) => {\n          const { name } = node;\n          if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== 'codeblock') {\n            // Set up for this textblock.\n            blockStart = blockContentStart(node);\n            blockEnd = blockContentEnd(node, state.doc);\n            startCovered = endCovered = false;\n          } else if (name === 'Link' || (name === 'Image' && enable)) {\n            // If the range partially overlaps a link or image, expand it to cover it.\n            if (from < node.from && to > node.from && to <= node.to) {\n              to = node.to;\n            } else if (to > node.to && from >= node.from && from < node.to) {\n              from = node.from;\n            }\n          } else if (IgnoreInline.has(name) && enable) {\n            // Move endpoints out of markers.\n            if (node.from < from && node.to > from) {\n              if (to === from) {\n                to = node.to;\n              }\n              from = node.to;\n            }\n            if (node.from < to && node.to > to) {\n              to = node.from;\n            }\n          } else if (Object.hasOwn(InlineMarker, name)) {\n            // This is an inline marker node.\n            const markType = InlineMarker[name];\n            const size = inlineMarkerText(markType).length;\n            const openEnd = node.from + size;\n            const closeStart = node.to - size;\n            // Determine whether the start/end of the range is covered\n            // by this.\n            if (markType === type) {\n              if (openEnd <= from && closeStart >= from) {\n                startCovered =\n                  !enable && openEnd === skipMarkers(from, node.node, -1, openEnd)\n                    ? { from: node.from, to: openEnd }\n                    : true;\n              }\n              if (openEnd <= to && closeStart >= to) {\n                endCovered =\n                  !enable && closeStart === skipMarkers(to, node.node, 1, closeStart)\n                    ? { from: closeStart, to: node.to }\n                    : true;\n              }\n            }\n            // Marks of the same type in range, or any mark if we're adding code style, need to be removed.\n            if (markType === type || (type === Inline.Code && enable)) {\n              if (node.from >= from && openEnd <= to) {\n                changes.push({ from: node.from, to: openEnd });\n                if (markType !== type && closeStart >= to) {\n                  // End marker outside, move start\n                  changesAtEnd.push({\n                    from: skipSpaces(Math.min(to, blockEnd), state.doc, 1, blockEnd),\n                    insert: inlineMarkerText(markType),\n                  });\n                }\n              }\n              if (closeStart >= from && node.to <= to) {\n                changes.push({ from: closeStart, to: node.to });\n                if (markType !== type && openEnd <= from) {\n                  // Start marker outside, move end\n                  changes.push({\n                    from: skipSpaces(Math.max(from, blockStart), state.doc, -1, blockStart),\n                    insert: inlineMarkerText(markType),\n                  });\n                }\n              }\n            }\n          }\n        },\n        leave: (node) => {\n          if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== 'codeblock') {\n            // Finish opening/closing the marks for this textblock.\n            const rangeStart = Math.max(from, blockStart);\n            const rangeEnd = Math.min(to, blockEnd);\n            if (enable) {\n              if (!startCovered) {\n                changes.push({ from: rangeStart, insert: marker });\n              }\n              if (!endCovered) {\n                changes.push({ from: rangeEnd, insert: marker });\n              }\n            } else {\n              if (typeof startCovered === 'object') {\n                changes.push(startCovered);\n              } else if (startCovered) {\n                changes.push({ from: skipSpaces(rangeStart, state.doc, -1, blockStart), insert: marker });\n              }\n              if (typeof endCovered === 'object') {\n                changes.push(endCovered);\n              } else if (endCovered) {\n                changes.push({ from: skipSpaces(rangeEnd, state.doc, 1, blockEnd), insert: marker });\n              }\n            }\n          }\n        },\n      });\n\n      if (blockStart < 0 && range.empty && enable && !/\\S/.test(state.doc.lineAt(range.from).text)) {\n        return {\n          changes: { from: range.head, insert: marker + marker },\n          range: EditorSelection.cursor(range.head + marker.length),\n        };\n      }\n\n      const changeSet = state.changes(changes.concat(changesAtEnd));\n      return {\n        changes: changeSet,\n        range:\n          range.empty && !changeSet.empty\n            ? EditorSelection.cursor(range.head + marker.length)\n            : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1)),\n      };\n    });\n\n    dispatch(\n      state.update(changes, {\n        userEvent: enable ? 'format.style.add' : 'format.style.remove',\n        scrollIntoView: true,\n      }),\n    );\n\n    return true;\n  };\n};\n\nexport const addStyle = (style: Inline): StateCommand => setStyle(style, true);\n\nexport const removeStyle = (style: Inline): StateCommand => setStyle(style, false);\n\nexport const toggleStyle = (style: Inline): StateCommand => {\n  return (arg) => {\n    const form = getFormatting(arg.state);\n    return setStyle(\n      style,\n      style === Inline.Strong\n        ? !form.strong\n        : style === Inline.Emphasis\n          ? !form.emphasis\n          : style === Inline.Strikethrough\n            ? !form.strikethrough\n            : !form.code,\n    )(arg);\n  };\n};\n\nexport const toggleStrong = toggleStyle(Inline.Strong);\nexport const toggleEmphasis = toggleStyle(Inline.Emphasis);\nexport const toggleStrikethrough = toggleStyle(Inline.Strikethrough);\nexport const toggleInlineCode = toggleStyle(Inline.Code);\n\nconst inlineMarkerText = (type: Inline) =>\n  type === Inline.Strong ? '**' : type === Inline.Strikethrough ? '~~' : type === Inline.Emphasis ? '*' : '`';\n\n//\n// Utils\n//\n\nconst blockContentStart = (node: SyntaxNodeRef) => {\n  const atx = /^ATXHeading(\\d)/.exec(node.name);\n  if (atx) {\n    return Math.min(node.to, node.from + +atx[1] + 1);\n  }\n  return node.from;\n};\n\nconst blockContentEnd = (node: SyntaxNodeRef, doc: Text) => {\n  const setext = /^SetextHeading(\\d)/.exec(node.name);\n  const lastLine = doc.lineAt(node.to);\n  if (setext || /^[\\s>]*$/.exec(lastLine.text)) {\n    return lastLine.from - 1;\n  }\n  return node.to;\n};\n\nconst skipSpaces = (pos: number, doc: Text, dir: -1 | 1, limit?: number) => {\n  const line = doc.lineAt(pos);\n  while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === ' ') {\n    pos += dir;\n  }\n  return pos;\n};\n\nconst skipMarkers = (pos: number, tree: SyntaxNode, dir: -1 | 1, limit?: number) => {\n  for (;;) {\n    const next = tree.resolve(pos, dir);\n    if (!IgnoreInline.has(next.name)) {\n      return pos;\n    }\n    const moveTo = dir < 0 ? next.from : next.to;\n    if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {\n      return pos;\n    }\n    pos = moveTo;\n  }\n};\n\n// TODO(burdon): Define and trigger snippets for codeblock, table, etc.\nconst snippets = {\n  codeblock: snippet(['```#{}', '', '```'].join('\\n')),\n  table: snippet(\n    trim`\n      | #{C-1} | #{C-2} | #{C-3} |\n      | ---- | ---- | ---- |\n      | #{A-1} | #{B-1} | #{C-1} |\n      | #{A-2} | #{B-2} | #{C-2} |\n      | #{A-3} | #{B-3} | #{C-3} |\n    ` + '\\n',\n  ),\n};\n\n//\n// Table\n//\n\n/**\n * Inserts the table snippet at the start of the line holding `at` (default: the cursor).\n * `at` is what lets the slash menu insert at the trigger it just consumed rather than wherever the\n * selection happens to sit, so both entry points produce the same table.\n */\nexport const insertTable = (view: EditorView, at?: number) => {\n  const {\n    selection: { main },\n    doc,\n  } = view.state;\n  const { number } = doc.lineAt(at ?? main.anchor);\n  const { from } = doc.line(number);\n\n  snippets.table(view, null, from, from);\n};\n\n//\n// Links\n//\n\n// For each link in the given range, remove the link markup\nconst removeLinkInner = (from: number, to: number, changes: ChangeSpec[], state: EditorState) => {\n  syntaxTree(state).iterate({\n    from,\n    to,\n    enter: (node) => {\n      if (node.name === 'Link' && node.from < to && node.to > from) {\n        node.node.cursor().iterate((node) => {\n          const { name } = node;\n          if (name === 'LinkMark' || name === 'LinkLabel') {\n            changes.push({ from: node.from, to: node.to });\n          } else if (name === 'LinkTitle' || name === 'URL') {\n            changes.push({ from: skipSpaces(node.from, state.doc, -1), to: skipSpaces(node.to, state.doc, 1) });\n          }\n        });\n        return false;\n      }\n    },\n  });\n};\n\n// Remove all links touching the selection\nexport const removeLink: StateCommand = ({ state, dispatch }) => {\n  const changes: ChangeSpec[] = [];\n  for (const { from, to } of state.selection.ranges) {\n    removeLinkInner(from, to, changes, state);\n  }\n  if (!changes.length) {\n    return false;\n  }\n  dispatch(state.update({ changes, userEvent: 'format.link.remove', scrollIntoView: true }));\n  return true;\n};\n\n// Add link markup around the selection.\nexport const addLink = ({ url, image }: { url?: string; image?: boolean } = {}): StateCommand => {\n  return ({ state, dispatch }) => {\n    const changes = state.changeByRange((range) => {\n      let { from, to } = range;\n      const cutStyles: SyntaxNode[] = [];\n      let okay: boolean | null = null;\n      // Check whether this range is in a position where a link makes sense.\n      syntaxTree(state).iterate({\n        from,\n        to,\n        enter: (node) => {\n          if (Object.hasOwn(Textblocks, node.name)) {\n            // If the selection spans multiple textblocks or is in a code block, abort.\n            okay =\n              Textblocks[node.name] !== 'codeblock' &&\n              from >= blockContentStart(node) &&\n              to <= blockContentEnd(node, state.doc);\n          } else if (Object.hasOwn(InlineMarker, node.name)) {\n            // Look for inline styles that partially overlap the range.\n            // Expand the range over them if they start directly outside, otherwise mark them for later.\n            const sNode = node.node;\n            if (node.from < from && node.to <= to) {\n              if (sNode.firstChild!.to === from) {\n                from = node.from;\n              } else {\n                cutStyles.push(sNode);\n              }\n            } else if (node.from >= from && node.to > to) {\n              if (sNode.lastChild!.from === to) {\n                to = node.to;\n              } else {\n                cutStyles.push(sNode);\n              }\n            }\n          }\n        },\n      });\n\n      if (okay === null) {\n        // No textblock found around selection. Check if the rest of the line is empty.\n        const line = state.doc.lineAt(from);\n        okay = to <= line.to && !/\\S/.test(line.text.slice(from - line.from));\n      }\n      if (!okay) {\n        return { range };\n      }\n\n      const changes: ChangeSpec[] = [];\n      // Some changes must be moved to end of change array so that they are applied in the right order.\n      const changesAfter: ChangeSpec[] = [];\n      // Clear existing links.\n      removeLinkInner(from, to, changesAfter, state);\n      let cursorOffset = 1;\n      // Close and reopen inline styles that partially overlap the range.\n      for (const style of cutStyles) {\n        const type = InlineMarker[style.name];\n        const mark = inlineMarkerText(type);\n        if (style.from < from) {\n          // Extends before.\n          changes.push({ from: skipSpaces(from, state.doc, -1), insert: mark });\n          changesAfter.push({ from: skipSpaces(from, state.doc, 1, to), insert: mark });\n        } else {\n          changes.push({ from: skipSpaces(to, state.doc, -1, from), insert: mark });\n          const after = skipSpaces(to, state.doc, 1);\n          if (after === to) {\n            cursorOffset += mark.length;\n          }\n          changesAfter.push({ from: after, insert: mark });\n        }\n      }\n\n      // Add the link markup.\n      changes.push({ from, insert: image ? '![' : '[' }, { from: to, insert: `](${url ?? ''})` });\n      const changeSet = state.changes(changes.concat(changesAfter));\n      // Put the cursor between the title or parenthesis.\n      return {\n        changes: changeSet,\n        range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0)),\n      };\n    });\n\n    if (changes.changes.empty) {\n      return false;\n    }\n\n    dispatch(\n      state.update(changes, {\n        userEvent: 'format.link.add',\n        scrollIntoView: true,\n      }),\n    );\n    return true;\n  };\n};\n\n//\n// Lists\n//\n\nexport const addList = (type: List): StateCommand => {\n  return ({ state, dispatch }) => {\n    let lastBlock = -1;\n    let counter = 1;\n    let first = true;\n    let parentColumn: number | null = null;\n\n    // Scan the syntax tree to locate textblocks that can be wrapped.\n    const blocks: { node: SyntaxNode; counter: number; parentColumn: number | null }[] = [];\n    for (const { from, to } of state.selection.ranges) {\n      syntaxTree(state).iterate({\n        from,\n        to,\n        enter: (node) => {\n          if ((Object.hasOwn(Textblocks, node.name) && node.name !== 'TableCell') || node.name === 'Table') {\n            if (first) {\n              // For the first block, see if it follows a list,\n              // so we can take indentation and numbering information from that one.\n              let before = node.node.prevSibling;\n              while (before && /Mark$/.test(before.name)) {\n                before = before.prevSibling;\n              }\n              if (before?.name === (type === List.Ordered ? 'OrderedList' : 'BulletList')) {\n                const item = before.lastChild!;\n                const itemLine = state.doc.lineAt(item.from);\n                const itemText = itemLine.text.slice(item.from - itemLine.from);\n                parentColumn = item.from - itemLine.from + /^\\s*/.exec(itemText)![0].length;\n                if (type === List.Ordered) {\n                  const mark = /^\\s*(\\d+)[.)]/.exec(itemText);\n                  if (mark) {\n                    parentColumn += mark[1].length;\n                    counter = +mark[1] + 1;\n                  }\n                }\n              }\n              first = false;\n            }\n            if (node.from === lastBlock) {\n              return;\n            }\n\n            lastBlock = node.from;\n            blocks.push({ node: node.node, counter, parentColumn });\n            counter++;\n            return false;\n          }\n        },\n        leave: (node) => {\n          // When exiting block-level markup, reset the indentation and counter.\n          if (node.name === 'BulletList' || node.name === 'OrderedList' || node.name === 'Blockquote') {\n            counter = 1;\n            parentColumn = null;\n          }\n        },\n      });\n    }\n\n    if (!blocks.length) {\n      // Insert a new list item if the selection is empty.\n      const { from, to } = state.doc.lineAt(state.selection.main.anchor);\n      if (from === to) {\n        const insert = type === List.Bullet ? '- ' : type === List.Ordered ? '1. ' : '- [ ] ';\n        dispatch(\n          state.update({\n            changes: [\n              {\n                from,\n                insert,\n              },\n            ],\n            selection: { anchor: from + insert.length },\n            userEvent: 'format.list.add',\n            scrollIntoView: true,\n          }),\n        );\n        return true;\n      }\n\n      return false;\n    }\n\n    const changes: SimpleChange[] = [];\n    for (let i = 0; i < blocks.length; i++) {\n      const { node, counter, parentColumn } = blocks[i];\n      const nodeFrom = node.name === 'CodeBlock' ? node.from - 4 : node.from;\n      // Compute a padding based on whether we are after whitespace.\n      let padding = nodeFrom > 0 && !/\\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;\n      // On ordered lists, the number is counted in the padding.\n      if (type === List.Ordered) {\n        padding += String(counter).length;\n      }\n      let line = state.doc.lineAt(nodeFrom);\n      const column = nodeFrom - line.from;\n      // Align to the list above if possible.\n      if (parentColumn !== null && parentColumn > column) {\n        padding = Math.max(padding, parentColumn - column);\n      }\n\n      let mark;\n      if (type === List.Ordered) {\n        // Scan ahead to find the max number we're adding, adjust padding for that.\n        let max = counter;\n        for (let j = i + 1; j < blocks.length; j++) {\n          if (blocks[j].counter !== max + 1) {\n            break;\n          }\n          max++;\n        }\n        const num = String(counter);\n        padding = Math.max(String(max).length, padding);\n        mark = ' '.repeat(Math.max(0, padding - num.length)) + num + '. ';\n      } else {\n        mark = ' '.repeat(padding) + '- ' + (type === List.Task ? '[ ] ' : '');\n      }\n\n      changes.push({ from: nodeFrom, insert: mark });\n      // Add indentation for the other lines in this block.\n      while (line.to < node.to) {\n        line = state.doc.lineAt(line.to + 1);\n        const open = /^[\\s>]*/.exec(line.text)![0].length;\n        changes.push({ from: line.from + Math.min(open, column), insert: ' '.repeat(mark.length) });\n      }\n    }\n\n    // If we are inserting an ordered list and there is another one right after the last selected block,\n    // renumber that one to match the new order.\n    if (type === List.Ordered) {\n      const last = blocks[blocks.length - 1];\n      let next = last.node.nextSibling;\n      while (next && /Mark$/.test(next.name)) {\n        next = next.nextSibling;\n      }\n      if (next?.name === 'OrderedList') {\n        renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);\n      }\n    }\n    const changeSet = state.changes(changes);\n    dispatch(\n      state.update({\n        changes: changeSet,\n        selection: state.selection.map(changeSet, 1),\n        userEvent: 'format.list.add',\n        scrollIntoView: true,\n      }),\n    );\n    return true;\n  };\n};\n\ntype ListItemContext = {\n  node: SyntaxNode;\n  /** The item's first line. */\n  line: Line;\n  /** Length of the matched marker (leading whitespace + list mark, including any task marker). */\n  markLength: number;\n  /** 1-based position among the matched items of the containing list. */\n  ordinal: number;\n  /** Whether this is the last matched item of its selection range (following siblings may need renumbering). */\n  endOfRange: boolean;\n};\n\n/**\n * Invokes `callback` for each list item in the selection whose style matches `type`. Task-ness is\n * resolved per item (an item is a task when it carries its own task marker), so mixed bullet/task\n * siblings match independently of document order. Shared by `removeList` and `convertList`.\n */\nconst forEachListItem = (state: EditorState, type: List, callback: (item: ListItemContext) => void): void => {\n  let lastBlock = -1;\n  const stack: string[] = [];\n  const ordinals: number[] = [];\n  for (const { from, to } of state.selection.ranges) {\n    syntaxTree(state).iterate({\n      from,\n      to,\n      enter: (node) => {\n        const { name } = node;\n        if (name === 'BulletList' || name === 'OrderedList' || name === 'Blockquote') {\n          // Maintain block context.\n          stack.push(name);\n          ordinals.push(0);\n        }\n      },\n      leave: (node) => {\n        const { name } = node;\n        if (name === 'BulletList' || name === 'OrderedList' || name === 'Blockquote') {\n          stack.pop();\n          ordinals.pop();\n        } else if (name === 'ListItem' && node.from !== lastBlock) {\n          const context = stack[stack.length - 1];\n          const isTask = node.node.getChild('Task') != null;\n          const matches =\n            type === List.Ordered\n              ? context === 'OrderedList'\n              : context === 'BulletList' && (type === List.Task) === isTask;\n          if (!matches) {\n            return;\n          }\n          lastBlock = node.from;\n          const line = state.doc.lineAt(node.from);\n          const mark = /^\\s*(\\d+[.)] |[-*+] (\\[[ xX]\\] )?)/.exec(line.text.slice(node.from - line.from));\n          if (!mark) {\n            return;\n          }\n          ordinals[ordinals.length - 1]++;\n          callback({\n            node: node.node,\n            line,\n            markLength: mark[0].length,\n            ordinal: ordinals[ordinals.length - 1],\n            endOfRange: node.to >= to,\n          });\n        }\n      },\n    });\n  }\n};\n\ntype SimpleChange = { from: number; to?: number; insert?: string };\n\n/**\n * Changes from `candidates` that overlap neither `changes` nor an earlier kept candidate.\n * Renumbering walks siblings on the original doc, so disjoint multi-cursor ranges in the same\n * ordered list can otherwise produce overlapping specs, which `state.changes` rejects.\n */\nconst nonOverlappingChanges = (candidates: SimpleChange[], changes: SimpleChange[]): SimpleChange[] => {\n  const kept: SimpleChange[] = [];\n  const overlaps = (a: SimpleChange, b: SimpleChange) => a.from < (b.to ?? b.from) && b.from < (a.to ?? a.from);\n  for (const candidate of candidates) {\n    if (![...changes, ...kept].some((change) => overlaps(candidate, change))) {\n      kept.push(candidate);\n    }\n  }\n  return kept;\n};\n\nexport const removeList = (type: List): StateCommand => {\n  return ({ state, dispatch }) => {\n    const changes: SimpleChange[] = [];\n    const renumbers: SimpleChange[] = [];\n    forEachListItem(state, type, ({ node, line: startLine, markLength, endOfRange }) => {\n      const column = node.from - startLine.from;\n      // Delete the marker on the first line.\n      changes.push({ from: node.from, to: node.from + markLength });\n      // and indentation on subsequent lines.\n      let line = startLine;\n      while (line.to < node.to) {\n        line = state.doc.lineAt(line.to + 1);\n        const open = /^[\\s>]*/.exec(line.text)![0].length;\n        if (open > column) {\n          changes.push({ from: line.from + column, to: line.from + Math.min(column + markLength, open) });\n        }\n      }\n      if (endOfRange) {\n        renumberListItems(node.nextSibling, 1, renumbers, state.doc);\n      }\n    });\n    if (!changes.length) {\n      return false;\n    }\n\n    dispatch(\n      state.update({\n        changes: [...changes, ...nonOverlappingChanges(renumbers, changes)],\n        userEvent: 'format.list.remove',\n        scrollIntoView: true,\n      }),\n    );\n    return true;\n  };\n};\n\nexport const toggleList = (type: List): StateCommand => {\n  return (target) => {\n    const formatting = getFormatting(target.state);\n    const active =\n      formatting.listStyle === (type === List.Bullet ? 'bullet' : type === List.Ordered ? 'ordered' : 'task');\n    if (active) {\n      return removeList(type)(target);\n    }\n    if (formatting.listStyle) {\n      const current =\n        formatting.listStyle === 'bullet' ? List.Bullet : formatting.listStyle === 'ordered' ? List.Ordered : List.Task;\n      return convertList(current, type)(target);\n    }\n    return addList(type)(target);\n  };\n};\n\n/**\n * Convert list items from one style to another by rewriting markers in place;\n * removing and re-adding would merge adjacent items into a single paragraph,\n * while adding on top of an existing list would nest markers (e.g. `- - [ ] item`).\n */\nexport const convertList = (from: List, to: List): StateCommand => {\n  return ({ state, dispatch }) => {\n    const changes: SimpleChange[] = [];\n    const renumbers: SimpleChange[] = [];\n    forEachListItem(state, from, ({ node, line: startLine, markLength, ordinal, endOfRange }) => {\n      const newMark = to === List.Ordered ? `${ordinal}. ` : to === List.Task ? '- [ ] ' : '- ';\n      changes.push({ from: node.from, to: node.from + markLength, insert: newMark });\n      // Adjust continuation-line indentation to match the new marker width.\n      const delta = newMark.length - markLength;\n      const column = node.from - startLine.from;\n      let line = startLine;\n      while (line.to < node.to) {\n        line = state.doc.lineAt(line.to + 1);\n        const open = /^[\\s>]*/.exec(line.text)![0].length;\n        if (open <= column) {\n          continue;\n        }\n        if (delta > 0) {\n          changes.push({ from: line.from + column, insert: ' '.repeat(delta) });\n        } else if (delta < 0) {\n          changes.push({ from: line.from + column, to: line.from + Math.min(column - delta, open) });\n        }\n      }\n      if (from === List.Ordered && endOfRange) {\n        renumberListItems(node.nextSibling, 1, renumbers, state.doc);\n      }\n    });\n\n    if (!changes.length) {\n      return false;\n    }\n\n    const changeSet = state.changes([...changes, ...nonOverlappingChanges(renumbers, changes)]);\n    dispatch(\n      state.update({\n        changes: changeSet,\n        selection: state.selection.map(changeSet, 1),\n        userEvent: 'format.list.convert',\n        scrollIntoView: true,\n      }),\n    );\n    return true;\n  };\n};\n\nconst renumberListItems = (item: SyntaxNode | null, counter: number, changes: SimpleChange[], doc: Text) => {\n  for (; item; item = item.nextSibling) {\n    if (item.name === 'ListItem') {\n      const number = /(\\s*)(\\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));\n      if (!number || +number[2] === counter) {\n        break;\n      }\n      const size = number[1].length + number[2].length;\n      const newNum = String(counter);\n      changes.push({ from: item.from + Math.max(0, size - newNum.length), to: item.from + size, insert: newNum });\n      counter++;\n    }\n  }\n};\n\n//\n// Block quotes\n//\n\nexport const setBlockquote = (enable: boolean): StateCommand => {\n  return ({ state, dispatch }) => {\n    const lines: Line[] = [];\n    let lastBlock = -1;\n    for (const { from, to } of state.selection.ranges) {\n      let sawBlock = false;\n      syntaxTree(state).iterate({\n        from,\n        to,\n        enter: (node) => {\n          if (Object.hasOwn(Textblocks, node.name) || node.name === 'Table') {\n            if (node.from === lastBlock) {\n              return false;\n            }\n            sawBlock = true;\n            lastBlock = node.from;\n            let line = state.doc.lineAt(node.from);\n            if (line.number > 1) {\n              const prevLine = state.doc.line(line.number - 1);\n              if (/^[>\\s]*$/.test(prevLine.text)) {\n                if (!enable || (lines.length && lines[lines.length - 1].number === prevLine.number - 1)) {\n                  lines.push(prevLine);\n                }\n              }\n            }\n            for (;;) {\n              lines.push(line);\n              if (line.to >= node.to) {\n                break;\n              }\n              line = state.doc.line(line.number + 1);\n            }\n            if (!enable && line.number < state.doc.lines) {\n              const nextLine = state.doc.line(line.number + 1);\n              if (/^[>\\s]*$/.test(nextLine.text)) {\n                lines.push(nextLine);\n              }\n            }\n            return false;\n          }\n        },\n      });\n      let line;\n      if (!sawBlock && enable && from === to && !/\\S/.test((line = state.doc.lineAt(from)).text)) {\n        lines.push(line);\n      }\n    }\n\n    const changes: ChangeSpec[] = [];\n    for (const line of lines) {\n      if (enable) {\n        changes.push({ from: line.from, insert: /\\S/.test(line.text) ? '> ' : '>' });\n      } else {\n        const quote = /((?:[\\s>\\-+*]|\\d+[.)])*?)> ?/.exec(line.text);\n        if (quote) {\n          changes.push({ from: line.from + quote[1].length, to: line.from + quote[0].length });\n        }\n      }\n    }\n    if (!changes.length) {\n      return false;\n    }\n\n    const changeSet = state.changes(changes);\n    dispatch(\n      state.update({\n        changes: changeSet,\n        selection: state.selection.map(changeSet, 1),\n        userEvent: enable ? 'format.blockquote.add' : 'format.blockquote.remove',\n        scrollIntoView: true,\n      }),\n    );\n    return true;\n  };\n};\n\nexport const addBlockquote = setBlockquote(true);\n\nexport const removeBlockquote = setBlockquote(false);\n\nexport const toggleBlockquote: StateCommand = (target) => {\n  return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);\n};\n\n//\n// Code block\n//\n\nexport const addCodeblock: StateCommand = (target) => {\n  const { state, dispatch } = target;\n  const { selection } = state;\n\n  // If on a blank line, use the code block snippet.\n  if (selection.ranges.length === 1 && selection.main.empty) {\n    const { head } = selection.main;\n    const line = state.doc.lineAt(head);\n    if (!/\\S/.test(line.text) && head === line.from) {\n      snippets.codeblock(target, null, line.from, line.to);\n      return true;\n    }\n  }\n\n  // Otherwise, wrap any selected blocks in triple backticks.\n  const ranges: { from: number; to: number }[] = [];\n  for (const { from, to } of selection.ranges) {\n    let blockFrom = from;\n    let blockTo = to;\n    syntaxTree(state).iterate({\n      from,\n      to,\n      enter: (node) => {\n        if (Object.hasOwn(Textblocks, node.name)) {\n          if (from >= node.from && to <= node.to) {\n            // Selection in a single block.\n            blockFrom = node.from;\n            blockTo = node.to;\n          } else {\n            // Expand to cover whole lines.\n            blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);\n            blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);\n          }\n        }\n      },\n    });\n    if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {\n      ranges[ranges.length - 1].to = blockTo;\n    } else {\n      ranges.push({ from: blockFrom, to: blockTo });\n    }\n  }\n  if (!ranges.length) {\n    return false;\n  }\n\n  const changes: ChangeSpec[] = ranges.map(({ from, to }) => {\n    const column = from - state.doc.lineAt(from).from;\n    return [\n      { from, insert: '```\\n' + ' '.repeat(column) },\n      { from: to, insert: '\\n' + ' '.repeat(column) + '```' },\n    ];\n  });\n\n  dispatch(\n    state.update({\n      changes,\n      userEvent: 'format.codeblock.add',\n      scrollIntoView: true,\n    }),\n  );\n\n  return true;\n};\n\nexport const removeCodeblock: StateCommand = ({ state, dispatch }) => {\n  let lastBlock = -1;\n\n  // Find all code blocks, remove their markup.\n  const changes: ChangeSpec[] = [];\n  for (const { from, to } of state.selection.ranges) {\n    syntaxTree(state).iterate({\n      from,\n      to,\n      enter: (node) => {\n        if (Textblocks[node.name] === 'codeblock' && lastBlock !== node.from) {\n          lastBlock = node.from;\n          const firstLine = state.doc.lineAt(node.from);\n          if (node.name === 'FencedCode') {\n            changes.push({ from: node.from, to: firstLine.to + 1 + node.from - firstLine.from });\n            const lastLine = state.doc.lineAt(node.to);\n            if (/^([\\s>]|[-*+] |\\d+[).])*`+$/.test(lastLine.text)) {\n              changes.push({\n                from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),\n                to: lastLine.to,\n              });\n            }\n          } else {\n            // Indented code block.\n            const column = node.from - firstLine.from;\n            for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {\n              changes.push({ from: line.from + column - 4, to: line.from + column });\n              if (line.to >= node.to) {\n                break;\n              }\n            }\n          }\n        }\n      },\n    });\n  }\n  if (!changes.length) {\n    return false;\n  }\n\n  dispatch(state.update({ changes, userEvent: 'format.codeblock.remove', scrollIntoView: true }));\n  return true;\n};\n\nexport const toggleCodeblock: StateCommand = (target) => {\n  return (getFormatting(target.state).blockType === 'codeblock' ? removeCodeblock : addCodeblock)(target);\n};\n\nconst InlineMarker: { [name: string]: number } = {\n  Emphasis: Inline.Emphasis,\n  StrongEmphasis: Inline.Strong,\n  InlineCode: Inline.Code,\n  Strikethrough: Inline.Strikethrough,\n};\n\nconst IgnoreInline = new Set([\n  'Autolink',\n  'CodeMark',\n  'CodeText',\n  'Comment',\n  'EmphasisMark',\n  'Hardbreak',\n  'HeaderMark',\n  'HTMLTag',\n  'LinkMark',\n  'ListMark',\n  'ProcessingInstruction',\n  'QuoteMark',\n  'StrikethroughMark',\n  'SubscriptMark',\n  'SuperscriptMark',\n  'TaskMarker',\n]);\n\nconst Textblocks: { [name: string]: NonNullable<Formatting['blockType']> } = {\n  ATXHeading1: 'heading1',\n  ATXHeading2: 'heading2',\n  ATXHeading3: 'heading3',\n  ATXHeading4: 'heading4',\n  ATXHeading5: 'heading5',\n  ATXHeading6: 'heading6',\n  CodeBlock: 'codeblock',\n  FencedCode: 'codeblock',\n  Paragraph: 'paragraph',\n  SetextHeading1: 'heading1',\n  SetextHeading2: 'heading2',\n  TableCell: 'tablecell',\n  Task: 'paragraph',\n};\n\n/**\n * Query the editor state for the active formatting at the selection.\n */\nexport const getFormatting = (state: EditorState): Formatting => {\n  // These will track the formatting we've seen so far.\n  // False indicates mixed block types.\n  let blockType: Formatting['blockType'] | false = null;\n  // Indexed by the Inline enum, tracks inline markup.\n  // null = no text seen, true = all text had the mark, false = saw text without it.\n  const inline: (boolean | null)[] = [null, null, null, null];\n  let link: boolean = false;\n  let blockQuote: boolean | null = null;\n  // False indicates mixed list styles.\n  let listStyle: Formatting['listStyle'] | null | false = null;\n\n  // Track block context for list/blockquote handling.\n  const stack: ('BulletList' | 'OrderedList' | 'Blockquote' | 'TaskList')[] = [];\n  // This is set when entering a textblock (paragraph, heading, etc.)\n  // and cleared when exiting again. It is used to track inline style.\n  // `active` holds an array that indicates, for the various style (`Inline` enum) whether they are currently active.\n  let currentBlock: { pos: number; end: number; active: boolean[] } | null = null;\n  // Advance over regular inline text. Will update `inline` depending on what styles are active.\n  const advanceInline = (upto: number) => {\n    if (!currentBlock) {\n      return;\n    }\n    upto = Math.min(upto, currentBlock.end);\n    if (upto <= currentBlock.pos) {\n      return;\n    }\n    for (let i = 0; i < currentBlock.active.length; i++) {\n      if (inline[i] === false) {\n        continue;\n      } else if (currentBlock.active[i]) {\n        inline[i] = true;\n      } else if (/\\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {\n        inline[i] = false;\n      }\n    }\n    currentBlock.pos = upto;\n  };\n\n  // Skip markup that shouldn't be treated as inline text for style-tracking purposes.\n  const skipInline = (upto: number) => {\n    if (currentBlock && upto > currentBlock.pos) {\n      currentBlock.pos = Math.min(upto, currentBlock.end);\n    }\n  };\n\n  const { selection } = state;\n  for (const range of selection.ranges) {\n    if (range.empty && inline.some((v) => v === null)) {\n      // Check for markers directly around the cursor (which, not being valid Markdown, the syntax tree won't pick up).\n      const contextSize = Math.min(range.head, 6);\n      const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);\n      let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);\n      for (let i = 0; i < contextSize; i++) {\n        const ch = contextAfter[i];\n        if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {\n          contextAfter = contextAfter.slice(0, i);\n          break;\n        }\n      }\n      for (let i = 0; i < inline.length; i++) {\n        const mark = inlineMarkerText(i);\n        const found = contextAfter.indexOf(mark);\n        if (found > -1) {\n          contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark.length);\n          if (inline[i] === null) {\n            inline[i] = true;\n          }\n        }\n      }\n    }\n\n    syntaxTree(state).iterate({\n      from: range.from,\n      to: range.to,\n      enter: (node) => {\n        advanceInline(node.from);\n        const { name } = node;\n        if (name === 'BulletList' || name === 'OrderedList' || name === 'Blockquote') {\n          // Maintain block context.\n          stack.push(name);\n        } else if (name === 'Link') {\n          link = true;\n        } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {\n          if (name === 'Task' && stack[stack.length - 1] === 'BulletList') {\n            stack[stack.length - 1] = 'TaskList';\n          }\n          const blockCode = Textblocks[name];\n          if (blockType === null) {\n            blockType = blockCode;\n          } else if (blockType !== blockCode) {\n            blockType = false;\n          }\n          if (blockCode !== 'codeblock' && inline.some((i) => i !== false)) {\n            // Set up inline content tracking for non-code textblocks.\n            currentBlock = {\n              pos: Math.max(range.from, node.from),\n              end: Math.min(range.to, node.to),\n              active: [false, false, false, false],\n            };\n          }\n        } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {\n          const index = InlineMarker[name];\n          // Cursors selections always count as active.\n          if (range.empty && inline[index] === null) {\n            inline[index] = true;\n          }\n          currentBlock.active[index] = true;\n        } else if (IgnoreInline.has(name)) {\n          skipInline(node.to);\n        }\n      },\n      leave: (node) => {\n        advanceInline(node.to);\n        const { name } = node;\n        if (name === 'BulletList' || name === 'OrderedList' || name === 'Blockquote') {\n          // Track block context.\n          stack.pop();\n        } else if (Object.hasOwn(Textblocks, name)) {\n          // Scan the stack for blockquote/list context.\n          // Done at end of node because task lists aren't recognized until a task is seen\n          let hasList: Formatting['listStyle'] | false = false;\n          let hasQuote = false;\n          for (let i = stack.length - 1; i >= 0; i--) {\n            if (stack[i] === 'Blockquote') {\n              hasQuote = true;\n            } else if (!hasList) {\n              hasList = stack[i] === 'TaskList' ? 'task' : stack[i] === 'BulletList' ? 'bullet' : 'ordered';\n            }\n          }\n          if (blockQuote === null) {\n            blockQuote = hasQuote;\n          } else if (!hasQuote && blockQuote) {\n            blockQuote = false;\n          }\n          if (listStyle === null) {\n            listStyle = hasList;\n          } else if (listStyle !== hasList) {\n            listStyle = false;\n          }\n\n          // End textblock.\n          currentBlock = null;\n        } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {\n          // Track markup in textblock.\n          currentBlock.active[InlineMarker[name]] = false;\n        }\n      },\n    });\n  }\n\n  const { from, to } = state.doc.lineAt(selection.main.anchor);\n  const blankLine = from === to;\n\n  return {\n    blankLine,\n    blockType: blockType || null,\n    blockQuote: blockQuote ?? false,\n    code: inline[Inline.Code] ?? false,\n    emphasis: inline[Inline.Emphasis] ?? false,\n    strong: inline[Inline.Strong] ?? false,\n    strikethrough: inline[Inline.Strikethrough] ?? false,\n    link,\n    listStyle: listStyle || null,\n  };\n};\n\n/**\n * Extension to compute and publish the current formatting state.\n * @param onStateChange - Callback invoked with the current formatting state when the editor state changes.\n * @param delay - Debounce/throttle delay in ms.\n */\nexport const formattingListener = (onStateChange: (state: Formatting) => void, delay = 100): Extension => {\n  return EditorView.updateListener.of(\n    debounceAndThrottle((update: ViewUpdate) => {\n      if (update.docChanged || update.selectionSet) {\n        onStateChange(getFormatting(update.state));\n      }\n    }, delay),\n  );\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type EditorView } from '@codemirror/view';\n\nimport type * as AppGraphNode from '@dxos/app-graph/AppGraphNode';\nimport { type MenuActionProperties } from '@dxos/ui-types';\n\nimport { createComment } from '../../review/comments.ts';\nimport {\n  Inline,\n  List,\n  addBlockquote,\n  addCodeblock,\n  addLink,\n  addList,\n  insertTable,\n  removeBlockquote,\n  removeCodeblock,\n  removeLink,\n  removeList,\n  setHeading,\n  setStyle,\n  toggleBlockquote,\n  toggleList,\n  toggleStyle,\n} from './formatting.ts';\n\nexport type PayloadType =\n  | 'view-mode'\n  | 'blockquote'\n  | 'strong'\n  | 'codeblock'\n  | 'comment'\n  | 'heading'\n  | 'image'\n  | 'emphasis'\n  | 'code'\n  | 'link'\n  | 'list-bullet'\n  | 'list-ordered'\n  | 'list-task'\n  | 'mention'\n  | 'prompt'\n  | 'search'\n  | 'strikethrough'\n  | 'table';\n\nexport type EditorActionPayload = {\n  type: PayloadType;\n  data?: any;\n};\n\nexport type EditorAction = AppGraphNode.Action<MenuActionProperties & EditorActionPayload>;\n\nexport type EditorPayloadHandler = (view: EditorView, payload: EditorActionPayload) => void;\n\nexport const processEditorPayload: EditorPayloadHandler = (view, { type, data }) => {\n  let inlineType, listType;\n  switch (type) {\n    case 'heading':\n      setHeading(parseInt(data))(view);\n      break;\n\n    case 'strong':\n    case 'emphasis':\n    case 'strikethrough':\n    case 'code':\n      inlineType =\n        type === 'strong'\n          ? Inline.Strong\n          : type === 'emphasis'\n            ? Inline.Emphasis\n            : type === 'strikethrough'\n              ? Inline.Strikethrough\n              : Inline.Code;\n      (typeof data === 'boolean' ? setStyle(inlineType, data) : toggleStyle(inlineType))(view);\n      break;\n\n    case 'list-ordered':\n    case 'list-bullet':\n    case 'list-task':\n      listType = type === 'list-ordered' ? List.Ordered : type === 'list-bullet' ? List.Bullet : List.Task;\n      (data === false ? removeList(listType) : data === true ? addList(listType) : toggleList(listType))(view);\n      break;\n\n    case 'blockquote':\n      (data === false ? removeBlockquote : data === true ? addBlockquote : toggleBlockquote)(view);\n      break;\n    case 'codeblock':\n      (data === false ? removeCodeblock : addCodeblock)(view);\n      break;\n    case 'table':\n      insertTable(view);\n      break;\n\n    case 'link':\n      (data === false ? removeLink : addLink())(view);\n      break;\n\n    case 'image':\n      addLink({ url: data, image: true })(view);\n      break;\n\n    case 'comment':\n      createComment(view);\n      break;\n  }\n\n  requestAnimationFrame(() => {\n    if (!view.hasFocus) {\n      view.focus();\n    }\n  });\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { HighlightStyle, syntaxHighlighting } from '@codemirror/language';\nimport { type Extension } from '@codemirror/state';\nimport { flowchartTags, mermaid as mermaidLanguage, mermaidTags } from 'codemirror-lang-mermaid';\n\nexport type MermaidHighlightOptions = {};\n\n/**\n * `codemirror-lang-mermaid` emits its own tags rather than the standard `@lezer/highlight` ones, so\n * no default highlight style matches them and the source renders unstyled without this.\n *\n * Only the distinctions worth drawing are styled — header, connectors, labels, comments — and the\n * rest inherits, as in `markdownHighlightStyle`. Classes are semantic theme tokens so the palette\n * follows light/dark rather than pinning syntax hues.\n */\nexport const mermaidHighlightStyle = (_options: MermaidHighlightOptions = {}) =>\n  HighlightStyle.define([\n    // `flowchart`/`graph` and its direction, plus the `subgraph`/`end` keywords.\n    {\n      tag: [mermaidTags.diagramName, flowchartTags.diagramName, flowchartTags.keyword, flowchartTags.orientation],\n      class: 'text-primary-500',\n    },\n    // Connectors recede; the nodes they join are the content. `link` is the arrow itself (`-->`),\n    // so it must not be underlined — that reads as a hyperlink.\n    { tag: [flowchartTags.link, flowchartTags.nodeEdge], class: 'text-fg-subtle' },\n    // Labels, on nodes and on edges. Node ids are left inheriting, as the backbone of the source.\n    {\n      tag: [flowchartTags.nodeText, flowchartTags.nodeEdgeText, flowchartTags.string, flowchartTags.number],\n      class: 'text-accent',\n    },\n    { tag: [flowchartTags.lineComment], class: 'text-fg-muted' },\n  ]);\n\n/**\n * Mermaid language support for a document that is entirely mermaid. For a fenced block inside\n * markdown use `createMarkdownExtensions`, which registers mermaid as a fenced-code language.\n */\nexport const createMermaidExtensions = (options: MermaidHighlightOptions = {}): Extension[] => [\n  mermaidLanguage(),\n  syntaxHighlighting(mermaidHighlightStyle(options)),\n];\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { LanguageDescription, syntaxHighlighting } from '@codemirror/language';\nimport { Compartment, type Extension } from '@codemirror/state';\nimport { type EditorView, ViewPlugin } from '@codemirror/view';\n\n/**\n * Mermaid fenced-code support. `codemirror-lang-mermaid` ships its parser tables in the same module\n * as its tags (~165 KB), so both the language and its highlight style load on the first document\n * that actually contains a mermaid fence rather than with the editor.\n */\nconst highlightCompartment = new Compartment();\n\n/** Views whose highlighting slot is still empty, reconfigured once the style resolves. */\nconst pendingViews = new Set<EditorView>();\n\n/** Set once the style has resolved, so editors created later start with it rather than empty. */\nlet highlightExtension: Extension | undefined;\n\n/** Shared while the import is in flight, so concurrent fences load the style once. */\nlet loading: Promise<void> | undefined;\n\nconst installHighlighting = (): Promise<void> => {\n  if (highlightExtension) {\n    return Promise.resolve();\n  }\n  loading ??= import('./highlight.ts')\n    .then(({ mermaidHighlightStyle }) => {\n      highlightExtension = syntaxHighlighting(mermaidHighlightStyle());\n      for (const view of pendingViews) {\n        view.dispatch({ effects: highlightCompartment.reconfigure(highlightExtension) });\n      }\n      pendingViews.clear();\n    })\n    .catch((err) => {\n      // Cleared so a later fence retries rather than inheriting a failed load.\n      loading = undefined;\n      throw err;\n    });\n\n  return loading;\n};\n\n/** CodeMirror calls `load` when it first parses a mermaid fence. */\nexport const mermaidLanguageDescription = LanguageDescription.of({\n  name: 'mermaid',\n  alias: ['mermaid'],\n  extensions: ['mmd'],\n  load: async () => {\n    const [{ mermaid }] = await Promise.all([import('codemirror-lang-mermaid'), installHighlighting()]);\n    return mermaid();\n  },\n});\n\n/** Slot that receives the mermaid highlight style, whether it loads before or after this view. */\nexport const mermaidHighlighting = (): Extension => [\n  highlightCompartment.of(highlightExtension ?? []),\n  ViewPlugin.define((view) => {\n    if (!highlightExtension) {\n      pendingViews.add(view);\n    }\n    return {\n      destroy: () => pendingViews.delete(view),\n    };\n  }),\n];\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { markdownLanguage } from '@codemirror/lang-markdown';\nimport { HighlightStyle } from '@codemirror/language';\nimport { Tag, styleTags, tags } from '@lezer/highlight';\nimport { type MarkdownConfig, Table } from '@lezer/markdown';\n\nimport { fontBody, markdownTheme } from '../../../styles/index.ts';\n\nexport type HighlightOptions = {};\n\n/**\n * Styling based on `lezer` parser tags.\n * https://codemirror.net/examples/styling\n * https://github.com/lezer-parser/highlight\n * https://github.com/lezer-parser/highlight/blob/main/src/highlight.ts#L427\n * https://lezer.codemirror.net/docs/ref/#highlight.tags (list of tags)\n *\n * Examples:\n * - https://github.com/codemirror/language/blob/main/src/highlight.ts#L194\n * - https://github.com/codemirror/theme-one-dark/blob/main/src/one-dark.ts#L115\n */\nexport const markdownHighlightStyle = (_options: HighlightOptions = {}) => {\n  return HighlightStyle.define(\n    [\n      {\n        tag: [\n          tags.keyword,\n          tags.name,\n          tags.deleted,\n          tags.character,\n          tags.propertyName,\n          tags.macroName,\n          tags.color,\n          tags.constant(tags.name),\n          tags.standard(tags.name),\n          tags.definition(tags.name),\n          tags.separator,\n          tags.typeName,\n          tags.className,\n          tags.number,\n          tags.changed,\n          tags.annotation,\n          tags.modifier,\n          tags.self,\n          tags.namespace,\n          tags.operator,\n          tags.operatorKeyword,\n          tags.escape,\n          tags.regexp,\n          tags.special(tags.string),\n          tags.meta,\n          tags.comment,\n          tags.atom,\n          tags.bool,\n          tags.special(tags.variableName),\n          tags.processingInstruction,\n          tags.string,\n          tags.inserted,\n          tags.invalid,\n        ],\n        // TODO(burdon): Explain.\n        color: 'inherit !important',\n      },\n\n      // Markdown marks.\n      {\n        tag: [\n          tags.meta,\n          tags.processingInstruction,\n          markdownTags.LinkLabel,\n          markdownTags.LinkReference,\n          markdownTags.ListMark,\n        ],\n        class: markdownTheme.mark,\n      },\n\n      // Markdown marks.\n      {\n        tag: [\n          //\n          markdownTags.CodeMark,\n          markdownTags.HeaderMark,\n          markdownTags.QuoteMark,\n          markdownTags.EmphasisMark,\n        ],\n        class: markdownTheme.mark,\n      },\n\n      // E.g., code block language (after ```).\n      {\n        tag: [\n          //\n          tags.function(tags.variableName),\n          tags.labelName,\n        ],\n        class: markdownTheme.codeMark,\n      },\n\n      // Fonts.\n      {\n        tag: [tags.monospace, tags.comment],\n        class: 'font-mono',\n      },\n\n      // Headings — use CSS properties only (no class:) so CodeMirror generates scoped CSS via\n      // StyleModule that overrides vscodeDarkStyle's t.heading rule. When class: is present,\n      // HighlightStyle silently ignores all other CSS properties (they're mutually exclusive).\n      // Font sizes use Tailwind v4 CSS variables so nothing is hardcoded.\n      {\n        tag: tags.heading,\n        color: 'var(--color-cm-heading) !important',\n        fontWeight: '300',\n      },\n      { tag: tags.heading1, ...markdownTheme.heading(1) },\n      { tag: tags.heading2, ...markdownTheme.heading(2) },\n      { tag: tags.heading3, ...markdownTheme.heading(3) },\n      { tag: tags.heading4, ...markdownTheme.heading(4) },\n      { tag: tags.heading5, ...markdownTheme.heading(5) },\n      { tag: tags.heading6, ...markdownTheme.heading(6) },\n\n      // Emphasis.\n      { tag: tags.emphasis, class: 'italic' },\n      { tag: tags.strong, class: 'font-bold' },\n      { tag: tags.strikethrough, class: 'line-through' },\n\n      // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).\n      // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,\n      // when a language is specified. In this case, the syntax highlighting extensions will colorize\n      // the code, but all other CSS properties will be inherited.\n      // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.\n      {\n        tag: [markdownTags.CodeText, markdownTags.InlineCode],\n        class: markdownTheme.code,\n      },\n\n      {\n        tag: [markdownTags.TableCell],\n        class: 'font-mono',\n      },\n    ],\n    {\n      scope: markdownLanguage,\n      all: {\n        fontFamily: fontBody,\n      },\n    },\n  );\n};\n\n/**\n * Custom tags defined and processed by the GFM lezer extension.\n * https://github.com/lezer-parser/markdown\n * https://github.com/lezer-parser/markdown/blob/main/src/markdown.ts\n */\nexport const markdownTags = {\n  Blockquote: Tag.define(),\n  CodeMark: Tag.define(),\n  CodeText: Tag.define(),\n  EmphasisMark: Tag.define(),\n  HeaderMark: Tag.define(),\n  InlineCode: Tag.define(),\n  LinkLabel: Tag.define(),\n  LinkReference: Tag.define(),\n  ListMark: Tag.define(),\n  QuoteMark: Tag.define(),\n  URL: Tag.define(),\n\n  // Custom.\n  TableCell: Tag.define(),\n};\n\n// TODO(burdon): Customize table parser (make all content monospace).\n//  https://github.com/lezer-parser/markdown/blob/main/src/extension.ts\nTable.defineNodes?.forEach((node) => {\n  if (typeof node === 'object' && node !== null && node.name === 'TableCell') {\n    node.style = markdownTags.TableCell;\n  }\n});\n\n/**\n * Lezer MarkdownConfig entries (GFM Table plus styleTags) contributed to the parser.\n * Note: this is parser config, not an editor Extension.\n */\nexport const markdownTagsExtensions: MarkdownConfig[] = [\n  Table,\n  {\n    props: [styleTags(markdownTags)],\n  },\n];\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { completionKeymap } from '@codemirror/autocomplete';\nimport { defaultKeymap, indentWithTab } from '@codemirror/commands';\nimport { jsonLanguage } from '@codemirror/lang-json';\nimport { insertNewlineContinueMarkupCommand, markdown, markdownLanguage } from '@codemirror/lang-markdown';\nimport { type LanguageDescription, foldNodeProp, syntaxHighlighting } from '@codemirror/language';\nimport { languages } from '@codemirror/language-data';\nimport { type Extension, Prec } from '@codemirror/state';\nimport { keymap } from '@codemirror/view';\nimport { type MarkdownConfig } from '@lezer/markdown';\n\nimport { isTruthy } from '@dxos/util';\n\nimport { mermaidHighlighting, mermaidLanguageDescription } from '../mermaid/index.ts';\nimport { markdownHighlightStyle, markdownTagsExtensions } from './highlight.ts';\n\nexport type MarkdownBundleOptions = {\n  /** Additional fenced-code languages prepended to the standard language-data list. */\n  codeLanguages?: LanguageDescription[];\n  extensions?: MarkdownConfig[];\n  indentWithTab?: boolean;\n  setextHeading?: boolean;\n};\n\n/**\n * Creates markdown extensions.\n * To be used in conjunction with createBasicExtensions.\n *\n * Refs:\n * https://codemirror.net/docs/community\n * https://codemirror.net/docs/ref/#codemirror.basicSetup\n */\nexport const createMarkdownExtensions = (options: MarkdownBundleOptions = {}): Extension[] => {\n  return [\n    Prec.high(keymap.of([{ key: 'Enter', run: insertNewlineContinueMarkupCommand({ nonTightLists: false }) }])),\n\n    // Main extension.\n    // https://github.com/codemirror/lang-markdown\n    // https://codemirror.net/5/mode/markdown/index.html (demo).\n    markdown({\n      // GRM by default (vs strict CommonMark):\n      // Table, TaskList, Strikethrough, and Autolink.\n      // NOTE: This extends the parser; it doesn't affect rendering.\n      // https://github.github.com/gfm\n      // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown\n      base: markdownLanguage,\n\n      // Languages for syntax highlighting fenced code blocks.\n      // Caller-supplied languages are checked first so they can override defaults.\n      // Mermaid is registered here rather than by plugin-mermaid: `codeLanguages` is read once when\n      // the parser is built, so an extension contributed later cannot add a fenced-code language.\n      defaultCodeLanguage: jsonLanguage,\n      codeLanguages: [...(options.codeLanguages ?? []), mermaidLanguageDescription, ...languages],\n\n      // Don't complete HTML tags.\n      completeHTMLTags: false,\n\n      // Parser extensions.\n      extensions: [\n        // GFM provided by default.\n        markdownTagsExtensions,\n        ...(options.extensions ?? defaultExtensions()),\n        // Disable folding for fenced code blocks by overriding foldNodeProp.\n        // Note: returning null from foldService does not prevent syntaxFolding fallback,\n        // so we must override the node prop directly on the FencedCode node type.\n        noFencedCodeFolding,\n      ],\n    }),\n\n    // Custom styles.\n    // Fenced mermaid uses custom tags, so it needs its own style alongside markdown's.\n    mermaidHighlighting(),\n    syntaxHighlighting(markdownHighlightStyle()),\n\n    keymap.of(\n      [\n        // https://codemirror.net/docs/ref/#commands.indentWithTab\n        options.indentWithTab !== false && indentWithTab,\n\n        // https://codemirror.net/docs/ref/#commands.defaultKeymap\n        ...defaultKeymap,\n\n        // TODO(burdon): Remove?\n        ...completionKeymap,\n      ].filter(isTruthy),\n    ),\n  ];\n};\n\n/**\n * Disables folding for fenced code blocks.\n *\n * foldService cannot block folding because returning null just defers to the next service,\n * and CodeMirror always falls back to syntaxFolding (which reads foldNodeProp).\n * The only reliable fix is to override foldNodeProp on FencedCode to return null.\n */\nconst noFencedCodeFolding: MarkdownConfig = {\n  props: [\n    foldNodeProp.add({\n      FencedCode: () => null,\n    }),\n  ],\n};\n\n/**\n * Default customizations.\n * https://github.com/lezer-parser/markdown/blob/main/src/markdown.ts\n */\nexport const defaultExtensions = (): MarkdownConfig[] => [noSetExtHeading, noHtml];\n\n/**\n * Remove SetextHeading (e.g., headings created from \"---\").\n */\nconst noSetExtHeading: MarkdownConfig = {\n  remove: ['SetextHeading'],\n};\n\n/**\n * Remove HTML and XML parsing.\n */\nconst noHtml: MarkdownConfig = {\n  // remove: ['HTMLBlock', 'HTMLTag'],\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type EditorState, type Extension, StateField } from '@codemirror/state';\nimport { type TreeCursor } from '@lezer/common';\n\n/**\n * StateField that serialises the syntax tree to JSON and invokes the callback on every state change (debug only).\n */\nexport const debugTree = (cb: (tree: DebugNode) => void): Extension =>\n  StateField.define({\n    create: (state) => cb(convertTreeToJson(state)),\n    update: (value, tr) => cb(convertTreeToJson(tr.state)),\n  });\n\nexport type DebugNode = {\n  type: string;\n  from: number;\n  to: number;\n  text: string;\n  children: DebugNode[];\n};\n\nexport const convertTreeToJson = (state: EditorState): DebugNode => {\n  const treeToJson = (cursor: TreeCursor): DebugNode => {\n    const node: DebugNode = {\n      type: cursor.type.name,\n      from: cursor.from,\n      to: cursor.to,\n      text: state.doc.slice(cursor.from, cursor.to).toString(),\n      children: [],\n    };\n\n    if (cursor.firstChild()) {\n      do {\n        node.children.push(treeToJson(cursor));\n      } while (cursor.nextSibling());\n      cursor.parent();\n    }\n\n    return node;\n  };\n\n  return treeToJson(syntaxTree(state).cursor());\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Extension, StateEffect, StateField } from '@codemirror/state';\nimport {\n  type Command,\n  Decoration,\n  EditorView,\n  ViewPlugin,\n  type ViewUpdate,\n  WidgetType,\n  keymap,\n} from '@codemirror/view';\n\nimport { Domino } from '@dxos/ui';\n\nimport { type MarkerHue, markerButtons, markerText, markerTheme } from '../../decoration/marker.ts';\nimport { busy, setBusy } from '../../state/busy.ts';\n\n//\n// State.\n//\n\n/**\n * Pending (not-yet-committed) text injected from an external source.\n * `final` is the accumulated finalized text; `interim` is the volatile in-flight tail; `placeholder`\n * is shown while both are empty (e.g. \"Recording…\"). `anchor` is the document position the text will\n * be inserted at; it is mapped through document changes so it survives concurrent edits.\n */\nexport type PendingTextState = {\n  anchor: number;\n  final: string;\n  interim: string;\n  placeholder?: string;\n};\n\n/** Start a pending-text session at the given position, optionally showing a placeholder until text arrives. */\nexport const setPendingAnchor = StateEffect.define<{ anchor: number; placeholder?: string }>();\n\n/** Append finalized text to the pending buffer; clears the interim tail and placeholder. */\nexport const appendPendingText = StateEffect.define<string>();\n\n/** Replace the volatile in-flight tail. */\nexport const setPendingInterim = StateEffect.define<string>();\n\n/** Replace the finalized buffer wholesale (e.g. with post-processed text). */\nexport const setPendingFinal = StateEffect.define<string>();\n\n/** Discard the pending buffer without modifying the document. */\nexport const cancelPendingText = StateEffect.define<void>();\n\nconst emptyAt = (anchor: number, placeholder?: string): PendingTextState => ({\n  anchor,\n  final: '',\n  interim: '',\n  placeholder,\n});\n\n/** StateField holding the pending text buffer (anchor, final, interim, placeholder); building block of the pendingText extension. */\nexport const pendingTextState = StateField.define<PendingTextState | null>({\n  create: () => null,\n  update: (value, tr) => {\n    // Map the anchor through document changes so injected text lands at the right place.\n    let next = value && tr.docChanged ? { ...value, anchor: tr.changes.mapPos(value.anchor) } : value;\n    for (const effect of tr.effects) {\n      if (effect.is(setPendingAnchor)) {\n        next = emptyAt(effect.value.anchor, effect.value.placeholder);\n      } else if (effect.is(appendPendingText)) {\n        const base = next ?? emptyAt(tr.state.selection.main.head);\n        next = { ...base, final: base.final + effect.value, interim: '', placeholder: undefined };\n      } else if (effect.is(setPendingInterim)) {\n        const base = next ?? emptyAt(tr.state.selection.main.head);\n        next = { ...base, interim: effect.value };\n      } else if (effect.is(setPendingFinal)) {\n        // Replace the EXISTING buffer only; never resurrect a cancelled/empty session. This lets a\n        // late post-process result (e.g. entity linking) be dropped if the user cancelled meanwhile.\n        if (next) {\n          next = { ...next, final: effect.value, placeholder: undefined };\n        }\n      } else if (effect.is(cancelPendingText)) {\n        next = null;\n      }\n    }\n\n    return next;\n  },\n});\n\nconst hasContent = (value: PendingTextState): boolean =>\n  value.final.length > 0 || value.interim.length > 0 || (value.placeholder?.length ?? 0) > 0;\n\n//\n// Commands.\n//\n\n/**\n * Insert the finalized pending text into the document and clear the buffer. The text is terminated\n * with a newline; when committed at the end of the document an extra newline is added so the document\n * always ends with a blank line.\n */\nexport const commitPending: Command = (view) => {\n  const value = view.state.field(pendingTextState, false);\n  if (!value || value.final.length === 0) {\n    return false;\n  }\n\n  const atEnd = value.anchor >= view.state.doc.length;\n  const insert = value.final + (atEnd ? '\\n\\n' : '\\n');\n  view.dispatch({\n    // Place the cursor on the line after the inserted text (before the trailing blank line).\n    changes: { from: value.anchor, insert },\n    selection: { anchor: value.anchor + value.final.length + 1 },\n    effects: cancelPendingText.of(),\n  });\n  return true;\n};\n\n/** Discard the pending text without modifying the document. */\nexport const cancelPending: Command = (view) => {\n  if (!view.state.field(pendingTextState, false)) {\n    return false;\n  }\n\n  view.dispatch({ effects: cancelPendingText.of() });\n  return true;\n};\n\n// Commit when there is finalized text, otherwise just consume the key so the editor's default\n// newline insertion cannot mutate the document mid-session (e.g. placeholder/interim-only state).\nconst commitOrConsumePending: Command = (view) => {\n  const value = view.state.field(pendingTextState, false);\n  if (!value) {\n    return false;\n  }\n\n  return value.final.length > 0 ? commitPending(view) : true;\n};\n\n//\n// Decorations.\n//\n\n// Finalized + interim text share one teal marker (a single transcription); the placeholder uses a\n// rose marker with a pulsing icon to signal active recording.\nconst TEXT_HUE: MarkerHue = 'teal';\nconst PLACEHOLDER_HUE: MarkerHue = 'rose';\n\nclass PendingTextWidget extends WidgetType {\n  constructor(private readonly _state: PendingTextState) {\n    super();\n  }\n\n  override eq(other: PendingTextWidget): boolean {\n    return (\n      other._state.final === this._state.final &&\n      other._state.interim === this._state.interim &&\n      other._state.placeholder === this._state.placeholder\n    );\n  }\n\n  override toDOM(view: EditorView): HTMLElement {\n    const root = Domino.of('span').classNames('cm-pending-text');\n    if (this._state.final.length > 0 || this._state.interim.length > 0) {\n      const marker = Domino.of('span').classNames('cm-marker-text').attributes({ 'data-hue': TEXT_HUE });\n      if (this._state.final.length > 0) {\n        marker.append(Domino.of('span').text(this._state.final));\n      }\n      if (this._state.interim.length > 0) {\n        marker.append(Domino.of('span').classNames('cm-pending-text-interim').text(this._state.interim));\n      }\n      root.append(marker);\n    } else if (this._state.placeholder) {\n      root.append(\n        markerText(this._state.placeholder, {\n          hue: PLACEHOLDER_HUE,\n          className: 'cm-pending-text-indicator',\n          icon: 'ph--circle-notch--regular',\n          iconClassNames: 'animate-spin',\n        }),\n      );\n    }\n\n    root.append(\n      markerButtons([\n        {\n          icon: 'ph--check--regular',\n          label: 'Confirm',\n          className: 'cm-marker-button-success',\n          testId: 'pending-text.confirm',\n          onClick: () => commitPending(view),\n        },\n        {\n          icon: 'ph--x--regular',\n          label: 'Cancel',\n          className: 'cm-marker-button-error',\n          testId: 'pending-text.cancel',\n          onClick: () => cancelPending(view),\n        },\n      ]),\n    );\n    return root.root;\n  }\n\n  override ignoreEvent(): boolean {\n    return true;\n  }\n}\n\nconst pendingDecorations = EditorView.decorations.compute([pendingTextState], (state) => {\n  const value = state.field(pendingTextState);\n  if (!value || !hasContent(value)) {\n    return Decoration.none;\n  }\n\n  return Decoration.set([Decoration.widget({ widget: new PendingTextWidget(value), side: 1 }).range(value.anchor)]);\n});\n\n/**\n * Marks the content element while the preview is on screen.\n *\n * `@codemirror/view`'s own placeholder keys off an empty document, and pending text is a decoration\n * rather than document content — so dictating into an empty editor draws the placeholder underneath\n * the words being spoken. That placeholder takes no condition, so it is suppressed from the DOM side\n * instead (see {@link styles}).\n *\n * Conditioned exactly as the decoration is: the placeholder goes when the preview arrives to cover\n * it, not when the mic is tapped — opening a session paints nothing, and blanking the hint before\n * there is anything to read in its place would only empty the field.\n */\nconst pendingAttributes = EditorView.contentAttributes.compute([pendingTextState], (state) => {\n  const value = state.field(pendingTextState);\n  const attributes: Record<string, string> = {};\n  if (value && hasContent(value)) {\n    attributes['data-pending-text'] = '';\n  }\n  return attributes;\n});\n\n// Flag the editor busy while a pending session is active so other extensions (e.g. the command-hint\n// placeholder) suppress themselves. Deferred to a microtask to avoid dispatching mid-update, and\n// carried by a ViewPlugin so the deferred work is bound to the extension's lifetime: a bare\n// updateListener's microtask outlives a reconfigure that drops the extension and would then read a\n// StateField the configuration no longer has.\nconst busyReporter = ViewPlugin.fromClass(\n  class {\n    /** Set once the plugin leaves the configuration, so queued microtasks abandon their dispatch. */\n    #destroyed = false;\n\n    update(update: ViewUpdate): void {\n      // The previous configuration need not have included the field (this may be the update that\n      // introduced the extension), so the prior value is read defensively.\n      const wasActive = update.startState.field(pendingTextState, false) != null;\n      const active = update.state.field(pendingTextState) != null;\n      if (active === wasActive) {\n        return;\n      }\n\n      queueMicrotask(() => {\n        if (this.#destroyed) {\n          return;\n        }\n        if ((update.view.state.field(pendingTextState) != null) === active) {\n          update.view.dispatch({ effects: setBusy.of(active) });\n        }\n      });\n    }\n\n    destroy(): void {\n      this.#destroyed = true;\n    }\n  },\n);\n\n//\n// Extension.\n//\n\n/**\n * Renders externally-supplied \"pending\" text as an inline preview at a tracked anchor, with inline\n * confirm/cancel affordances. An external controller drives the buffer via the `setPendingAnchor` /\n * `appendPendingText` / `setPendingInterim` / `cancelPendingText` effects; the user commits or\n * discards it via the affordances or the Enter/Escape keys. While a session is active the editor is\n * flagged {@link busy} so hint placeholders suppress themselves. The extension is agnostic to the\n * source of the text (e.g. speech transcription), so any CodeMirror component can opt in.\n */\nexport const pendingText = (): Extension => [\n  pendingTextState,\n  busy(),\n  pendingDecorations,\n  pendingAttributes,\n  busyReporter,\n  markerTheme(),\n  styles,\n  keymap.of([\n    { key: 'Enter', run: commitOrConsumePending },\n    { key: 'Escape', run: cancelPending },\n  ]),\n];\n\nconst styles = EditorView.theme({\n  // See {@link pendingAttributes}: the built-in placeholder would otherwise sit behind the preview.\n  '.cm-content[data-pending-text] .cm-placeholder': {\n    display: 'none',\n  },\n  // Keep the marker and affordances on a single line (it already sits on its own line at the anchor).\n  '.cm-pending-text': {\n    display: 'inline-flex',\n    alignItems: 'center',\n    gap: '0.5rem',\n  },\n  // The interim tail is distinguished from the finalized text only by reduced opacity.\n  '.cm-pending-text-interim': {\n    opacity: 0.6,\n  },\n  // Keep the \"Recording…\" indicator (text + spinner) on a single line.\n  '.cm-pending-text-indicator': {\n    whiteSpace: 'nowrap',\n  },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorView } from '@codemirror/view';\n\nimport {\n  appendPendingText,\n  cancelPendingText,\n  setPendingAnchor,\n  setPendingFinal,\n  setPendingInterim,\n} from './pending-text.ts';\n\n//\n// Sinks — decouple the streamer from its target (a live editor, or memory for tests/stories).\n//\n\n/**\n * Target for streamed pending text. The {@link PendingTextStreamer} writes to this interface; the\n * editor implementation dispatches CodeMirror effects, while the memory implementation accumulates\n * state so the mechanism can be exercised without a CodeMirror component.\n */\nexport type PendingTextSink = {\n  begin(options?: { anchor?: number; placeholder?: string }): void;\n  /** Set the volatile in-flight tail. */\n  interim(text: string): void;\n  /** Append finalized text to the buffer. */\n  appendFinal(text: string): void;\n  /** Replace the finalized buffer wholesale (e.g. post-processed text). */\n  replaceFinal(text: string): void;\n  cancel(): void;\n};\n\n/** Sink that drives the `pendingText` extension in a live editor via its state effects. */\nexport const editorPendingTextSink = (view: EditorView): PendingTextSink => ({\n  begin: ({ anchor = view.state.selection.main.head, placeholder } = {}) =>\n    view.dispatch({ effects: setPendingAnchor.of({ anchor, placeholder }) }),\n  interim: (text) => view.dispatch({ effects: setPendingInterim.of(text) }),\n  appendFinal: (text) => view.dispatch({ effects: appendPendingText.of(text) }),\n  replaceFinal: (text) => view.dispatch({ effects: setPendingFinal.of(text) }),\n  cancel: () => view.dispatch({ effects: cancelPendingText.of() }),\n});\n\nexport type PendingTextSnapshot = {\n  active: boolean;\n  final: string;\n  interim: string;\n  placeholder?: string;\n};\n\n/** In-memory sink for tests and CodeMirror-free stories. */\nexport const memoryPendingTextSink = (\n  onChange?: (snapshot: PendingTextSnapshot) => void,\n): PendingTextSink & { snapshot: () => PendingTextSnapshot } => {\n  let state: PendingTextSnapshot = { active: false, final: '', interim: '', placeholder: undefined };\n  const set = (next: PendingTextSnapshot) => {\n    state = next;\n    onChange?.(state);\n  };\n\n  return {\n    begin: ({ placeholder } = {}) => set({ active: true, final: '', interim: '', placeholder }),\n    interim: (text) => set({ ...state, interim: text }),\n    appendFinal: (text) => set({ ...state, final: state.final + text, interim: '', placeholder: undefined }),\n    replaceFinal: (text) => set({ ...state, final: text, placeholder: undefined }),\n    cancel: () => set({ active: false, final: '', interim: '', placeholder: undefined }),\n    snapshot: () => state,\n  };\n};\n\n//\n// Streamer — buffering, pacing, and post-processing between a text source and a sink.\n//\n\n/** Minimal timer interface so tests can drive the streamer deterministically. */\nexport type Scheduler = {\n  setTimeout: (callback: () => void, ms: number) => unknown;\n  clearTimeout: (handle: unknown) => void;\n};\n\nconst defaultScheduler: Scheduler = {\n  setTimeout: (callback, ms) => setTimeout(callback, ms),\n  clearTimeout: (handle) => clearTimeout(handle as ReturnType<typeof setTimeout>),\n};\n\nexport type PendingTextStreamerOptions = {\n  /** Delay (ms) before the first text is revealed — absorbs the source's initial latency. */\n  initialBufferMs?: number;\n  /** `batch` reveals each pushed chunk whole; `word` reveals one word per {@link wordIntervalMs}. */\n  mode?: 'batch' | 'word';\n  /** Pacing (ms) for `word` mode. */\n  wordIntervalMs?: number;\n  /** Idle delay (ms) before {@link postProcess} runs after the buffer settles. */\n  postProcessDebounceMs?: number;\n  /** Rewrites the finalized buffer once it settles (e.g. entity extraction / dx-anchor links). */\n  postProcess?: (final: string) => string | Promise<string>;\n  /** Injectable timers (defaults to the global timers). */\n  scheduler?: Scheduler;\n};\n\nconst DEFAULTS = {\n  initialBufferMs: 0,\n  mode: 'batch' as const,\n  wordIntervalMs: 120,\n  postProcessDebounceMs: 800,\n};\n\n/**\n * Streams text from an arbitrary source (e.g. a transcriber, or a scripted mock) into a\n * {@link PendingTextSink}, decoupling the source from the editor and centralizing the cross-cutting\n * concerns: an initial buffering delay, optional word-by-word reveal, and a post-processing pass.\n */\nexport class PendingTextStreamer {\n  readonly #sink: PendingTextSink;\n  readonly #options: Required<Omit<PendingTextStreamerOptions, 'postProcess' | 'scheduler'>> &\n    Pick<PendingTextStreamerOptions, 'postProcess'>;\n  readonly #scheduler: Scheduler;\n\n  #started = false;\n  #buffering = false;\n  #final = '';\n  #queue: string[] = [];\n  #bufferHandle: unknown = null;\n  #wordHandle: unknown = null;\n  #postHandle: unknown = null;\n  /** The in-flight post-process pass, tracked so `flush()` can await it (drain contract). */\n  #postPromise: Promise<void> | null = null;\n  #disposed = false;\n\n  constructor(sink: PendingTextSink, options: PendingTextStreamerOptions = {}) {\n    this.#sink = sink;\n    this.#options = { ...DEFAULTS, ...options };\n    this.#scheduler = options.scheduler ?? defaultScheduler;\n  }\n\n  /** Begin a session; nothing is revealed until the initial buffer elapses (or the first push, if 0). */\n  start(options: { placeholder?: string; anchor?: number } = {}): void {\n    this.#started = true;\n    this.#final = '';\n    this.#queue = [];\n    this.#sink.begin(options);\n    if (this.#options.initialBufferMs > 0) {\n      this.#buffering = true;\n      this.#bufferHandle = this.#scheduler.setTimeout(() => {\n        this.#buffering = false;\n        this.#bufferHandle = null;\n        this.#drain();\n      }, this.#options.initialBufferMs);\n    }\n  }\n\n  /** Feed a finalized text chunk from the source. */\n  push(text: string): void {\n    if (!this.#started) {\n      this.start();\n    }\n    const trimmed = text.trim();\n    if (trimmed.length === 0) {\n      return;\n    }\n    this.#queue.push(...(this.#options.mode === 'word' ? trimmed.split(/\\s+/) : [trimmed]));\n    this.#drain();\n  }\n\n  /** Feed a volatile interim chunk (when the source yields partials). */\n  pushInterim(text: string): void {\n    if (!this.#started) {\n      this.start();\n    }\n    if (!this.#buffering) {\n      this.#sink.interim(text);\n    }\n  }\n\n  /**\n   * Reveal everything immediately and run any pending post-process (e.g. on stop). Resolves once the\n   * post-process pass (entity linking, …) has settled, so callers can drain before disposing.\n   */\n  async flush(): Promise<void> {\n    this.#clear(this.#bufferHandle);\n    this.#bufferHandle = null;\n    this.#buffering = false;\n    this.#clear(this.#wordHandle);\n    this.#wordHandle = null;\n    while (this.#queue.length > 0) {\n      this.#appendFinal(this.#queue.shift()!);\n    }\n    this.#sink.interim('');\n    this.#clear(this.#postHandle);\n    this.#postHandle = null;\n    if (this.#options.postProcess && this.#final.length > 0) {\n      // Await any post-process already in flight (its scheduled timer may have fired), then run a\n      // final pass over the settled buffer — so draining truly waits for post-processing to finish.\n      if (this.#postPromise) {\n        await this.#postPromise;\n      }\n      this.#postPromise = this.#runPostProcess();\n      await this.#postPromise;\n    }\n  }\n\n  dispose(): void {\n    this.#disposed = true;\n    [this.#bufferHandle, this.#wordHandle, this.#postHandle].forEach((handle) => this.#clear(handle));\n    this.#bufferHandle = this.#wordHandle = this.#postHandle = null;\n  }\n\n  #drain(): void {\n    if (this.#buffering) {\n      return;\n    }\n    if (this.#options.mode === 'word') {\n      if (this.#wordHandle == null) {\n        this.#tickWord();\n      }\n    } else {\n      while (this.#queue.length > 0) {\n        this.#appendFinal(this.#queue.shift()!);\n      }\n    }\n  }\n\n  #tickWord(): void {\n    const word = this.#queue.shift();\n    if (word == null) {\n      this.#wordHandle = null;\n      return;\n    }\n    this.#appendFinal(word);\n    this.#wordHandle = this.#scheduler.setTimeout(() => this.#tickWord(), this.#options.wordIntervalMs);\n  }\n\n  #appendFinal(text: string): void {\n    const chunk = (this.#final.length > 0 ? ' ' : '') + text;\n    this.#final += chunk;\n    this.#sink.appendFinal(chunk);\n    this.#schedulePostProcess();\n  }\n\n  #schedulePostProcess(): void {\n    if (!this.#options.postProcess) {\n      return;\n    }\n    this.#clear(this.#postHandle);\n    this.#postHandle = this.#scheduler.setTimeout(() => {\n      this.#postHandle = null;\n      this.#postPromise = this.#runPostProcess();\n    }, this.#options.postProcessDebounceMs);\n  }\n\n  async #runPostProcess(): Promise<void> {\n    const { postProcess } = this.#options;\n    if (!postProcess) {\n      return;\n    }\n    const input = this.#final;\n    try {\n      const result = await postProcess(input);\n      // Skip if disposed while processing (avoid writing into a torn-down sink / destroyed view), or\n      // if the buffer grew — a later pass will cover the newer text.\n      if (!this.#disposed && result !== input && this.#final === input) {\n        this.#final = result;\n        this.#sink.replaceFinal(result);\n      }\n    } catch {\n      // Post-process is best-effort (e.g. entity linking); a failure leaves the un-rewritten text.\n    }\n  }\n\n  #clear(handle: unknown): void {\n    if (handle != null) {\n      this.#scheduler.clearTimeout(handle);\n    }\n  }\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport {\n  Annotation,\n  ChangeSet,\n  EditorState,\n  type Extension,\n  StateEffect,\n  StateField,\n  type Transaction,\n} from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';\n\nimport { Domino } from '@dxos/ui';\n\nimport { escapeRegExpSource } from '../../util/index.ts';\n\nexport type TypewriterOptions = {\n  /** Show a blinking cursor at the insertion point while streaming. */\n  cursor?: boolean;\n  /** Tag names whose inner content should be streamed (not buffered until close). */\n  streamingTags?: Set<string>;\n  /** Hard cap before falling back to single-flush mode. Defaults to 2 000 chars. */\n  flushThreshold?: number;\n  /** Per-frame time budget for draining (ms). Defaults to 20 ms. */\n  frameBudgetMs?: number;\n  /**\n   * Maximum visible characters of plain text emitted per animation frame. Atomic\n   * structures (XML elements, markdown links) always flush whole and are not subject\n   * to this cap. Default 1 — ~60 char/s at 60 fps. Raise for faster typewriter speed.\n   */\n  charsPerFrame?: number;\n};\n\n/**\n * Intercepts appended text and inserts it as time-budgeted batches per animation frame,\n * while flushing XML tags, markdown links, and images atomically for smooth typewriter-style\n * streaming. Falls back to bulk-flush when the buffer grows past `flushThreshold` to keep\n * the editor responsive on long runs.\n */\nexport const typewriter = (options: TypewriterOptions = {}): Extension => {\n  const streamingTags = options.streamingTags ?? new Set<string>();\n  const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;\n  const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;\n  const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;\n\n  // Effect to suppress a transaction from being applied (replaced by buffered insert).\n  const suppressAppend = StateEffect.define<{ from: number; text: string }>();\n  // Effect to insert text from the buffer (single batch per frame).\n  const insertChunk = StateEffect.define<{ from: number; text: string }>();\n\n  // State field that holds the pending buffer of text to drip into the document.\n  const bufferField = StateField.define<BufferState>({\n    create: () => ({ text: '', head: 0, insertAt: 0 }),\n    update: (value, tr) => {\n      let { text, head, insertAt } = value;\n      for (const effect of tr.effects) {\n        if (effect.is(suppressAppend)) {\n          // If pending was empty, anchor insertAt at the new append point.\n          if (text.length === head) {\n            insertAt = effect.value.from;\n          }\n          text += effect.value.text;\n        }\n        if (effect.is(insertChunk)) {\n          head += effect.value.text.length;\n          insertAt = effect.value.from + effect.value.text.length;\n          // Compact lazily so the prefix doesn't grow without bound.\n          if (head >= COMPACT_HEAD_THRESHOLD || (head > 0 && head * 2 >= text.length)) {\n            text = text.slice(head);\n            head = 0;\n          }\n        }\n      }\n\n      // Reset buffer when document is cleared or fully replaced.\n      if (tr.docChanged) {\n        let isReset = tr.state.doc.length === 0;\n        if (!isReset && tr.startState.doc.length > 0) {\n          tr.changes.iterChanges((fromA, toA) => {\n            if (fromA === 0 && toA === tr.startState.doc.length) {\n              isReset = true;\n            }\n          });\n        }\n        if (isReset) {\n          return { text: '', head: 0, insertAt: 0 };\n        }\n\n        // Map insertion position through document changes not caused by us.\n        if (!tr.effects.some((effect) => effect.is(insertChunk))) {\n          insertAt = tr.changes.mapPos(Math.min(insertAt, tr.startState.doc.length));\n        }\n      }\n\n      return { text, head, insertAt };\n    },\n  });\n\n  // Transaction filter: intercept appends at end-of-document and buffer them.\n  const filter = EditorState.transactionFilter.of((tr: Transaction) => {\n    if (!tr.docChanged) {\n      return tr;\n    }\n\n    // Allow bypassed and drip insertions through.\n    if (tr.annotation(typewriterBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {\n      return tr;\n    }\n\n    // Collect appended text at the end of the document.\n    let appendedText = '';\n    let appendFrom = -1;\n    let isAppendOnly = true;\n\n    tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {\n      if (toA === tr.startState.doc.length && fromA === toA && inserted.length > 0) {\n        appendedText += inserted.sliceString(0);\n        if (appendFrom === -1) {\n          appendFrom = fromA;\n        }\n      } else {\n        isAppendOnly = false;\n      }\n    });\n\n    if (!isAppendOnly || appendedText.length === 0) {\n      return tr;\n    }\n\n    // Suppress the original insert; buffer the text instead.\n    return {\n      changes: ChangeSet.empty(tr.startState.doc.length),\n      effects: suppressAppend.of({ from: appendFrom, text: appendedText }),\n    };\n  });\n\n  // View plugin that drains the buffer once per animation frame, emitting as many atomic\n  // chunks as fit within `frameBudgetMs`. Falls back to a single dispatch when the buffer\n  // grows past `flushThreshold`.\n  const drainPlugin = ViewPlugin.fromClass(\n    class {\n      _raf: number | undefined;\n      _activeStreamTag: string | null = null;\n\n      constructor(private view: EditorView) {\n        // Note: do NOT eagerly start the drain here. The buffer is empty at construction,\n        // and any synchronous `view.dispatch` inside the constructor is rejected by\n        // CodeMirror because we're inside the initial update flow.\n      }\n\n      update(update: ViewUpdate) {\n        const { text, head } = update.state.field(bufferField);\n        const pending = text.length - head;\n        if (pending === 0) {\n          this._activeStreamTag = null;\n        }\n        if (pending > 0 && this._raf === undefined) {\n          this._start();\n        }\n      }\n\n      _start() {\n        // Announce drain start. Deferred via microtask because `_start` runs from inside\n        // `update()`, where synchronous `view.dispatch` is disallowed.\n        queueMicrotask(() => {\n          this.view.dispatch({\n            effects: typewriterDrainingEffect.of(true),\n            annotations: typewriterBypass.of(true),\n          });\n        });\n        this._raf = requestAnimationFrame(this._tick);\n      }\n\n      _tick = () => {\n        const { text, head, insertAt } = this.view.state.field(bufferField);\n        const pending = text.length - head;\n\n        if (pending === 0) {\n          this.view.dispatch({\n            effects: typewriterDrainingEffect.of(false),\n            annotations: typewriterBypass.of(true),\n          });\n          this._raf = undefined;\n          return;\n        }\n\n        // Backpressure: flush everything in one shot when the buffer is too large to\n        // pace at typewriter rates. Streaming-tag context is dropped — anything still\n        // buffered (including a closing tag, if present) lands as one chunk.\n        if (pending > flushThreshold) {\n          const chunk = text.slice(head);\n          this._activeStreamTag = null;\n          this.view.dispatch({\n            changes: { from: insertAt, insert: chunk },\n            effects: insertChunk.of({ from: insertAt, text: chunk }),\n          });\n          this._raf = requestAnimationFrame(this._tick);\n          return;\n        }\n\n        // Time-budgeted batch: accumulate atomic chunks until we run out of budget,\n        // hit an incomplete structure, exhaust the buffer, or hit the per-frame\n        // visible-character cap. Atomic chunks (count > 1) are always emitted whole;\n        // the cap only throttles plain-text drips so the typewriter cadence stays\n        // visible at the per-frame level.\n        const startTime = performance.now();\n        let pos = head;\n        let activeTag = this._activeStreamTag;\n        let charsEmitted = 0;\n        while (pos < text.length && performance.now() - startTime < frameBudgetMs) {\n          const result = flushable(text, pos, streamingTags, activeTag);\n          if (result.count === 0) {\n            // Head is inside an incomplete structure — wait for more data.\n            break;\n          }\n          // Stop after the per-frame cap, but only once we've already emitted at\n          // least one chunk this frame — otherwise a multi-char atomic structure\n          // (e.g. a markdown link) could starve forever on a small cap.\n          if (charsEmitted > 0 && charsEmitted + result.count > charsPerFrame) {\n            break;\n          }\n          if (result.enterTag) {\n            activeTag = result.enterTag;\n          }\n          if (result.exitTag) {\n            activeTag = null;\n          }\n          pos += result.count;\n          charsEmitted += result.count;\n        }\n\n        const totalCount = pos - head;\n        if (totalCount > 0) {\n          const chunk = text.slice(head, head + totalCount);\n          this._activeStreamTag = activeTag;\n          this.view.dispatch({\n            changes: { from: insertAt, insert: chunk },\n            effects: insertChunk.of({ from: insertAt, text: chunk }),\n          });\n        }\n\n        // Continue draining next frame; if pending is 0 after the dispatch, the next\n        // tick will land on the early-out above and stop the loop.\n        this._raf = requestAnimationFrame(this._tick);\n      };\n\n      destroy() {\n        if (this._raf !== undefined) {\n          cancelAnimationFrame(this._raf);\n        }\n      }\n    },\n  );\n\n  return [bufferField, filter, drainPlugin, options.cursor && typewriterCursor(bufferField)].filter(\n    Boolean,\n  ) as Extension[];\n};\n\n/** Annotate a transaction to bypass the typewriter buffer (content appears immediately). */\nexport const typewriterBypass = Annotation.define<boolean>();\n\n/**\n * Public state effect signalling whether typewriter's drip queue is currently draining.\n * Other extensions can subscribe to coordinate behaviour with the typewriter (e.g. hide\n * a block-widget footer while text is being dripped to avoid scroll-measure conflicts).\n *\n * Dispatched at queue transitions:\n * - `true` — empty → non-empty (drain rAF/interval is starting).\n * - `false` — non-empty → empty (last char written, drain stopped).\n */\nexport const typewriterDrainingEffect = StateEffect.define<boolean>();\n\n/**\n * Buffer state. The pending text is `text.slice(head)` — but `head` is advanced\n * without slicing on every drip to avoid O(n²) string copying. The prefix is\n * compacted lazily once it exceeds half the string or `COMPACT_HEAD_THRESHOLD`.\n */\ntype BufferState = { text: string; head: number; insertAt: number };\n\n/** How long to show the cursor after motion has stopped. */\nconst CURSOR_LINGER = 3_000;\n\n/** Per-frame time budget for draining the buffer. Stays well under a 16 ms frame. */\nconst FRAME_BUDGET_MS = 4;\n\n/**\n * Visible characters of plain text emitted per animation frame. At 60 fps a value of 1\n * gives ~60 char/s — fast enough to keep up with most streams while still reading as a\n * smooth character-by-character drip. Atomic structures (XML elements, markdown links)\n * always flush whole regardless of this cap.\n */\nconst CHARS_PER_FRAME = 5;\n\n/**\n * When the pending buffer exceeds this many characters, abandon typewriter pacing\n * and flush the entire buffer in one transaction. The visual benefit of typewriter\n * has negative value past human-reading rates.\n */\nconst FLUSH_THRESHOLD = 2_000;\n\n/** Compaction trigger for the head-offset rope. */\nconst COMPACT_HEAD_THRESHOLD = 4_096;\n\n//\n// Cursor\n//\n\n/**\n * Blinking cursor widget at the insertion point while the buffer is draining.\n * Lingers for 2s after the buffer empties before being removed.\n */\nconst typewriterCursor = (bufferField: StateField<BufferState>): Extension => {\n  const hideCursor = StateEffect.define();\n\n  const visibilityField = StateField.define<{\n    visible: boolean;\n    insertAt: number;\n    /** Position after the last non-whitespace character was inserted. */\n    lastNonWsAt: number;\n  }>({\n    create: () => ({ visible: false, insertAt: 0, lastNonWsAt: 0 }),\n    update: (value, tr) => {\n      const { text, head, insertAt } = tr.state.field(bufferField);\n      const pending = text.length - head;\n      if (pending > 0) {\n        // Track the last position where a non-whitespace character was inserted.\n        let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));\n        if (tr.docChanged) {\n          tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {\n            const chunk = inserted.sliceString(0);\n            if (chunk.trim().length > 0) {\n              lastNonWsAt = _fromB + chunk.length;\n            }\n          });\n        }\n        return { visible: true, insertAt, lastNonWsAt };\n      }\n      for (const effect of tr.effects) {\n        if (effect.is(hideCursor)) {\n          return { ...value, visible: false };\n        }\n      }\n      return value;\n    },\n  });\n\n  const decorationField = StateField.define<DecorationSet>({\n    create: () => Decoration.none,\n    update: (_decorations, tr) => {\n      const { visible, insertAt, lastNonWsAt } = tr.state.field(visibilityField);\n      if (!visible) {\n        return Decoration.none;\n      }\n\n      const { text, head } = tr.state.field(bufferField);\n      // While draining, show cursor at the insertion point.\n      // When lingering (buffer empty), show at last non-whitespace position.\n      const cursorAt = text.length > head ? insertAt : lastNonWsAt;\n      const pos = Math.min(cursorAt, tr.state.doc.length);\n      return Decoration.set([\n        Decoration.widget({\n          widget: new CursorWidget(),\n          side: 1,\n        }).range(pos),\n      ]);\n    },\n    provide: (field) => EditorView.decorations.from(field),\n  });\n\n  const timerPlugin = ViewPlugin.fromClass(\n    class {\n      _timer: ReturnType<typeof setTimeout> | undefined;\n\n      constructor(private view: EditorView) {}\n\n      update(update: ViewUpdate) {\n        const { text, head } = update.state.field(bufferField);\n        const { visible } = update.state.field(visibilityField);\n        const pending = text.length - head;\n\n        if (pending > 0) {\n          clearTimeout(this._timer);\n          this._timer = undefined;\n        } else if (visible && this._timer === undefined) {\n          this._timer = setTimeout(() => {\n            this.view.dispatch({ effects: hideCursor.of(null) });\n            this._timer = undefined;\n          }, CURSOR_LINGER);\n        }\n      }\n\n      destroy() {\n        clearTimeout(this._timer);\n      }\n    },\n  );\n\n  return [visibilityField, decorationField, timerPlugin];\n};\n\n/**\n * U+2217 Asterisk\n * U+25CF Ballot Box\n */\nclass CursorWidget extends WidgetType {\n  // All instances are interchangeable — let CM reuse the existing DOM across drips so\n  // the blink animation isn't restarted on every transaction.\n  override eq(other: WidgetType): boolean {\n    return other instanceof CursorWidget;\n  }\n\n  toDOM() {\n    const inner = Domino.of('span').text('∗').style({ animation: 'blink 1s infinite', animationDelay: '250ms' });\n    return Domino.of('span').style({ opacity: '0.8' }).append(inner).root;\n  }\n}\n\n/**\n * Matches the local name of an opening tag after `<` (not `</`).\n * Custom elements use hyphens; `\\w+` alone incorrectly stops at `-` (e.g. `dom` from `dom-widget`).\n */\nconst OPENING_TAG_NAME = /^<([a-zA-Z][\\w-]*)/;\n\n/** Tag names are short — bound the slice we hand to the regex matcher. */\nconst TAG_NAME_PROBE = 64;\n\ntype FlushResult = {\n  count: number;\n  /** Tag name to enter streaming mode for. */\n  enterTag?: string;\n  /** Whether to exit streaming mode. */\n  exitTag?: boolean;\n};\n\n/**\n * Scans the buffer starting at `start` and returns the number of characters that can be\n * flushed. Returns 0 if the head of the buffer is inside an incomplete structure (XML\n * element, markdown link, or image) that should be flushed atomically. Returns > 1 when\n * a complete structure is at the head and should be emitted in one batch.\n *\n * When `activeStreamTag` is set, we're inside a streaming tag: inner content drips one\n * character at a time, and the closing tag is flushed atomically.\n */\nconst flushable = (\n  buffer: string,\n  start: number,\n  streamingTags: Set<string>,\n  activeStreamTag: string | null,\n): FlushResult => {\n  if (start >= buffer.length) {\n    return { count: 0 };\n  }\n\n  // Inside a streaming tag: drip content, flush closing tag atomically.\n  if (activeStreamTag) {\n    const closeTag = `</${activeStreamTag}>`;\n    if (buffer.startsWith(closeTag, start)) {\n      return { count: closeTag.length, exitTag: true };\n    }\n    // Nested XML element — buffer atomically.\n    if (buffer[start] === '<') {\n      return { count: xmlElementLength(buffer, start) };\n    }\n    // Drip inner content one character at a time.\n    return { count: 1 };\n  }\n\n  const ch = buffer[start];\n\n  // XML element.\n  if (ch === '<') {\n    // Tag-name match against a bounded slice (tag names are short).\n    const probe = buffer.slice(start, start + TAG_NAME_PROBE);\n    const nameMatch = probe.match(OPENING_TAG_NAME);\n    if (nameMatch && streamingTags.has(nameMatch[1])) {\n      const close = buffer.indexOf('>', start);\n      if (close === -1) {\n        return { count: 0 }; // Opening tag incomplete.\n      }\n      // Self-closing streaming tag — flush atomically, no streaming mode.\n      if (buffer[close - 1] === '/') {\n        return { count: close + 1 - start };\n      }\n      // Flush opening tag and enter streaming mode.\n      return { count: close + 1 - start, enterTag: nameMatch[1] };\n    }\n    // Non-streaming XML: buffer the entire element.\n    return { count: xmlElementLength(buffer, start) };\n  }\n\n  // Image: ![alt](url) — starts with '!'.\n  if (ch === '!' && buffer.length > start + 1 && buffer[start + 1] === '[') {\n    return { count: linkLength(buffer, start, start + 1) };\n  }\n\n  // Link: [text](url).\n  if (ch === '[') {\n    return { count: linkLength(buffer, start, start) };\n  }\n\n  return { count: 1 };\n};\n\n/**\n * Returns the length of a complete XML element starting at `start`, or 0 if the element\n * is incomplete. Handles self-closing tags, closing tags, and opening tags with nested\n * content. E.g., `<foo>content<bar />more</foo>` returns the full length.\n */\nexport const xmlElementLength = (buffer: string, start = 0): number => {\n  const close = buffer.indexOf('>', start);\n  if (close === -1) {\n    return 0; // Tag not closed yet.\n  }\n\n  // Self-closing tag: <foo />.\n  if (buffer[close - 1] === '/') {\n    return close + 1 - start;\n  }\n\n  // Closing tag: </foo>.\n  if (buffer[start + 1] === '/') {\n    return close + 1 - start;\n  }\n\n  // Opening tag: extract the tag name and find its matching closing tag.\n  const probe = buffer.slice(start, start + TAG_NAME_PROBE);\n  const nameMatch = probe.match(OPENING_TAG_NAME);\n  if (!nameMatch) {\n    // Not a valid tag (e.g., `< ` or `<123`); emit one character.\n    return 1;\n  }\n\n  const tagName = nameMatch[1];\n  let depth = 0;\n\n  // Walk through all tags in the buffer tracking nesting depth, starting at `start`.\n  const tagPattern = new RegExp(`<(/?)${escapeRegExpSource(tagName)}(\\\\s[^>]*)?>`, 'g');\n  tagPattern.lastIndex = start;\n  let match: RegExpExecArray | null;\n  while ((match = tagPattern.exec(buffer)) !== null) {\n    const isSelfClosing = match[0].endsWith('/>');\n    const isClosing = match[1] === '/';\n\n    if (isSelfClosing) {\n      // Self-closing doesn't change depth, but if depth is 0 this is the root.\n      if (depth === 0) {\n        return match.index + match[0].length - start;\n      }\n    } else if (isClosing) {\n      depth--;\n      if (depth === 0) {\n        return match.index + match[0].length - start;\n      }\n    } else {\n      depth++;\n    }\n  }\n\n  // Unbalanced — still waiting for closing tag.\n  return 0;\n};\n\n/**\n * Returns the length (from `start`) of a complete markdown link/image whose `[` is at\n * `bracketAt`, or 0 if the structure is incomplete. Returns 1 if the bracket is not part\n * of a link (no following `(`).\n */\nconst linkLength = (buffer: string, start: number, bracketAt: number): number => {\n  const bracketClose = buffer.indexOf(']', bracketAt + 1);\n  if (bracketClose === -1) {\n    return 0;\n  }\n\n  // Must be followed by '(' for a standard link.\n  if (bracketClose + 1 >= buffer.length) {\n    return 0;\n  }\n  if (buffer[bracketClose + 1] !== '(') {\n    // Not a link — just a bracket; emit one character.\n    return 1;\n  }\n\n  const parenClose = buffer.indexOf(')', bracketClose + 2);\n  if (parenClose === -1) {\n    return 0;\n  }\n\n  return parenClose + 1 - start;\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { EditorView, WidgetType } from '@codemirror/view';\nimport { type FunctionComponent } from 'react';\n\nimport { invariant } from '@dxos/invariant';\nimport { log } from '@dxos/log';\nimport { Domino } from '@dxos/ui';\n\nimport { type WidgetProps, type WidgetState } from './widgets.ts';\n\nexport interface WidgetNotifier {\n  mounted(widget: WidgetState): void;\n  /**\n   * `root` identifies the destroyed instance: when CodeMirror replaces a widget (same id, `eq`\n   * false — e.g. a context rebuild), it draws the NEW widget before destroying the OLD one, so an\n   * id-only unmount would wipe the replacement's fresh registration.\n   */\n  unmounted(id: string, root?: HTMLElement | null): void;\n  /**\n   * Re-render the mounted widget for `id` with updated props. Keyed by id rather than by widget\n   * instance: a rebuild constructs fresh widgets, but `StubWidget.eq` (id equality) makes CodeMirror\n   * keep the previously-rendered DOM, so the instance in the decoration set is not the one holding\n   * the mounted root.\n   */\n  updated(id: string, widgetState: Partial<WidgetProps>): void;\n  /**\n   * Drop any mounted widgets whose id is not in `liveIds`. Needed because CM reuses a widget's DOM\n   * via `updateDOM` (without calling `destroy`) when a decoration's widget changes in place, so an\n   * id that is no longer present — e.g. a position-keyed id after an edit shifted the node — would\n   * otherwise leak a stale portal.\n   */\n  reconcile(liveIds: Set<string>): void;\n}\n\n/**\n * Mounts a DOM placeholder that React portals render into.\n * Uses a block <div> for block widgets and an inline <span> for inline widgets.\n *\n * Culling a large block widget out of the viewport and re-showing it tears down and rebuilds the\n * portal, causing a blank frame + flicker. `#keepAlive` (for known-height blocks) caches `#root` and\n * keeps the portal mounted across culls so CM re-shows the same populated node; `toDOM` also gives the\n * placeholder a fixed height so CM's measurement is authoritative and content-independent.\n *\n * NOTE: This does NOT address the separate scroll-*jump* on large/fast scroll deltas. That was verified\n * (Playwright, both async and plain-sync block stories) to be widget-independent: CM's measure-phase\n * scroll re-anchor correcting its content-height *estimate* when a big scroll delta reveals a lot of\n * not-yet-measured content. Gradual scrolling does not jump. That is general CM heightmap behavior, not\n * fixable here — see upstream CM #1727 (https://code.haverbeke.berlin/codemirror/dev/issues/1727).\n */\nexport type StubWidgetOptions<TProps> = {\n  id: string;\n  Component: FunctionComponent<TProps>;\n  props: TProps;\n  notifier: WidgetNotifier;\n  /**\n   * The source text the props were built from, verbatim. `eq` consults it because a widget id is not\n   * always content-derived: a streaming tag is keyed on its opening position alone (its end moves every\n   * tick), so a run that grows in place keeps its id and would otherwise leave CodeMirror holding the\n   * instance built from the first chunk. Compared in full rather than hashed: a 32-bit hash collides on\n   * pairs as short as `ab`/`bA` (djb2's adjacent-character deltas cancel), and a collision here is a\n   * widget frozen at stale props — the bug this exists to prevent.\n   */\n  signature?: string;\n  streaming?: boolean;\n  block?: boolean;\n  /**\n   * Reserved block height (px). Feeds CodeMirror's off-screen viewport estimate and pre-sizes the\n   * placeholder so the block occupies its final height before the portaled content resolves —\n   * otherwise it collapses to the 24px minimum, causing scroll jitter and a blank on scroll-back.\n   */\n  blockHeight?: number;\n  /**\n   * How `blockHeight` is applied. `fixed` (the default) pins the box, which is right when the\n   * height is known up front — an image, a chart. `min` makes it a floor, for content that can\n   * legitimately grow after it mounts: a disclosure the reader opens is pinned shut by `fixed`,\n   * and clipped by its `overflow: hidden`.\n   */\n  heightMode?: 'fixed' | 'min';\n  /** Keep the root and its portal across culls and rebuilds even without a reserved height. */\n  keepAlive?: boolean;\n  /** When true, trace the widget's DOM lifecycle to diagnose scroll-cull jitter/jump (see PreviewScrollSurface). */\n  debug?: boolean;\n};\n\n/** Marks the placeholder a portaled widget renders into. */\nexport const WIDGET_ROOT_ATTRIBUTE = 'data-widget-root';\n\n/**\n * Frees a mounted block's reserved height in place, from any element the widget rendered: the\n * placeholder stops pinning and the editor re-measures, with no decoration rebuild — so nothing is\n * redrawn under the user. Pair with a `LinkWidgetState.intrinsic` report so the next rebuild does\n * not pin it again.\n */\nexport const releaseBlockHeight = (view: EditorView, element: Element): void => {\n  const root = element.closest(`[${WIDGET_ROOT_ATTRIBUTE}]`);\n  if (root instanceof HTMLElement && (root.style.height || root.style.minHeight)) {\n    root.style.height = '';\n    root.style.minHeight = '';\n    root.style.overflow = '';\n    view.requestMeasure();\n  }\n};\n\nexport class StubWidget<TProps extends WidgetProps> extends WidgetType {\n  #root: HTMLElement | null = null;\n  #view: EditorView | undefined;\n  // Throttle the (hot) coordsAt trace to at most once per frame.\n  #coordsLoggedThisFrame = false;\n\n  readonly id: string;\n  readonly Component: FunctionComponent<TProps>;\n  readonly props: TProps;\n  readonly notifier: WidgetNotifier;\n  readonly signature?: string;\n  readonly streaming?: boolean;\n  readonly block?: boolean;\n  readonly blockHeight?: number;\n  readonly heightMode?: 'fixed' | 'min';\n  readonly keepAlive?: boolean;\n  readonly debug?: boolean;\n\n  constructor(options: StubWidgetOptions<TProps>) {\n    super();\n    this.id = options.id;\n    this.Component = options.Component;\n    this.props = options.props;\n    this.notifier = options.notifier;\n    this.signature = options.signature;\n    this.streaming = options.streaming;\n    this.block = options.block;\n    this.blockHeight = options.blockHeight;\n    this.heightMode = options.heightMode;\n    this.keepAlive = options.keepAlive;\n    this.debug = options.debug;\n    invariant(this.id);\n  }\n\n  get root(): HTMLElement | null {\n    return this.#root;\n  }\n\n  /** Gated lifecycle trace. Enable per-tag via `XmlWidgetDef.debug` to watch the scroll-cull sequence. */\n  #trace(event: string, data?: Record<string, unknown>): void {\n    if (this.debug) {\n      log.info(`stub-widget: ${event}`, { id: this.id, block: this.block, ...data });\n    }\n  }\n\n  // CodeMirror reserves this height for the block while it is outside the rendered viewport.\n  override get estimatedHeight() {\n    return this.block && this.blockHeight != null ? this.blockHeight : -1;\n  }\n\n  // Report per-position screen coordinates inside the block so CM can anchor scroll correctly. Large\n  // block widgets need this alongside `estimatedHeight` (codemirror/dev#761); returning the same rect\n  // for every position confuses CM's geometry, so interpolate a vertical position across the widget's\n  // range (offset `pos` within `to - from`).\n  override coordsAt(dom: HTMLElement, pos: number, side: number) {\n    if (!this.block) {\n      return null;\n    }\n    const rect = dom.getBoundingClientRect();\n    const range = (this.props as WidgetProps).range;\n    const length = range ? range.to - range.from : 0;\n    const fraction = length > 0 ? Math.min(1, Math.max(0, pos / length)) : side > 0 ? 1 : 0;\n    const y = rect.top + rect.height * fraction;\n    if (this.debug) {\n      const detached = rect.height === 0 || (rect.top === 0 && rect.bottom === 0);\n      // A detached / re-parented node reports a zero rect; feeding that to CM's scroll anchor snaps the\n      // viewport to the document top. Flag it so the jump-to-top is attributable in the trace.\n      if (detached) {\n        log.warn('stub-widget: coordsAt on detached node (zero rect)', {\n          id: this.id,\n          pos,\n          side,\n          connected: dom.isConnected,\n          rect: { top: rect.top, height: rect.height },\n          y,\n        });\n      } else if (!this.#coordsLoggedThisFrame) {\n        // CM consults coordsAt to anchor the scroll; log one sample per frame to correlate with jumps.\n        this.#coordsLoggedThisFrame = true;\n        requestAnimationFrame(() => {\n          this.#coordsLoggedThisFrame = false;\n        });\n        log.info('stub-widget: coordsAt', {\n          id: this.id,\n          pos,\n          side,\n          rect: { top: Math.round(rect.top), height: Math.round(rect.height) },\n          y: Math.round(y),\n          scrollTop: Math.round(this.#view?.scrollDOM.scrollTop ?? -1),\n        });\n      }\n    }\n    return { left: rect.left, right: rect.right, top: y, bottom: y };\n  }\n\n  override eq(other: this) {\n    if (this.streaming) {\n      return false;\n    }\n\n    // Context too, not just the id: props are captured at build time, so an id-only comparison makes\n    // CodeMirror keep the existing widget (and its stale props) when the host publishes the context\n    // after the first build — leaving every widget callback bound to `undefined`.\n    // The signature too: an id that does not encode the tag's content (a streaming tag is keyed on\n    // its opening position) would otherwise pin the widget to the props of the first chunk, so a run\n    // that keeps appending to the same tag never re-renders until the document is rebuilt.\n    // `block` too: CodeMirror reuses an equal widget's element, and a block's div must never stand\n    // in for an inline span (or the reverse) when a link switches form. Not the reserved height: a\n    // host releases a pin on the mounted element (`releaseBlockHeight`), and a rebuild that differed\n    // only there would remount every embed for nothing.\n    const context = (props: TProps) => (props as WidgetProps).context;\n    return (\n      this.id === other.id &&\n      this.block === other.block &&\n      this.signature === other.signature &&\n      context(this.props) === context(other.props)\n    );\n  }\n\n  override ignoreEvent() {\n    return true;\n  }\n\n  /**\n   * Keep the root DOM (and its portal) alive across CM viewport culls, for block widgets whose height\n   * we know up front. A cull calls `destroy()` then `toDOM()` on the SAME widget instance; reusing the\n   * cached, already-rendered node instead of rebuilding it keeps the block's measured geometry stable\n   * across the cull boundary, which sidesteps CM's measure-phase scroll re-anchor (upstream #1727) and\n   * the blank/flicker from the portal remounting. Genuine removals (the tag edited out) are pruned by\n   * `notifier.reconcile` on the next document change, not by cull. Scoped to known-height blocks and\n   * blocks that ask for it, so ordinary widgets keep CM's normal virtualization (no unbounded retained\n   * portals). Rebuilds go through the same path: a kept block whose portal would otherwise remount\n   * into a fresh root keeps its element, and with it any focus the user has in it.\n   */\n  get #keepAlive(): boolean {\n    return !!this.block && (this.keepAlive === true || this.blockHeight != null);\n  }\n\n  override toDOM(view: EditorView) {\n    this.#view = view;\n    const cached = this.#keepAlive && this.#root != null;\n    if (!this.#root) {\n      this.#root = this.block ? Domino.of('div').classNames('min-h-[24px]').root : Domino.of('span').root;\n      this.#root.setAttribute(WIDGET_ROOT_ATTRIBUTE, '');\n      this.#applyBlockHeight(this.#root);\n    }\n\n    const props = Object.assign({}, this.props, { view }) as TProps;\n    // `WidgetState` erases the specific `TProps` this instance renders with (a heterogeneous list of\n    // portaled widgets shares one array element type); `Component` and `props` are still built from\n    // the same `TProps` above, so the pairing stays sound despite the erasure.\n    this.notifier.mounted({\n      id: this.id,\n      root: this.#root,\n      props,\n      Component: this.Component as FunctionComponent<WidgetProps>,\n    });\n    this.#trace(cached ? 'toDOM (reuse cached root)' : 'toDOM (create)', {\n      blockHeight: this.blockHeight,\n      scrollTop: Math.round(view.scrollDOM.scrollTop),\n    });\n    this.#measureAfterPaint('toDOM');\n    return this.#root;\n  }\n\n  // Called on the REPLACEMENT widget with the outgoing instance's DOM, so `view` arrives as the\n  // argument — `#view` is only ever set by this instance's own `toDOM`, which has not run.\n  override updateDOM(dom: HTMLElement, view: EditorView) {\n    // CodeMirror offers the outgoing DOM to any widget of the same class; a block cannot continue\n    // in an inline span (or the reverse), so decline and let `toDOM` build the right element.\n    if (this.block !== (dom.tagName === 'DIV')) {\n      return false;\n    }\n    this.#root = dom;\n    this.#view = view;\n    // The reserved height is this instance's, not the outgoing widget's: a host that released it\n    // (`intrinsic`) or set another must not inherit the old pin from the reused element.\n    this.#applyBlockHeight(dom);\n    const props = Object.assign({}, this.props, { view }) as TProps;\n    this.notifier.mounted({\n      id: this.id,\n      root: this.#root,\n      props,\n      Component: this.Component as FunctionComponent<WidgetProps>,\n    });\n    this.#trace('updateDOM (reuse/re-parent)', { connected: dom.isConnected });\n    this.#measureAfterPaint('updateDOM');\n    return true;\n  }\n\n  override destroy(_dom: HTMLElement) {\n    if (this.#keepAlive) {\n      // Cull, not removal: keep #root and the portal mounted so the next toDOM re-shows the identical,\n      // already-rendered node. `notifier.reconcile` drops it if the tag is genuinely removed.\n      this.#trace('destroy (cull, keep-alive)', { scrollTop: Math.round(this.#view?.scrollDOM.scrollTop ?? -1) });\n      return;\n    }\n\n    this.#trace('destroy (cull)', { scrollTop: Math.round(this.#view?.scrollDOM.scrollTop ?? -1) });\n    this.notifier.unmounted(this.id, this.#root ?? _dom);\n    this.#root = null;\n    this.#view = undefined;\n  }\n\n  /** Pins, floors, or frees the block per `blockHeight`/`heightMode`; a no-op for inline widgets. */\n  #applyBlockHeight(root: HTMLElement): void {\n    if (!this.block) {\n      return;\n    }\n    const pinned = this.blockHeight != null && this.heightMode !== 'min';\n    const floored = this.blockHeight != null && this.heightMode === 'min';\n    // A floor rather than a pin: the box cannot be measured empty while its portaled content is\n    // still to paint, and can still grow when that content changes size.\n    root.style.minHeight = floored ? `${this.blockHeight}px` : '';\n    // Fixed (not min) height: give CM an authoritative, content-independent measurement so an async\n    // widget that mounts / re-lays-out later cannot perturb the heightmap (we know the height up\n    // front). `overflow: hidden` keeps content that briefly overshoots from changing the measured box.\n    root.style.height = pinned ? `${this.blockHeight}px` : '';\n    root.style.overflow = pinned ? 'hidden' : '';\n  }\n\n  /**\n   * After the next paint, compare the measured block height against the reserved `estimatedHeight`.\n   * A large discrepancy is what shifts CM's total-height model and drives the scroll jump; logging it\n   * pinpoints when (and by how much) the portaled content diverges from the reserved space.\n   */\n  #measureAfterPaint(source: string): void {\n    if (!this.debug || !this.block) {\n      return;\n    }\n\n    const root = this.#root;\n    requestAnimationFrame(() => {\n      if (!root) {\n        return;\n      }\n\n      const measured = root.getBoundingClientRect().height;\n      const reserved = this.blockHeight ?? -1;\n      log.info(`stub-widget: measured after ${source}`, {\n        id: this.id,\n        reserved,\n        measured,\n        delta: reserved > 0 ? measured - reserved : undefined,\n        connected: root.isConnected,\n      });\n    });\n  }\n}\n","//\n// Copyright 2025 DXOS.org\n//\n\n// Widgets: syntax-tree ranges replaced by CodeMirror widgets — native ones rendered inline, React/Solid\n// ones portaled by the host into a placeholder. This module owns everything after a match: the one\n// decoration field (block decorations cannot come from a view plugin), the placeholder lifecycle the\n// host renders portals from, widget state and context effects, and bookmark navigation. What is\n// matched is a matcher's business: `xmlTags` claims XML elements, `objectLinks` object links.\n\nimport { ensureSyntaxTree, syntaxTree } from '@codemirror/language';\nimport {\n  type EditorState,\n  type Extension,\n  Facet,\n  Prec,\n  RangeSetBuilder,\n  StateEffect,\n  StateField,\n} from '@codemirror/state';\nimport {\n  Decoration,\n  type DecorationSet,\n  EditorView,\n  ViewPlugin,\n  type ViewUpdate,\n  type WidgetType,\n  keymap,\n} from '@codemirror/view';\nimport { type SyntaxNodeRef } from '@lezer/common';\nimport { type FunctionComponent, type ReactNode } from 'react';\n\nimport { log } from '@dxos/log';\n\nimport { type Range } from '../../types/index.ts';\nimport { decorationSetToArray } from '../../util/index.ts';\nimport { crawlerLineEffect } from '../streaming/index.ts';\nimport { StubWidget, type WidgetNotifier } from './stub.ts';\n\n//\n// Types\n//\n\n/**\n * StateEffect for navigating to previous bookmark.\n */\nexport const navigatePreviousEffect = StateEffect.define<void>();\n\n/**\n * StateEffect for navigating to next bookmark.\n */\nexport const navigateNextEffect = StateEffect.define<void>();\n\n/**\n * Dispatch function for updating state.\n */\nexport type StateDispatch<T> = T | ((state: T) => T);\n\n/**\n * Manages widget state.\n */\nexport interface WidgetStateManager {\n  updateWidget<T>(id: string, props: StateDispatch<T>): void;\n}\n\nexport type WidgetEventHandler<TEvent = unknown> = (event: TEvent) => void;\n\n/**\n * Props every widget receives: what matched (`_tag`), where, and the host's context. `TProps`\n * defaults to an index signature (rather than `unknown`) so a matcher can carry the tag's own\n * caller-defined attributes (see `xml-tags.ts`) without narrowing them first.\n */\nexport type WidgetProps<TProps = Record<string, unknown>, TContext = unknown> = TProps & {\n  _tag: string;\n  view?: EditorView | null;\n  range: Range;\n  children?: ReactNode[];\n  context?: TContext;\n  onEvent?: WidgetEventHandler;\n};\n\n/**\n * Factory for creating widgets.\n */\nexport type WidgetFactory<TProps extends WidgetProps = WidgetProps> = (props: TProps) => WidgetType | null;\n\n/**\n * How a match is rendered.\n * NOTE: Widgets should NOT use top/bottom margins (it causes unstable measurements while scrolling which leads to jumps).\n * If required, use encapsulated divs with padding instead.\n */\nexport type WidgetDef<TProps extends WidgetProps = WidgetProps> = {\n  /**\n   * Block widget.\n   */\n  block?: boolean;\n\n  /**\n   * Debug only.\n   */\n  debug?: boolean;\n\n  /**\n   * React/Solid widget (rendered in portals outside of the editor).\n   */\n  Component?: FunctionComponent<TProps>;\n\n  /**\n   * Native widget (rendered inline).\n   */\n  factory?: WidgetFactory<TProps>;\n\n  /**\n   * Reserved block height (px) derived from the widget's props (e.g. parsed from the image label),\n   * used to size the placeholder and CodeMirror's viewport estimate before the React content mounts.\n   * Only meaningful for block widgets.\n   */\n  estimatedHeight?: (props: TProps) => number | undefined;\n\n  /**\n   * How `estimatedHeight` is applied: `fixed` (default) pins the block, `min` treats it as a floor.\n   * Use `min` for a widget whose height changes after it mounts — a disclosure, a growing log —\n   * since a pinned box cannot open and clips what it holds.\n   */\n  heightMode?: 'fixed' | 'min';\n\n  /**\n   * Keep a block's placeholder (and portal) mounted across viewport culls and decoration rebuilds\n   * even without a reserved height — otherwise only known-height blocks are kept. For a bounded set\n   * of widgets (a document's embeds), not a stream: every kept block stays mounted off screen.\n   */\n  keepAlive?: boolean;\n};\n\nexport type WidgetState = {\n  id: string;\n  root: HTMLElement;\n  props: WidgetProps;\n  Component: FunctionComponent<WidgetProps>;\n};\n\nexport type { WidgetNotifier };\n\n//\n// Effects\n//\n\n/**\n * Update context.\n */\nexport const widgetContextEffect = StateEffect.define<unknown>();\n\n/**\n * Reset all state.\n */\nexport const widgetResetEffect = StateEffect.define();\n\n/**\n * Force a full decoration rebuild without a document change — dispatched once background parsing of a\n * long document completes, so blocks past the initial parse window are decorated without needing an edit.\n */\nexport const widgetRebuildEffect = StateEffect.define();\n\n/** A widget update either replaces the accumulated props outright or derives them from the prior value. */\ntype WidgetPropsUpdate = Partial<WidgetProps> | ((prev: Partial<WidgetProps> | undefined) => Partial<WidgetProps>);\n\n/**\n * Update widget.\n */\nexport const widgetUpdateEffect = StateEffect.define<{ id: string; value: WidgetPropsUpdate }>();\n\n//\n// Matchers\n//\n\n/** Widget props updates accumulated by id, merged over a widget's baked props at mount/re-render. */\ntype WidgetStateMap = Record<string, Partial<WidgetProps>>;\n\n/** What a matcher sees for one node of the tree walk. */\nexport type WidgetMatchContext = {\n  state: EditorState;\n  /** Opaque host-supplied value (theme, callbacks, …); narrowed by the host's own widget code. */\n  context: unknown;\n  widgetStateMap: WidgetStateMap;\n  notifier: WidgetNotifier;\n  /** Per-build scratch counters, for ids that must not depend on document position. */\n  counters: Map<string, number>;\n};\n\n/** What a matcher sees for the document tail after the walk. */\nexport type WidgetTailContext = Omit<WidgetMatchContext, 'counters'> & {\n  range: Range;\n};\n\nexport type WidgetMatch = {\n  from: number;\n  to: number;\n  decoration: Decoration;\n  /** Start of an unclosed streaming range, which the next rebuild starts from. */\n  streamingFrom?: number;\n};\n\n/**\n * Turns syntax nodes into widget decorations. Matchers are contributed by extensions such as\n * `xmlTags` and `objectLinks` and run in the order they are registered.\n */\nexport type WidgetMatcher = {\n  /** Syntax node types this matcher claims; a claimed node is not descended into. */\n  nodes: string[];\n  /** Whether any of the matcher's widgets is in debug mode. */\n  debug?: boolean;\n  match: (node: SyntaxNodeRef, ctx: WidgetMatchContext) => WidgetMatch | undefined;\n  /** Runs after the walk over the rebuilt range — for markup the parser has not closed yet. */\n  tail?: (ctx: WidgetTailContext) => WidgetMatch | undefined;\n};\n\n/** Matchers, in registration order. */\nexport const widgetMatchersFacet = Facet.define<WidgetMatcher, readonly WidgetMatcher[]>({\n  combine: (matchers) => matchers,\n});\n\n//\n// Host\n//\n\nexport type WidgetHostOptions = {\n  /** Tags to bookmark for navigation. */\n  bookmarks?: string[];\n\n  /** Called when widgets are mounted or unmounted. */\n  setWidgets?: (widgets: WidgetState[]) => void;\n};\n\nconst widgetHostFacet = Facet.define<WidgetHostOptions, WidgetHostOptions>({\n  combine: (options) => ({\n    setWidgets: options.find((option) => option.setWidgets)?.setWidgets,\n    bookmarks: options.flatMap((option) => option.bookmarks ?? []),\n  }),\n});\n\n/**\n * The host's side of the widget lifecycle: where portaled widgets are reported, and which tags\n * bookmark navigation jumps between. One per editor, alongside the matchers it hosts.\n */\nexport const widgetHost = (options: WidgetHostOptions): Extension => widgetHostFacet.of(options);\n\n//\n// Widget creation\n//\n\n/** Stable id for portaled React/Solid widgets; explicit `id` on the tag wins for `updateWidget`. */\nexport const widgetId = (explicit: unknown, fallback: string): string =>\n  typeof explicit === 'string' && explicit.length > 0 ? explicit : fallback;\n\nexport type CreateWidgetOptions<TProps extends WidgetProps> = {\n  def: WidgetDef<TProps>;\n  id: string;\n  props: TProps;\n  notifier: WidgetNotifier;\n  /** The matched text, so a rebuilt widget with the same id is still replaced when its source changed. */\n  signature?: string;\n  streaming?: boolean;\n};\n\n/**\n * The widget a definition renders for a match: the factory's native widget, or a placeholder the host\n * portals the component into. Undefined when the definition has neither, or the factory declines.\n */\nexport const createWidget = <TProps extends WidgetProps>({\n  def,\n  id,\n  props,\n  notifier,\n  signature,\n  streaming,\n}: CreateWidgetOptions<TProps>): WidgetType | undefined => {\n  if (def.factory) {\n    return def.factory(props) ?? undefined;\n  }\n  if (def.Component) {\n    // Known-height block widgets get their reserved height so the placeholder's fixed-height\n    // keep-alive applies before the component mounts.\n    const blockHeight = def.block ? def.estimatedHeight?.(props) : undefined;\n    const widget = new StubWidget({\n      id,\n      Component: def.Component,\n      props,\n      notifier,\n      signature,\n      streaming,\n      block: !!def.block,\n      blockHeight,\n      heightMode: def.heightMode,\n      keepAlive: def.keepAlive,\n      debug: def.debug,\n    });\n    return widget;\n  }\n  return undefined;\n};\n\n//\n// State\n//\n\n/**\n * Context state.\n */\nconst widgetContextStateField = StateField.define<unknown>({\n  create: () => undefined,\n  update: (value, tr) => {\n    for (const effect of tr.effects) {\n      if (effect.is(widgetContextEffect)) {\n        return effect.value;\n      }\n    }\n\n    return value;\n  },\n});\n\n/**\n * Widget state management.\n */\nconst widgetStateMapStateField = StateField.define<WidgetStateMap>({\n  create: () => ({}),\n  update: (map, tr) => {\n    // Reduce over every effect: a transaction may carry several updates (or a reset followed by them),\n    // and returning on the first would silently drop the rest.\n    let next = map;\n    for (const effect of tr.effects) {\n      if (effect.is(widgetResetEffect)) {\n        next = {};\n      } else if (effect.is(widgetUpdateEffect)) {\n        // Update accumulated widget props by id.\n        const { id, value } = effect.value;\n        log('widget updated', { id, value });\n        next = { ...next, [id]: typeof value === 'function' ? value(next[id]) : value };\n      }\n    }\n\n    return next;\n  },\n});\n\n/** The accumulated state a host has reported for a widget id, if any. */\nexport const getWidgetState = (state: EditorState, id: string): Partial<WidgetProps> | undefined =>\n  state.field(widgetStateMapStateField, false)?.[id];\n\n/**\n * Re-applies the accumulated widget state at mount time.\n *\n * A widget's props are baked when its decoration is built, but state can be dispatched before the\n * widget mounts — rehydration after a document reset targets widgets that are still outside\n * CodeMirror's viewport, and `widgetUpdateEffect` alone rebuilds no decorations. Such a widget would\n * otherwise mount with the stale props it was built with, since `StubWidget.eq` (id equality) stops a\n * later rebuild from replacing the instance.\n */\nconst withCurrentWidgetState = (state: WidgetState): WidgetState => {\n  const view: EditorView | null | undefined = state.props?.view;\n  const widgetState = view?.state.field(widgetStateMapStateField, false)?.[state.id];\n  return widgetState ? { ...state, props: { ...state.props, ...widgetState } } : state;\n};\n\n/**\n * Manages the collection of widgets.\n */\nconst createWidgetMap = (setWidgets?: WidgetHostOptions['setWidgets'], debug = false): WidgetNotifier => {\n  const widgets = new Map<string, WidgetState>();\n\n  // TODO(burdon): Batch updates?\n  const notifier = {\n    mounted: (rawState: WidgetState) => {\n      const state = withCurrentWidgetState(rawState);\n      const isNew = !widgets.has(state.id);\n      widgets.set(state.id, state);\n      // Only a genuinely new id changes the portal set (blank-frame culprit); re-parents just re-notify.\n      if (debug && isNew) {\n        log.info('widget-map: mounted', { id: state.id, count: widgets.size });\n      }\n      setWidgets?.([...widgets.values()]);\n    },\n    updated: (id: string, widgetState: Partial<WidgetProps>) => {\n      const current = widgets.get(id);\n      if (!current || !widgetState) {\n        return;\n      }\n\n      widgets.set(id, { ...current, props: { ...current.props, ...widgetState } });\n      setWidgets?.([...widgets.values()]);\n    },\n    unmounted: (id: string, root?: HTMLElement | null) => {\n      // Stale destroy: a replacement widget (same id) already registered a newer root — CodeMirror\n      // draws the replacement before destroying the old instance, and deleting here would orphan\n      // the live placeholder with no portal (embeds vanished until a view-mode toggle).\n      const current = widgets.get(id);\n      if (!current || (root && current.root !== root)) {\n        return;\n      }\n      widgets.delete(id);\n      // A cull drops the portal for a frame before it re-mounts — this is the blank-space window.\n      if (debug) {\n        log.info('widget-map: unmounted', { id, count: widgets.size });\n      }\n      setWidgets?.([...widgets.values()]);\n    },\n    reconcile: (liveIds: Set<string>) => {\n      let changed = false;\n      for (const id of [...widgets.keys()]) {\n        if (!liveIds.has(id)) {\n          widgets.delete(id);\n          changed = true;\n        }\n      }\n      if (changed) {\n        if (debug) {\n          log.info('widget-map: reconcile pruned', { live: [...liveIds], count: widgets.size });\n        }\n        setWidgets?.([...widgets.values()]);\n      }\n    },\n  } satisfies WidgetNotifier;\n\n  return notifier;\n};\n\ntype WidgetDecorationSet = {\n  from: number;\n  /** Start position of an active unclosed streaming tag (for rebuild range). */\n  streamingFrom?: number;\n  decorations: DecorationSet;\n  /** `decorations` as the ranges the caret skips; block widgets are widened past their line breaks. */\n  atomic: DecorationSet;\n  /** Created with the field, from the host's `setWidgets`; the update plugin reports through it. */\n  notifier: WidgetNotifier;\n};\n\n/** Ceiling on parsing ahead of the background parser; past it `parseProgressPlugin` rebuilds. */\nconst PARSE_BUDGET = 50;\n\n/**\n * Creates widget decorations over `range` by handing each claimed node to the matchers, then the\n * document tail for markup the parser has not closed yet.\n */\nconst buildDecorations = (\n  state: EditorState,\n  range: Range,\n  notifier: WidgetNotifier,\n  /** Parse ahead of the background parser rather than reading a partial tree — see {@link PARSE_BUDGET}. */\n  forceParse = false,\n): Omit<WidgetDecorationSet, 'notifier'> => {\n  const matchers = state.facet(widgetMatchersFacet);\n  const context = state.field(widgetContextStateField, false);\n  const widgetStateMap = state.field(widgetStateMapStateField, false) ?? {};\n  const builder = new RangeSetBuilder<Decoration>();\n\n  // A fresh view has parsed only about a viewport, so a large payload's `Element` is absent at\n  // first paint and its markup renders raw until a rebuild — the flash on a remounted row.\n  const tree = (forceParse ? ensureSyntaxTree(state, range.to, PARSE_BUDGET) : null) ?? syntaxTree(state);\n  if (!tree || (tree.type.name === 'Program' && tree.length === 0)) {\n    return { from: range.from, decorations: Decoration.none, atomic: Decoration.none };\n  }\n\n  let last = range.from;\n  let streamingFrom: number | undefined;\n  const ctx: WidgetMatchContext = { state, context, widgetStateMap, notifier, counters: new Map() };\n  // Collected, then added sorted: the builder requires ascending ranges, and the streaming tail\n  // can start before a link the tree walk already matched in a later paragraph.\n  const matches: WidgetMatch[] = [];\n\n  tree.iterate({\n    from: range.from,\n    to: range.to,\n    enter: (node) => {\n      const claimants = matchers.filter((matcher) => matcher.nodes.includes(node.type.name));\n      if (claimants.length === 0) {\n        return;\n      }\n\n      for (const matcher of claimants) {\n        try {\n          const match = matcher.match(node, ctx);\n          if (match) {\n            matches.push(match);\n            // Track last widget (NOTE: range is inclusive).\n            last = match.to - 1;\n            break;\n          }\n        } catch (err) {\n          log.catch(err);\n        }\n      }\n\n      // Don't descend into children.\n      return false;\n    },\n  });\n\n  for (const matcher of matchers) {\n    const match = matcher.tail?.({ state, range, context, widgetStateMap, notifier });\n    if (match) {\n      // The tail claims everything after it: a match the walk made inside that range would overlap.\n      const kept = matches.filter((candidate) => candidate.to <= match.from);\n      matches.length = 0;\n      matches.push(...kept, match);\n      streamingFrom = match.streamingFrom;\n      last = match.from;\n      // Only one streaming range at a time.\n      break;\n    }\n  }\n\n  for (const match of matches.sort((a, b) => a.from - b.from || a.to - b.to)) {\n    builder.add(match.from, match.to, match.decoration);\n  }\n\n  const decorations = builder.finish();\n  return { from: last, streamingFrom, decorations, atomic: atomicRanges(state, decorations) };\n};\n\n/**\n * Builds and maintains the widget decorations.\n * Must be a StateField because block decorations cannot be provided via ViewPlugin.\n */\nconst widgetDecorationsField = StateField.define<WidgetDecorationSet>({\n  create: (state) => {\n    const { setWidgets } = state.facet(widgetHostFacet);\n    const debug = state.facet(widgetMatchersFacet).some((matcher) => matcher.debug);\n    const notifier = createWidgetMap(setWidgets, debug);\n    // Forced only here, so a streamed chunk is not charged for a parse it will get anyway.\n    return { ...buildDecorations(state, { from: 0, to: state.doc.length }, notifier, true), notifier };\n  },\n  update: ({ from, streamingFrom, decorations, atomic, notifier }, tr) => {\n    const rebuild = (range: Range): WidgetDecorationSet => ({\n      ...buildDecorations(tr.state, range, notifier),\n      notifier,\n    });\n    const whole = { from: 0, to: tr.state.doc.length };\n\n    // Check for reset effect.\n    for (const effect of tr.effects) {\n      if (effect.is(widgetResetEffect)) {\n        if (tr.docChanged) {\n          return rebuild(whole);\n        }\n        return { from: 0, decorations: Decoration.none, atomic: Decoration.none, notifier };\n      }\n      // Full rebuild once background parsing has advanced (no document change).\n      if (effect.is(widgetRebuildEffect)) {\n        return rebuild(whole);\n      }\n      // Widget props capture the context at build time and `StubWidget.eq` compares ids, so a context\n      // set after the first build would otherwise never reach a widget — leaving its callbacks bound to\n      // `undefined` for the life of the document.\n      if (effect.is(widgetContextEffect)) {\n        return rebuild(whole);\n      }\n    }\n\n    if (tr.docChanged) {\n      const { state } = tr;\n      // Flag if the transaction has modified the head of the document.\n      const reset = tr.changes.touchesRange(0, from);\n      if (reset) {\n        log('document reset', { from, to: state.doc.length });\n        // Full rebuild from start.\n        return rebuild(whole);\n      } else {\n        // Rebuild from the streaming tag start (if active) so the tree walk can detect completion.\n        const rebuildFrom = streamingFrom ?? from;\n        const result = buildDecorations(state, { from: rebuildFrom, to: state.doc.length }, notifier);\n        const next = decorations.update({\n          // Remove old streaming decorations — they are rebuilt each tick.\n          filter: (_f, _t, deco) => !deco.spec.streaming,\n          add: decorationSetToArray(result.decorations),\n        });\n        return {\n          from: result.from,\n          streamingFrom: result.streamingFrom,\n          decorations: next,\n          atomic: atomicRanges(state, next),\n          notifier,\n        };\n      }\n    }\n\n    return { from, streamingFrom, decorations, atomic, notifier };\n  },\n  provide: (field) => [\n    EditorView.decorations.from(field, (v) => v.decorations),\n    EditorView.atomicRanges.of((view) => view.state.field(field).atomic || Decoration.none),\n  ],\n});\n\n/**\n * The ranges cursor motion skips over. A block widget replaces whole lines, so both ends of its own\n * range sit on the hidden line and the caret vanished there for two presses; widened by the line\n * breaks on either side, a single press lands on the neighbouring line instead.\n */\nconst atomicRanges = (state: EditorState, decorations: DecorationSet): DecorationSet =>\n  Decoration.set(\n    decorationSetToArray(decorations).map((range) =>\n      range.value.spec.block\n        ? range.value.range(Math.max(0, range.from - 1), Math.min(state.doc.length, range.to + 1))\n        : range.value.range(range.from, range.to),\n    ),\n    true,\n  );\n\n//\n// Plugins\n//\n\n/**\n * Debug-only: flag large single-event scroll deltas (the \"jump\") so they can be correlated with the\n * widget lifecycle trace. A smooth user scroll produces small per-event deltas; a jump-to-top is one\n * big negative delta to near-zero.\n */\nconst scrollJumpMonitor = (() => {\n  let last = -1;\n  return EditorView.domEventHandlers({\n    scroll: (_event, view) => {\n      if (!view.state.facet(widgetMatchersFacet).some((matcher) => matcher.debug)) {\n        return;\n      }\n      const top = view.scrollDOM.scrollTop;\n      if (last >= 0 && Math.abs(top - last) > 600) {\n        log.warn('widgets: scroll jump', {\n          from: Math.round(last),\n          to: Math.round(top),\n          delta: Math.round(top - last),\n        });\n      }\n      last = top;\n    },\n  });\n})();\n\n/**\n * Re-decorate whenever the syntax tree changes. `StateField.create` (and incremental rebuilds) see\n * only the tree parsed so far: background parsing extends it later, and a parse that runs out of time\n * skips ranges outside the viewport and parses them as they scroll in, neither with a document change.\n * Edits rebuild too: the incremental merge duplicates a widget appended after the last one (see the\n * `xml-tags` invariant tests), and this rebuild settles it. Rebuilds within one task coalesce.\n */\nconst parseProgressPlugin = ViewPlugin.fromClass(\n  class {\n    #pending = false;\n\n    update(update: ViewUpdate) {\n      if (this.#pending || syntaxTree(update.startState) === syntaxTree(update.state)) {\n        return;\n      }\n      this.#pending = true;\n      // Defer: cannot dispatch during an update.\n      queueMicrotask(() => {\n        this.#pending = false;\n        update.view.dispatch({ effects: widgetRebuildEffect.of(null) });\n      });\n    }\n  },\n);\n\n/**\n * Navigation keys; active only when the host bookmarks something.\n */\nconst keyHandlers = Prec.highest(\n  keymap.of([\n    {\n      key: 'Mod-ArrowUp',\n      run: (view) => {\n        if (!view.state.facet(widgetHostFacet).bookmarks?.length) {\n          return false;\n        }\n        view.dispatch({ effects: navigatePreviousEffect.of() });\n        return true;\n      },\n    },\n    {\n      key: 'Mod-ArrowDown',\n      run: (view) => {\n        if (!view.state.facet(widgetHostFacet).bookmarks?.length) {\n          return false;\n        }\n        view.dispatch({ effects: navigateNextEffect.of() });\n        return true;\n      },\n    },\n  ]),\n);\n\n/**\n * Effect processing plugin for navigation.\n * Handles navigation up/down effects.\n */\nconst navigationEffectPlugin = EditorView.updateListener.of((update) => {\n  const { bookmarks } = update.state.facet(widgetHostFacet);\n  update.transactions.forEach((transaction) => {\n    for (const effect of transaction.effects) {\n      if (effect.is(navigatePreviousEffect)) {\n        const view = update.view;\n        const cursorPos = view.state.doc.lineAt(view.state.selection.main.head).from;\n        let widget: { from: number; to: number; tag: string } | null = null;\n        const { decorations } = view.state.field(widgetDecorationsField);\n        for (const range of decorationSetToArray(decorations)) {\n          if (range.from < cursorPos) {\n            const tag = range.value.spec.tag;\n            if (bookmarks?.includes(tag)) {\n              if (!widget || range.from > widget.from) {\n                widget = { from: range.from, to: range.to, tag };\n              }\n            }\n          }\n        }\n\n        const line = view.state.doc.lineAt(widget?.from ?? 0);\n        view.dispatch({\n          selection: { anchor: line.from, head: line.from },\n          effects: crawlerLineEffect.of({ line: line.number - 1, offset: -16 }),\n        });\n\n        continue;\n      }\n\n      if (effect.is(navigateNextEffect)) {\n        const view = update.view;\n        const cursorPos = view.state.doc.lineAt(view.state.selection.main.head).to;\n        let widget: { from: number; to: number; tag: string } | null = null;\n        const { decorations } = view.state.field(widgetDecorationsField);\n        for (const range of decorationSetToArray(decorations)) {\n          if (range.from > cursorPos) {\n            const tag = range.value.spec.tag;\n            if (bookmarks?.includes(tag)) {\n              if (!widget || range.from < widget.from) {\n                widget = { from: range.from, to: range.to, tag };\n              }\n            }\n          }\n        }\n\n        if (widget) {\n          const line = view.state.doc.lineAt(widget?.from);\n          view.dispatch({\n            selection: { anchor: line.to, head: line.to },\n            effects: crawlerLineEffect.of({ line: line.number - 1, offset: -16 }),\n          });\n        } else {\n          const line = view.state.doc.lineAt(view.state.doc.length);\n          view.dispatch({\n            selection: { anchor: line.to, head: line.to },\n            effects: crawlerLineEffect.of({ line: line.number - 1, position: 'end' }),\n          });\n        }\n\n        continue;\n      }\n    }\n  });\n});\n\n/**\n * Handles effect that updates widget state.\n */\nconst widgetUpdatePlugin = ViewPlugin.fromClass(\n  class {\n    update(update: ViewUpdate) {\n      const widgetStateMap = update.state.field(widgetStateMapStateField);\n      const { decorations, notifier } = update.state.field(widgetDecorationsField);\n\n      // Prune widgets orphaned by a rebuild (position-keyed ids change when an edit shifts a node).\n      if (update.docChanged) {\n        const liveIds = new Set<string>();\n        for (const range of decorationSetToArray(decorations)) {\n          const widget = range.value?.spec?.widget;\n          if (widget instanceof StubWidget) {\n            liveIds.add(widget.id);\n          }\n        }\n        notifier.reconcile(liveIds);\n      }\n\n      // Re-render widgets whose state changed. Widgets not yet mounted pick their state up at mount\n      // time instead (see `withCurrentWidgetState`).\n      for (const effect of update.transactions.flatMap((tr) => tr.effects)) {\n        if (effect.is(widgetUpdateEffect)) {\n          notifier.updated(effect.value.id, widgetStateMap[effect.value.id]);\n        }\n      }\n    }\n  },\n);\n\n/**\n * The shared machinery every matcher extension includes; one instance, so CodeMirror deduplicates it\n * however many matchers a host registers.\n */\nexport const widgetsCore: Extension = [\n  widgetContextStateField,\n  widgetStateMapStateField,\n  widgetDecorationsField,\n  widgetUpdatePlugin,\n  parseProgressPlugin,\n  navigationEffectPlugin,\n  keyHandlers,\n  scrollJumpMonitor,\n];\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, Facet } from '@codemirror/state';\nimport { Decoration, type EditorView } from '@codemirror/view';\n\nimport {\n  type WidgetDef,\n  type WidgetMatcher,\n  type WidgetProps,\n  createWidget,\n  getWidgetState,\n  widgetMatchersFacet,\n  widgetRebuildEffect,\n  widgetsCore,\n  widgetUpdateEffect,\n} from './widgets.ts';\n\n/** Decides whether a link's URL is one of this matcher's. */\nexport type LinkMatch = (url: string) => boolean;\n\n/**\n * Every registered link matcher, so the rest of the editor can tell a link a widget will replace\n * from one it should render itself: `decorateMarkdown` leaves the former alone.\n */\nexport const linkMatchFacet = Facet.define<LinkMatch, readonly LinkMatch[]>({\n  combine: (matchers) => matchers,\n});\n\n/** Whether some registered link widget claims the URL. */\nexport const isWidgetLink = (state: EditorState, url: string): boolean =>\n  state.facet(linkMatchFacet).some((match) => match(url));\n\n/** Matches URLs by scheme prefix: `matchSchemes(['dxn:', 'echo:'])`. */\nexport const matchSchemes =\n  (schemes: string[]): LinkMatch =>\n  (url) =>\n    schemes.some((scheme) => url.startsWith(scheme));\n\n/** Matches http(s) URLs by host, subdomains included: `matchHosts(['github.com'])`. */\nexport const matchHosts =\n  (hosts: string[]): LinkMatch =>\n  (url) => {\n    let parsed: URL;\n    try {\n      parsed = new URL(url);\n    } catch {\n      return false;\n    }\n    // A host names a web resource: `ftp://github.com/…` is not the widget's.\n    if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n      return false;\n    }\n    const { hostname } = parsed;\n    return hosts.some((host) => hostname === host || hostname.endsWith(`.${host}`));\n  };\n\n/** Matches URLs against a pattern: `matchPattern(/github\\.com\\/[^/]+\\/[^/]+\\/pull\\/\\d+/)`. */\nexport const matchPattern =\n  (pattern: RegExp): LinkMatch =>\n  (url) => {\n    // A sticky or global pattern carries `lastIndex` between calls, alternating its answer.\n    pattern.lastIndex = 0;\n    return pattern.test(url);\n  };\n\n/** What a link widget can report back about its target (see {@link setLinkWidgetState}). */\nexport type LinkWidgetState = {\n  /**\n   * The target cannot be resolved (deleted, or never reachable): the link is rendered as its\n   * source text, editable, with the widget inline after it instead of replacing it.\n   */\n  unresolved?: boolean;\n  /** The widget sizes itself; no reserved height is applied to the block. */\n  intrinsic?: boolean;\n};\n\n/** Props of a widget standing in for `[label](url)` or `![label](url)`. */\nexport type LinkWidgetProps<TContext = unknown> = WidgetProps<\n  LinkWidgetState & {\n    id: string;\n    label: string;\n    url: string;\n    block: boolean;\n    suggest: boolean;\n  },\n  TContext\n>;\n\n/**\n * Reports a link widget's target state: an `unresolved` block becomes an inline widget after\n * editable source, and `intrinsic` builds without a reserved height. With `rebuild` (the default)\n * the decorations are rebuilt so it takes effect now; without it the state applies at the next\n * rebuild, for a change the widget applies to its own element meanwhile (see\n * {@link releaseBlockHeight}) — a redraw under the user's pointer can swap the element they are\n * clicking. Idempotent: a report that changes nothing is skipped, so a widget may call it from a\n * render effect.\n */\nexport const setLinkWidgetState = (\n  view: EditorView,\n  id: string,\n  state: LinkWidgetState,\n  { rebuild = true }: { rebuild?: boolean } = {},\n): void => {\n  const current = getWidgetState(view.state, id) ?? {};\n  const keys = Object.keys(state) as (keyof LinkWidgetState)[];\n  if (keys.every((key) => (current[key] ?? false) === state[key])) {\n    return;\n  }\n  view.dispatch({\n    effects: [\n      widgetUpdateEffect.of({ id, value: (prev) => ({ ...prev, ...state }) }),\n      ...(rebuild ? [widgetRebuildEffect.of(null)] : []),\n    ],\n  });\n};\n\nexport type LinkWidgetsOptions<TProps extends LinkWidgetProps = LinkWidgetProps> = {\n  /** Which URLs are this matcher's. */\n  match: LinkMatch;\n  /** The inline widget for `[label](url)`. */\n  link?: WidgetDef<TProps>;\n  /** The block widget for `![label](url)`. */\n  image?: WidgetDef<TProps>;\n  /**\n   * The widget's props from the link's — how a matcher names what it parsed out of the URL for the\n   * widget it hands them to (`eid` for an object link). Identity when absent.\n   */\n  props?: (props: LinkWidgetProps) => TProps;\n};\n\n/**\n * Markdown links as widgets: a link whose URL the matcher accepts is replaced by the inline widget,\n * an image by the block one. Several `linkWidgets` may be registered; the first to accept a URL\n * renders it.\n */\nexport function linkWidgets(options: LinkWidgetsOptions): Extension;\nexport function linkWidgets<TProps extends LinkWidgetProps>(\n  options: LinkWidgetsOptions<TProps> & { props: (props: LinkWidgetProps) => TProps },\n): Extension;\nexport function linkWidgets({ match, link, image, props: toProps }: LinkWidgetsOptions): Extension {\n  const matcher: WidgetMatcher = {\n    // `URL` is the bare autolink a reader gets by pasting: GFM writes it as a `URL` node with no\n    // marks, and the same link typed as `[label](url)` arrives as a `Link` with a `URL` child. A\n    // matcher that took only the second form left a pasted link as plain text in the editor while\n    // the read-only renderer — which overrides every `a`, autolinks included — showed a chip.\n    nodes: ['Link', 'Image', 'URL'],\n    debug: link?.debug || image?.debug,\n    match: (node, { state, context, widgetStateMap, notifier, counters }) => {\n      const bare = node.type.name === 'URL';\n      // A `URL` inside a link is that link's own target, decorated by the `Link` case.\n      if (bare && (node.node.parent?.name === 'Link' || node.node.parent?.name === 'Image')) {\n        return undefined;\n      }\n\n      const urlNode = bare ? node.node : node.node.getChild('URL');\n      const markNodes = bare ? [] : node.node.getChildren('LinkMark');\n      if (!urlNode || (!bare && markNodes.length < 2)) {\n        return undefined;\n      }\n      const url = state.sliceDoc(urlNode.from, urlNode.to);\n      if (!match(url)) {\n        return undefined;\n      }\n      const isBlock = node.type.name === 'Image';\n      const def = isBlock ? image : link;\n      if (!def) {\n        return undefined;\n      }\n\n      // A bare URL is its own text; a widget that wants to say something shorter derives it from the\n      // URL, which is the only thing the reader wrote.\n      const label = bare ? url : state.sliceDoc(markNodes[0].to, markNodes[1].from);\n      if (!label) {\n        return undefined;\n      }\n      const range = { from: node.node.from, to: node.node.to };\n      // Ids count occurrences of a URL rather than positions: keyed on position, every keystroke above\n      // a widget would rename it and remount its portal; occurrence order is stable because edits above\n      // force a full rebuild.\n      const occurrence = counters.get(url) ?? 0;\n      counters.set(url, occurrence + 1);\n      const id = `cm-url-${url}-${occurrence}`;\n      const linkProps: LinkWidgetProps = {\n        id,\n        _tag: isBlock ? 'image' : 'link',\n        range,\n        context,\n        label,\n        url,\n        block: isBlock,\n        suggest: isBlock,\n        ...widgetStateMap[id],\n      };\n      const props = toProps ? toProps(linkProps) : linkProps;\n      const signature = state.sliceDoc(range.from, range.to);\n      // An unresolved target: the source stays, editable, and the widget follows it inline.\n      if (linkProps.unresolved) {\n        const widget = createWidget({\n          def: { ...def, block: false, estimatedHeight: undefined },\n          id,\n          props,\n          notifier,\n          signature,\n        });\n        if (!widget) {\n          return undefined;\n        }\n        return {\n          from: range.to,\n          to: range.to,\n          decoration: Decoration.widget({ widget, side: 1, tag: props._tag }),\n        };\n      }\n      const widget = createWidget({\n        def: { ...def, block: isBlock, estimatedHeight: linkProps.intrinsic ? undefined : def.estimatedHeight },\n        id,\n        props,\n        notifier,\n        signature,\n      });\n      if (!widget) {\n        return undefined;\n      }\n      return {\n        ...range,\n        decoration: Decoration.replace({ widget, block: isBlock, atomic: true, inclusive: true, tag: props._tag }),\n      };\n    },\n  };\n\n  return [widgetsCore, widgetMatchersFacet.of(matcher), linkMatchFacet.of(match)];\n}\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type ChangeSpec, Transaction } from '@codemirror/state';\nimport { type PluginValue, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\n// TODO(burdon): Tests.\n/**\n * ViewPlugin that augments input and paste: auto-links pasted URLs and fixes task markers.\n */\nexport const adjustChanges = () => {\n  return ViewPlugin.fromClass(\n    class implements PluginValue {\n      update(update: ViewUpdate) {\n        const tree = syntaxTree(update.state);\n        const adjustments: ChangeSpec[] = [];\n\n        for (const tr of update.transactions) {\n          const event = tr.annotation(Transaction.userEvent);\n          switch (event) {\n            //\n            // Enter\n            //\n            case 'input': {\n              const changes = tr.changes;\n              if (changes.empty) {\n                break;\n              }\n\n              changes.iterChanges((fromA) => {\n                const node = tree.resolveInner(fromA, 1);\n                if (node?.name === 'BulletList') {\n                  // Add space to previous line if an empty list item (otherwise it is not interpreted as a Task).\n                  const { text } = update.state.doc.lineAt(fromA);\n                  if (text.endsWith(']')) {\n                    adjustments.push({ from: fromA, to: fromA, insert: ' ' });\n                  }\n                }\n              });\n\n              break;\n            }\n\n            //\n            // Paste\n            //\n            case 'input.paste': {\n              const changes = tr.changes;\n              if (changes.empty) {\n                break;\n              }\n\n              changes.iterChanges((fromA, toA, fromB, toB, text) => {\n                // Check for URL.\n                const url = getValidUrl(update.view.state.sliceDoc(fromB, toB));\n                if (url) {\n                  // Check if pasting inside existing link.\n                  const node = tree.resolveInner(fromA, -1);\n                  const invalidPositions = new Set(['Code', 'CodeText', 'FencedCode', 'Link', 'LinkMark', 'URL']);\n                  if (!invalidPositions.has(node?.name)) {\n                    const replacedText = tr.startState.sliceDoc(fromA, toA);\n                    adjustments.push({ from: fromA, to: toB, insert: createLink(url, replacedText) });\n                  }\n                } else {\n                  const node = tree.resolveInner(fromA, 1);\n                  switch (node?.name) {\n                    case 'Task': {\n                      // Remove task marker if pasting into task list.\n                      const str = text.toString();\n                      const match = str.match(/\\s*- \\[[ xX]\\]\\s*(.+)/);\n                      if (match) {\n                        const [, replacement] = match;\n                        adjustments.push({ from: fromA, to: toB, insert: replacement });\n                      }\n                      break;\n                    }\n                  }\n                }\n              });\n\n              break;\n            }\n          }\n        }\n\n        // TODO(burdon): Is this the right way to augment changes? Alt: EditorState.transactionFilter\n        if (adjustments.length) {\n          setTimeout(() => {\n            update.view.dispatch(\n              update.view.state.update({\n                changes: adjustments,\n              }),\n            );\n          });\n        }\n      }\n    },\n  );\n};\n\n//\n// Links\n//\n\nexport const createLink = (url: URL, label: string): string => {\n  // Check if image.\n  // Example: https://media.dxos.network/dxos-logotype-blue.png\n  const { host, pathname } = url;\n  const [, extension] = pathname.split('.');\n  const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp'];\n  if (imageExtensions.includes(extension)) {\n    return `![${label || host}](${url})`;\n  }\n\n  if (!label) {\n    label = createLinkLabel(url);\n  }\n\n  return `[${label}](${url})`;\n};\n\nexport const createLinkLabel = (url: URL): string => {\n  let { protocol, host, pathname } = url;\n  if (protocol === 'http:' || protocol === 'https:') {\n    protocol = '';\n  }\n\n  // NOTE(Zan): Consult: https://github.com/dxos/dxos/issues/7331 before changing this.\n  // Remove 'www.' if at the beginning of the URL\n  host = host.replace(/^www\\./, '');\n\n  return [protocol, host].filter(Boolean).join('//') + (pathname !== '/' ? pathname : '');\n};\n\nconst getValidUrl = (str: string): URL | undefined => {\n  const validProtocols = ['http:', 'https:', 'mailto:', 'tel:'];\n  try {\n    const url = new URL(str);\n    if (!validProtocols.includes(url.protocol)) {\n      return undefined;\n    }\n\n    return url;\n  } catch (_err) {\n    return undefined;\n  }\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type EditorState, type Extension, type Range, StateField, type Transaction } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';\n\nimport { focusField } from '../../state/focus.ts';\nimport { isWidgetLink } from '../../widgets/link-widgets.ts';\n\nexport type ImageNodeData = { name: 'Image'; url: string };\n\nexport type ImageOptions = {\n  /**\n   * Predicate that returns true to suppress rendering of an image node.\n   * When skipped, the markdown link source is left visible to the user instead of being\n   * replaced by an `<img>` widget.\n   */\n  skip?: (node: ImageNodeData) => boolean;\n};\n\n/**\n * Create image decorations.\n */\nexport const image = (options: ImageOptions = {}): Extension => {\n  return [\n    StateField.define<DecorationSet>({\n      create: (state) => {\n        return Decoration.set(buildDecorations(state, 0, state.doc.length, options));\n      },\n      update: (value: DecorationSet, tr: Transaction) => {\n        let from = Number.POSITIVE_INFINITY;\n        let to = Number.NEGATIVE_INFINITY;\n        if (tr.docChanged || tr.selection) {\n          // The changed ranges and both cursor positions, since an image's source is shown only under the cursor.\n          const cursor = tr.state.selection.main.head;\n          const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);\n          from = Math.min(cursor, oldCursor);\n          to = Math.max(cursor, oldCursor);\n          tr.changes.iterChangedRanges((_fromA, _toA, fromB, toB) => {\n            from = Math.min(from, fromB);\n            to = Math.max(to, toB);\n          });\n        }\n\n        // The parser extends the tree in transactions of its own as the viewport reaches unparsed content, so images\n        // there are decorated as they are parsed rather than by rebuilding the whole document on every scroll.\n        const parsedBefore = tr.changes.mapPos(syntaxTree(tr.startState).length);\n        const parsedAfter = syntaxTree(tr.state).length;\n        if (parsedAfter > parsedBefore) {\n          from = Math.min(from, parsedBefore);\n          to = Math.max(to, parsedAfter);\n        }\n\n        if (from > to) {\n          return value.map(tr.changes);\n        }\n\n        // Expand to cover lines.\n        from = tr.state.doc.lineAt(from).from;\n        to = tr.state.doc.lineAt(to).to;\n\n        return value.map(tr.changes).update({\n          filterFrom: from,\n          filterTo: to,\n          filter: () => false,\n          add: buildDecorations(tr.state, from, to, options),\n        });\n      },\n      provide: (field) => EditorView.decorations.from(field),\n    }),\n  ];\n};\n\nconst buildDecorations = (state: EditorState, from: number, to: number, options: ImageOptions = {}) => {\n  const decorations: Range<Decoration>[] = [];\n  const cursor = state.selection.main.head;\n  syntaxTree(state).iterate({\n    enter: (node) => {\n      if (node.name === 'Image') {\n        const urlNode = node.node.getChild('URL');\n        if (urlNode) {\n          const hide = state.readOnly || cursor < node.from || cursor > node.to || !state.field(focusField);\n\n          const url = state.sliceDoc(urlNode.from, urlNode.to);\n          // Some plugins might be using custom URLs; avoid attempts to render those URLs.\n          if (url.match(/^https?:\\/\\//) === null && url.match(/^file?:\\/\\//) === null) {\n            return;\n          }\n\n          // A registered link widget's image is the widget's to render; otherwise the consumer's\n          // filter (e.g., disable remote-image rendering by setting).\n          if (isWidgetLink(state, url) || options.skip?.({ name: 'Image', url })) {\n            return;\n          }\n\n          preloadImage(url);\n          decorations.push(\n            Decoration.replace({\n              block: true, // Prevent cursor from entering.\n              widget: new ImageWidget(url),\n            }).range(hide ? node.from : node.to, node.to),\n          );\n        }\n      }\n    },\n    from,\n    to,\n  });\n\n  return decorations;\n};\n\nconst preloaded = new Set<string>();\n\nconst preloadImage = (url: string) => {\n  if (!preloaded.has(url)) {\n    const img = document.createElement('img');\n    img.src = url;\n    preloaded.add(url);\n  }\n};\n\nclass ImageWidget extends WidgetType {\n  constructor(readonly _url: string) {\n    super();\n  }\n\n  override eq(other: this) {\n    return this._url === other._url;\n  }\n\n  override toDOM(view: EditorView) {\n    const img = document.createElement('img');\n    img.setAttribute('src', this._url);\n    img.setAttribute('class', 'cm-image');\n    const focused = view.state.field(focusField);\n    // If focused, hide image until successfully loaded to avoid flickering effects.\n    if (focused) {\n      img.onload = () => {\n        img.classList.add('cm-loaded-image');\n        collapseIfTrackingPixel(img);\n      };\n    } else {\n      img.classList.add('cm-loaded-image');\n      img.onload = () => collapseIfTrackingPixel(img);\n    }\n    // Error (e.g., blocked tracker URL): also collapse so we don't leave a hole.\n    img.onerror = () => collapseLine(img);\n\n    return img;\n  }\n}\n\n/**\n * Tracking pixels are commonly 1×1 (or 0×0) transparent images embedded by mail senders.\n * They add no visual value, so hide them once their natural dimensions are known.\n */\nconst collapseIfTrackingPixel = (img: HTMLImageElement) => {\n  if (img.naturalWidth <= 1 && img.naturalHeight <= 1) {\n    collapseLine(img);\n  }\n};\n\nconst collapseLine = (img: HTMLImageElement) => {\n  img.style.display = 'none';\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { EditorView } from '@codemirror/view';\n\nimport { fontBody, fontMono } from '../../../styles/index.ts';\n\nexport const bulletListIndentationWidth = 24;\nexport const orderedListIndentationWidth = 36; // TODO(burdon): Make variable length based on number of digits.\n\n/**\n * EditorView theme with CSS for horizontal rules, lists, blockquotes, code, tasks, tables, and images.\n */\nexport const formattingStyles = EditorView.theme({\n  /**\n   * Horizontal rule.\n   */\n  '& .cm-hr': {\n    display: 'inline-block',\n    width: '100%',\n    height: '0',\n    verticalAlign: 'middle',\n    borderTop: '1px solid var(--color-cm-separator)',\n    opacity: 0.5,\n  },\n\n  /**\n   * Lists.\n   */\n  '& .cm-list-item': {},\n  '& .cm-list-mark': {\n    // Must be inline-block so the fixed width applies and the first-line text start matches the\n    // hanging indent (`text-indent: -width`) set on `.cm-list-item`.\n    display: 'inline-block',\n    textAlign: 'right',\n    paddingRight: '0.5em',\n    // Being inline-block makes this a block container, so it would otherwise inherit the row's\n    // hanging indent and lay its own glyph out against a shifted line box.\n    textIndent: 0,\n    fontVariant: 'tabular-nums',\n    // Anchor to the line top (not the baseline) so the inline-block marker — whose height already\n    // equals the line-height — fills the line box exactly instead of extending it and making a\n    // bulleted line taller than a plain one.\n    verticalAlign: 'top',\n    lineHeight: 'inherit',\n  },\n  '& .cm-list-mark-bullet': {\n    width: `${bulletListIndentationWidth}px`,\n    // Centered (not right-aligned like an ordered marker, whose digits align on the last column) so\n    // the glyph lands on the same axis as a task checkbox, which fills the same box from its start.\n    textAlign: 'center',\n    // The rendered bullet widget sits inside the `ListMark` font-mono highlight span; a direct\n    // font-family on the widget overrides that inherited monospace so the bullet uses the body font.\n    fontFamily: fontBody,\n  },\n  '& .cm-list-mark-ordered': {\n    width: `${orderedListIndentationWidth}px`,\n  },\n\n  /**\n   * Blockquote.\n   */\n  '& .cm-blockquote': {\n    background: 'var(--color-cm-codeblock)',\n    borderLeft: '2px solid var(--color-cm-separator)',\n    paddingLeft: '1rem',\n    margin: 0,\n  },\n\n  /**\n   * Code and codeblocks.\n   */\n  '& code': {\n    fontFamily: fontMono,\n    color: 'var(--color-cm-code)',\n    whiteSpace: 'nowrap',\n  },\n  '& .cm-code': {\n    fontFamily: fontMono,\n    color: 'var(--color-cm-code)',\n  },\n  // Inline code spans (triggered by backticks) use `cm-code-inline` + `font-mono`.\n  // Different monospace font metrics can slightly overflow the fixed CodeMirror line box,\n  // so constrain them to the target 24px height.\n  '& .cm-code-inline': {\n    fontFamily: fontMono,\n    // A step below the body text: a monospace face runs wider and taller than the prose at the same size.\n    fontSize: '0.9em',\n    height: '24px',\n    alignItems: 'center',\n    overflow: 'hidden',\n    whiteSpace: 'nowrap',\n    padding: '0 0.25rem',\n    color: 'var(--color-cm-code-inline)',\n    backgroundColor: 'var(--color-cm-codeblock)',\n  },\n  '& .cm-code-mark': {\n    fontFamily: fontMono,\n    height: '24px',\n    display: 'inline-flex',\n    alignItems: 'center',\n    overflow: 'hidden',\n  },\n  '& .cm-codeblock-line': {\n    background: 'var(--color-cm-codeblock)',\n    paddingLeft: '1rem !important',\n    paddingRight: '1.5rem !important',\n  },\n  '& .cm-codeblock-start': {\n    borderTopLeftRadius: '.25rem',\n    borderTopRightRadius: '.25rem',\n  },\n  '& .cm-codeblock-end': {\n    borderBottomLeftRadius: '.25rem',\n    borderBottomRightRadius: '.25rem',\n  },\n\n  /**\n   * Task list.\n   */\n  '& .cm-task': {\n    display: 'inline-flex',\n    width: `${bulletListIndentationWidth}px`,\n    height: '20px',\n  },\n  '& .cm-task-checkbox': {\n    display: 'grid',\n    margin: '0',\n    transform: 'translateY(2px)',\n  },\n\n  /**\n   * Table.\n   */\n  '.cm-table': {\n    borderCollapse: 'separate',\n    borderSpacing: '2px',\n  },\n  '.cm-table *': {\n    lineHeight: 1.5,\n  },\n  '.cm-table-editor *': {\n    fontFamily: fontMono,\n  },\n  '.cm-table-head': {\n    padding: '8px 8px',\n    paddingRight: '24px',\n    overflowWrap: 'break-word',\n    whiteSpace: 'pre-wrap',\n    wordBreak: 'keep-all',\n    textAlign: 'left',\n    textDecoration: 'none !important',\n    fontSize: 'small',\n    textTransform: 'uppercase',\n    color: 'var(--color-fg-muted)',\n    backgroundColor: 'var(--color-group-alt-surface)',\n  },\n  '.cm-table-cell': {\n    padding: '4px 8px',\n    overflowWrap: 'break-word',\n    whiteSpace: 'pre-wrap',\n    wordBreak: 'keep-all',\n    verticalAlign: 'top',\n    backgroundColor: 'var(--color-group-surface)',\n    fontVariantNumeric: 'tabular-nums',\n  },\n\n  /**\n   * Image.\n   */\n  '.cm-image': {\n    display: 'block',\n    height: '0',\n  },\n  '.cm-image.cm-loaded-image': {\n    height: 'auto',\n    borderTop: '0.5rem solid transparent',\n    borderBottom: '0.5rem solid transparent',\n  },\n  '.cm-image-with-loader': {\n    display: 'block',\n    opacity: 0,\n    transitionDuration: '350ms',\n    transitionProperty: 'opacity',\n  },\n  '.cm-image-with-loader.cm-loaded-image': {\n    opacity: 1,\n  },\n  '.cm-image-wrapper': {\n    'grid-template-columns': '1fr',\n    'display': 'grid',\n    'margin': '0.5rem 0',\n    'overflow': 'hidden',\n    'transitionDuration': '350ms',\n    'transitionProperty': 'height',\n    '& > *': {\n      'grid-row-start': 1,\n      'grid-column-start': 1,\n    },\n  },\n});\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport {\n  type EditorState,\n  type Extension,\n  type RangeSet,\n  RangeSetBuilder,\n  StateField,\n  type Transaction,\n} from '@codemirror/state';\nimport { Decoration, EditorView, WidgetType } from '@codemirror/view';\n\n// TODO(burdon): Snippet to create basic table.\n//  https://codemirror.net/docs/ref/#autocomplete.snippet\n// TODO(burdon): Advanced formatting (left/right/center).\n// TODO(burdon): Editor to auto balance columns.\n\nexport type TableOptions = {};\n\n/**\n * GFM tables.\n * https://github.github.com/gfm/#tables-extension\n */\nexport const table = (options: TableOptions = {}): Extension => {\n  return StateField.define<RangeSet<Decoration>>({\n    create: (state) => update(state, options),\n    update: (_: RangeSet<Decoration>, tr: Transaction) => update(tr.state, options),\n    provide: (field) => EditorView.decorations.from(field),\n  });\n};\n\ntype Table = {\n  from: number;\n  to: number;\n  header?: string[];\n  rows?: string[][];\n};\n\nconst update = (state: EditorState, _options: TableOptions) => {\n  const builder = new RangeSetBuilder<Decoration>();\n  const cursor = state.selection.main.head;\n\n  const tables: Table[] = [];\n  const getTable = () => tables[tables.length - 1];\n\n  // Parse table. Cells are split from each row's text rather than read from `TableCell` nodes, which the GFM parser\n  // omits for empty cells (so an empty header or value would shift the columns after it).\n  syntaxTree(state).iterate({\n    enter: (node) => {\n      switch (node.name) {\n        case 'Table': {\n          tables.push({ from: node.from, to: node.to });\n          break;\n        }\n        case 'TableHeader': {\n          getTable().header = splitRow(state.sliceDoc(node.from, node.to));\n          return false;\n        }\n        case 'TableRow': {\n          (getTable().rows ??= []).push(splitRow(state.sliceDoc(node.from, node.to)));\n          return false;\n        }\n      }\n    },\n  });\n\n  // Every row has the table's full column count, so a short row still lines up.\n  tables.forEach((table) => {\n    const columns = Math.max(table.header?.length ?? 0, ...(table.rows ?? []).map((row) => row.length));\n    const pad = (row: string[]) => [...row, ...Array<string>(Math.max(0, columns - row.length)).fill('')];\n    table.header = table.header && pad(table.header);\n    table.rows = table.rows?.map(pad);\n  });\n\n  tables.forEach((table) => {\n    const replace = state.readOnly || cursor < table.from || cursor > table.to;\n    if (replace) {\n      // TODO(burdon): Compute left/right alignment based on column syntax.\n      builder.add(\n        table.from,\n        table.to,\n        Decoration.replace({\n          block: true,\n          widget: new TableWidget(table),\n        }),\n      );\n    } else {\n      builder.add(\n        table.from,\n        table.to,\n        Decoration.mark({\n          class: 'cm-table-editor',\n        }),\n      );\n    }\n  });\n\n  return builder.finish();\n};\n\n/** A row's cells: the text between unescaped pipes, without the optional leading and trailing pipe. */\nconst splitRow = (text: string): string[] => {\n  const trimmed = text\n    .trim()\n    .replace(/^\\|/, '')\n    .replace(/(?<!\\\\)\\|$/, '');\n  return trimmed.split(/(?<!\\\\)\\|/).map((cell) => cell.trim().replace(/\\\\\\|/g, '|'));\n};\n\n/** Renders cell text into el, processing inline markdown (bold, italic, code). */\nconst renderCellContent = (el: HTMLElement, text: string): void => {\n  const parts = text.split(/(`[^`\\n]+`|\\*\\*[^*\\n]+\\*\\*|__[^_\\n]+__|\\*[^*\\n]+\\*|_[^_\\n]+_)/);\n  for (const part of parts) {\n    if (part.length > 2 && part.startsWith('`') && part.endsWith('`')) {\n      const code = document.createElement('code');\n      code.textContent = part.slice(1, -1);\n      el.appendChild(code);\n    } else if ((part.startsWith('**') && part.endsWith('**')) || (part.startsWith('__') && part.endsWith('__'))) {\n      const strong = document.createElement('strong');\n      strong.textContent = part.slice(2, -2);\n      el.appendChild(strong);\n    } else if ((part.startsWith('*') && part.endsWith('*')) || (part.startsWith('_') && part.endsWith('_'))) {\n      const em = document.createElement('em');\n      em.textContent = part.slice(1, -1);\n      el.appendChild(em);\n    } else {\n      el.appendChild(document.createTextNode(part));\n    }\n  }\n};\n\nclass TableWidget extends WidgetType {\n  constructor(readonly _table: Table) {\n    super();\n  }\n\n  override eq(other: this) {\n    return (\n      JSON.stringify(this._table.header) === JSON.stringify(other._table.header) &&\n      JSON.stringify(this._table.rows) === JSON.stringify(other._table.rows)\n    );\n  }\n\n  override ignoreEvent(e: Event): boolean {\n    return !/^mouse/.test(e.type);\n  }\n\n  override toDOM(_view: EditorView) {\n    const div = document.createElement('div');\n    const table = div.appendChild(document.createElement('table'));\n    table.setAttribute('class', 'cm-table');\n\n    const header = table.appendChild(document.createElement('thead'));\n    const tr = header.appendChild(document.createElement('tr'));\n    this._table.header?.forEach((cell) => {\n      const th = document.createElement('th');\n      th.setAttribute('class', 'cm-table-head');\n      renderCellContent(tr.appendChild(th), cell);\n    });\n\n    const body = table.appendChild(document.createElement('tbody'));\n    this._table.rows?.forEach((row) => {\n      const tr = body.appendChild(document.createElement('tr'));\n      row.forEach((cell) => {\n        const td = document.createElement('td');\n        td.setAttribute('class', 'cm-table-cell');\n        renderCellContent(tr.appendChild(td), cell);\n      });\n    });\n\n    return div;\n  }\n}\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type EditorState, Prec, RangeSetBuilder, StateEffect } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';\nimport { type SyntaxNodeRef } from '@lezer/common';\n\nimport { invariant } from '@dxos/invariant';\n\nimport { type HeadingLevel, markdownTheme } from '../../../styles/index.ts';\nimport { type RenderCallback } from '../../../types/index.ts';\nimport { wrapWithCatch } from '../../../util/index.ts';\nimport { isWidgetLink } from '../../widgets/link-widgets.ts';\nimport { adjustChanges } from './changes.ts';\nimport { image } from './image.ts';\nimport { bulletListIndentationWidth, formattingStyles, orderedListIndentationWidth } from './styles.ts';\nimport { table } from './table.ts';\n\n/** The anchor every link form renders as, so a bare address is styled like a bracketed one. */\nconst linkMark = (url: string, withButton = false) =>\n  Decoration.mark({\n    tagName: 'a',\n    attributes: {\n      class: withButton ? 'cm-link cm-link-with-button' : 'cm-link',\n      href: url,\n      rel: 'noreferrer',\n      target: '_blank',\n      // The document is reached with the caret, not with Tab: an anchor left tabbable inside the\n      // editing host takes the stop that leaves the editor, so a linked field becomes a trap.\n      tabindex: '-1',\n    },\n  });\n\nexport type NodeData =\n  | {\n      name: 'Link';\n      url: string;\n    }\n  | {\n      name: 'Image';\n      url: string;\n    };\n\nexport interface DecorateOptions {\n  /**\n   * Prevents triggering decorations as the cursor moves through the document.\n   */\n  selectionChangeDelay?: number;\n  numberedHeadings?: { from: number; to?: number };\n  // TODO(burdon): Additional padding for each line.\n  listPaddingLeft?: number;\n  /**\n   * Leaves a link or image undecorated. A link some registered link widget claims (`objectLinks`,\n   * a plugin's matcher) is always left to the widget, without being named here.\n   */\n  skip?: (node: NodeData) => boolean;\n  // TODO(burdon): Remove.\n  renderLinkButton?: RenderCallback<{ url: string }>;\n}\n\n/**\n * WYSIWYG markdown decorator (headings, lists, tasks, quotes, code, links, horizontal rules);\n * bundles image, table, adjustChanges, and formattingStyles.\n */\nexport const decorateMarkdown = (options: DecorateOptions = {}) => {\n  return [\n    ViewPlugin.fromClass(\n      class {\n        deco: DecorationSet;\n        atomicDeco: DecorationSet;\n        pendingUpdate?: NodeJS.Timeout;\n\n        constructor(view: EditorView) {\n          ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));\n        }\n\n        update(update: ViewUpdate) {\n          if (\n            update.docChanged ||\n            update.viewportChanged ||\n            update.focusChanged ||\n            update.transactions.some((tr) => tr.effects.some((effect) => effect.is(forceUpdate))) ||\n            (update.selectionSet && !options.selectionChangeDelay)\n          ) {\n            ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(\n              update.view,\n              options,\n              update.view.hasFocus,\n            ));\n\n            this.clearUpdate();\n          } else if (update.selectionSet) {\n            this.scheduleUpdate(update.view);\n          }\n        }\n\n        // Defer update in case moving through the document.\n        scheduleUpdate(view: EditorView) {\n          this.clearUpdate();\n          this.pendingUpdate = setTimeout(() => {\n            view.dispatch({ effects: forceUpdate.of(null) });\n          }, options.selectionChangeDelay);\n        }\n\n        clearUpdate() {\n          if (this.pendingUpdate) {\n            clearTimeout(this.pendingUpdate);\n            this.pendingUpdate = undefined;\n          }\n        }\n\n        destroy() {\n          this.clearUpdate();\n        }\n      },\n      {\n        provide: (plugin) => [\n          Prec.low(EditorView.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration.none)),\n          EditorView.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration.none),\n          EditorView.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration.none),\n        ],\n      },\n    ),\n    image({ skip: options.skip ? (node) => !!options.skip?.(node) : undefined }),\n    table(),\n    adjustChanges(),\n    formattingStyles,\n  ];\n};\n\n/**\n * Unicode characters.\n * NOTE: Depends on font.\n * https://www.compart.com/en/unicode (nice resource).\n * https://en.wikipedia.org/wiki/List_of_Unicode_characters\n */\nconst Unicode = {\n  emDash: '\\u2014',\n  bullet: '\\u2022',\n  bulletSmall: '\\u2219',\n  bulletSquare: '\\u2b1d',\n};\n\n//\n// Widgets\n//\n\nclass HorizontalRuleWidget extends WidgetType {\n  override toDOM(): HTMLSpanElement {\n    const el = document.createElement('span');\n    el.className = 'cm-hr';\n    return el;\n  }\n}\n\nclass LinkButton extends WidgetType {\n  constructor(\n    private readonly url: string,\n    private readonly render: RenderCallback<{ url: string }>,\n  ) {\n    super();\n  }\n\n  override eq(other: this) {\n    return this.url === other.url;\n  }\n\n  override toDOM(view: EditorView) {\n    const el = document.createElement('span');\n    this.render(el, { url: this.url }, view);\n    return el;\n  }\n}\n\nclass CheckboxWidget extends WidgetType {\n  constructor(private _checked: boolean) {\n    super();\n  }\n\n  override eq(other: this) {\n    return this._checked === other._checked;\n  }\n\n  override ignoreEvent() {\n    return false;\n  }\n\n  override toDOM(view: EditorView) {\n    const input = document.createElement('input');\n    input.className = 'cm-task-checkbox dx-checkbox-native';\n    input.type = 'checkbox';\n    input.tabIndex = -1;\n    input.checked = this._checked;\n    if (view.state.readOnly) {\n      input.setAttribute('disabled', 'true');\n    } else {\n      input.onmousedown = (event: Event) => {\n        // Could be beginning of line.\n        const line = view.state.doc.lineAt(view.posAtDOM(span));\n        const text = view.state.sliceDoc(line.from, line.to);\n        const match = text.match(/^\\s*- (\\[[xX ]]).*/);\n        if (match) {\n          const [, checked] = match;\n          const pos = line.from + text.indexOf(checked);\n          this._checked = checked !== '[ ]';\n          view.dispatch({\n            changes: { from: pos + 1, to: pos + 2, insert: this._checked ? ' ' : 'x' },\n          });\n\n          event.preventDefault();\n        }\n      };\n    }\n\n    const span = document.createElement('span');\n    span.className = 'cm-task';\n    span.appendChild(input);\n    return span;\n  }\n}\n\nclass TextWidget extends WidgetType {\n  constructor(\n    private readonly text: string,\n    private readonly className?: string,\n  ) {\n    super();\n  }\n\n  override toDOM(): HTMLSpanElement {\n    const el = document.createElement('span');\n    if (this.className) {\n      el.className = this.className;\n    }\n    el.innerText = this.text;\n    return el;\n  }\n}\n\nconst hide = Decoration.replace({});\nconst blockQuote = Decoration.line({ class: 'cm-blockquote' });\nconst fencedCodeLine = Decoration.line({ class: 'cm-code cm-codeblock-line' });\nconst fencedCodeLineFirst = Decoration.line({ class: 'cm-code cm-codeblock-line cm-codeblock-start' });\nconst fencedCodeLineLast = Decoration.line({ class: 'cm-code cm-codeblock-line cm-codeblock-end' });\nconst commentBlockLine = fencedCodeLine;\nconst commentBlockLineFirst = fencedCodeLineFirst;\nconst commentBlockLineLast = fencedCodeLineLast;\nconst horizontalRule = Decoration.replace({ widget: new HorizontalRuleWidget() });\nconst checkedTask = Decoration.replace({ widget: new CheckboxWidget(true) });\nconst uncheckedTask = Decoration.replace({ widget: new CheckboxWidget(false) });\n\n/**\n * Checks if cursor is inside text.\n */\n/** GFM autolinks match schemeless hosts and bare email addresses, which are not usable as `href`. */\nconst normalizeUrl = (text: string): string | undefined => {\n  if (/^[a-z][a-z0-9+.-]*:/i.test(text)) {\n    return text;\n  }\n  if (/^www\\./i.test(text)) {\n    return `https://${text}`;\n  }\n  if (text.includes('@')) {\n    return `mailto:${text}`;\n  }\n\n  return undefined;\n};\n\nconst editingRange = (state: EditorState, range: { from: number; to: number }, focus: boolean) => {\n  const {\n    readOnly,\n    selection: {\n      main: { head },\n    },\n  } = state;\n  return focus && !readOnly && head >= range.from && head <= range.to;\n};\n\nconst autoHideTags = new Set([\n  'CodeMark',\n  'CodeInfo',\n  'EmphasisMark',\n  'StrikethroughMark',\n  'SubscriptMark',\n  'SuperscriptMark',\n]);\n\n/**\n * Markdown list level.\n */\ntype NumberingLevel = { type: string; from: number; to: number; level: number; number: number };\n\nconst buildDecorations = (view: EditorView, options: DecorateOptions, focus: boolean) => {\n  const decoRanges: { from: number; to: number; deco: Decoration }[] = [];\n  const atomicDecoRanges: { from: number; to: number; deco: Decoration }[] = [];\n  const { state } = view;\n\n  // Header numbering.\n  // TODO(burdon): Pre-parse headers to allow virtualization.\n  const headerLevels: (NumberingLevel | null)[] = [];\n  const getHeaderLevels = (node: SyntaxNodeRef, level: number): (NumberingLevel | null)[] => {\n    invariant(level > 0);\n    if (level > headerLevels.length) {\n      const len = headerLevels.length;\n      headerLevels.length = level;\n      headerLevels.fill(null, len);\n      headerLevels[level - 1] = { type: node.name, from: node.from, to: node.to, level, number: 0 };\n    } else {\n      headerLevels.splice(level);\n    }\n\n    return headerLevels.slice(0, level);\n  };\n\n  // List numbering and indentation.\n  const listLevels: NumberingLevel[] = [];\n  const enterList = (node: SyntaxNodeRef) => {\n    listLevels.push({ type: node.name, from: node.from, to: node.to, level: listLevels.length, number: 0 });\n  };\n  const leaveList = () => {\n    listLevels.pop();\n  };\n  const getCurrentListLevel = (): NumberingLevel => {\n    invariant(listLevels.length);\n    return listLevels[listLevels.length - 1];\n  };\n\n  // let count = 0;\n  const enterNode = (node: SyntaxNodeRef) => {\n    // console.log(`[${count++}]`, { node: node.name, from: node.from, to: node.to });\n    switch (node.name) {\n      // ATXHeading > HeaderMark > Paragraph\n      // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be\n      // processed and the numbering will be incorrect.\n      case 'ATXHeading1':\n      case 'ATXHeading2':\n      case 'ATXHeading3':\n      case 'ATXHeading4':\n      case 'ATXHeading5':\n      case 'ATXHeading6': {\n        const level = parseInt(node.name['ATXHeading'.length]) as HeadingLevel;\n        const headers = getHeaderLevels(node, level);\n        if (options.numberedHeadings?.from !== undefined) {\n          const header = headers[level - 1];\n          // TODO(burdon): Header will be missing if headers are out of order (e.g., ## header then # header).\n          if (header) {\n            header.number++;\n          }\n        }\n\n        const editing = editingRange(state, node, focus);\n        if (editing) {\n          break;\n        }\n\n        const mark = node.node.firstChild!;\n        if (mark?.name === 'HeaderMark') {\n          const { from, to = 6 } = options.numberedHeadings ?? {};\n          const text = state.sliceDoc(node.from, node.to);\n          const len = text.match(/[#\\s]+/)![0].length;\n          if (!from || level < from || level > to) {\n            atomicDecoRanges.push({ from: mark.from, to: mark.from + len, deco: hide });\n          } else {\n            // TODO(burdon): Number format/style.\n            const num =\n              headers\n                .slice(from - 1)\n                .map((level) => level?.number ?? 0)\n                .join('.') + '). ';\n\n            if (num.length) {\n              atomicDecoRanges.push({\n                from: mark.from,\n                to: mark.from + len,\n                deco: Decoration.replace({\n                  widget: new TextWidget(num, markdownTheme.heading(level).className),\n                }),\n              });\n            }\n          }\n        }\n\n        return false;\n      }\n\n      //\n      // Lists.\n      // [BulletList | OrderedList] > (ListItem > ListMark) > (Task > TaskMarker)?\n      //\n\n      case 'BulletList':\n      case 'OrderedList': {\n        enterList(node);\n        break;\n      }\n\n      case 'ListItem': {\n        const line = state.doc.lineAt(node.from);\n\n        // Set indentation.\n        const list = getCurrentListLevel();\n        const width = list.type === 'OrderedList' ? orderedListIndentationWidth : bulletListIndentationWidth;\n        const offset = (options?.listPaddingLeft ?? 0) + ((list.level ?? 0) + 1) * width;\n        if (node.from === line.to - 1) {\n          // Abort if only the hyphen is typed.\n          return false;\n        }\n\n        // Add line decoration for the continuation indent.\n        // TODO(burdon): Bug if indentation is more than one indentation unit (e.g., 4 spaces) from the previous line.\n        decoRanges.push({\n          from: line.from,\n          to: line.from,\n          deco: Decoration.line({\n            class: 'cm-list-item',\n            attributes: {\n              style: `padding-left: ${offset}px; text-indent: -${width}px;`,\n            },\n          }),\n        });\n\n        break;\n      }\n\n      case 'ListMark': {\n        const list = getCurrentListLevel();\n\n        // Look-ahead for task marker.\n        // NOTE: Requires space to exist (otherwise the text is parsed as the start of a link).\n        const next = tree.resolve(node.to + 1, 1);\n        if (next?.name === 'TaskMarker') {\n          break;\n        }\n\n        // TODO(burdon): Option to make hierarchical; or a), i), etc.\n        const label = list.type === 'OrderedList' ? `${++list.number}.` : Unicode.bulletSmall;\n        const line = state.doc.lineAt(node.from);\n        const to = state.doc.sliceString(node.to, node.to + 1) === ' ' ? node.to + 1 : node.to;\n        atomicDecoRanges.push({\n          from: line.from,\n          to,\n          deco: Decoration.replace({\n            widget: new TextWidget(\n              label,\n              list.type === 'OrderedList' ? 'cm-list-mark cm-list-mark-ordered' : 'cm-list-mark cm-list-mark-bullet',\n            ),\n          }),\n        });\n        break;\n      }\n\n      case 'TaskMarker': {\n        const checked = state.doc.sliceString(node.from + 1, node.to - 1) === 'x';\n        // Check if the next character is a space and if so, include it in the replacement.\n        const line = state.doc.lineAt(node.from);\n        const to = state.doc.sliceString(node.to, node.to + 1) === ' ' ? node.to + 1 : node.to;\n        atomicDecoRanges.push({ from: line.from, to, deco: checked ? checkedTask : uncheckedTask });\n        break;\n      }\n\n      //\n      // Blockquote > QuoteMark > Paragraph\n      //\n\n      case 'Blockquote': {\n        // Only a single leading '>' denotes a quote — runs of '>' (nested-quote syntax, and\n        // notably '>>>>>>>' merge-conflict markers) are left undecorated.\n        const startLine = state.doc.lineAt(node.from);\n        if (/^\\s{0,3}>{2,}/.test(startLine.text)) {\n          break;\n        }\n\n        const editing = editingRange(state, node, focus);\n        const quoteMark = node.node.getChild('QuoteMark');\n        const paragraph = node.node.getChild('Paragraph');\n        if (!editing && quoteMark && paragraph) {\n          atomicDecoRanges.push({ from: quoteMark.from, to: paragraph.from, deco: hide });\n        }\n\n        for (const block of view.viewportLineBlocks) {\n          if (block.to < node.from) {\n            continue;\n          }\n          if (block.from > node.to) {\n            break;\n          }\n\n          decoRanges.push({ from: block.from, to: block.from, deco: blockQuote });\n        }\n\n        break;\n      }\n\n      //\n      // CommentBlock\n      //\n\n      case 'CommentBlock': {\n        const editing = editingRange(state, node, focus);\n        for (const block of view.viewportLineBlocks) {\n          if (block.to < node.from) {\n            continue;\n          }\n          if (block.from > node.to) {\n            break;\n          }\n\n          const isFirst = block.from <= node.from;\n          const isLast = block.to >= node.to && /^(\\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));\n\n          decoRanges.push({\n            from: block.from,\n            to: block.from,\n            deco: isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine,\n          });\n\n          if (!editing && (isFirst || isLast)) {\n            atomicDecoRanges.push({ from: block.from, to: block.to, deco: hide });\n          }\n        }\n        break;\n      }\n\n      //\n      // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark\n      //\n\n      case 'FencedCode': {\n        for (const block of view.viewportLineBlocks) {\n          if (block.to < node.from) {\n            continue;\n          }\n          if (block.from > node.to) {\n            break;\n          }\n\n          const first = block.from <= node.from;\n          const last = block.to >= node.to && /```$/.test(state.doc.sliceString(block.from, block.to));\n          decoRanges.push({\n            from: block.from,\n            to: block.from,\n            deco: first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine,\n          });\n\n          const editing = editingRange(state, node, focus);\n          if (!editing && (first || last)) {\n            atomicDecoRanges.push({ from: block.from, to: block.to, deco: hide });\n          }\n        }\n        return false;\n      }\n\n      //\n      // Link > [LinkMark, URL]\n      //\n\n      case 'Link': {\n        const marks = node.node.getChildren('LinkMark');\n        const urlNode = node.node.getChild('URL');\n        const editing = editingRange(state, node, focus);\n        if (urlNode && marks.length >= 2) {\n          const url = state.sliceDoc(urlNode.from, urlNode.to);\n          if (isWidgetLink(state, url) || options.skip?.({ name: 'Link', url })) {\n            break;\n          }\n          if (!editing) {\n            atomicDecoRanges.push({ from: node.from, to: marks[0].to, deco: hide });\n          }\n\n          decoRanges.push({\n            from: marks[0].to,\n            to: !editing && options.renderLinkButton ? node.to : marks[1].from,\n            deco: linkMark(url, !!options.renderLinkButton),\n          });\n\n          if (!editing) {\n            atomicDecoRanges.push({\n              from: marks[1].from,\n              to: node.to,\n              deco: options.renderLinkButton\n                ? Decoration.replace({\n                    widget: new LinkButton(url, options.renderLinkButton),\n                  })\n                : hide,\n            });\n          }\n        }\n        break;\n      }\n\n      //\n      // Bare URLs (GFM autolink) and `<url>` autolinks.\n      // Autolink > [LinkMark, URL, LinkMark] | URL\n      //\n\n      case 'URL': {\n        // The `[label](url)` form is decorated by the `Link` case above, which owns the whole node.\n        const parent = node.node.parent;\n        if (parent?.name === 'Link' || parent?.name === 'Image') {\n          break;\n        }\n\n        // Not gated on a link widget's claim: widgets replace `[label](url)` and image nodes, never a\n        // bare URL, which would otherwise render as neither a link nor a chip.\n        const text = state.sliceDoc(node.from, node.to);\n        const url = normalizeUrl(text);\n        if (!url || options.skip?.({ name: 'Link', url })) {\n          break;\n        }\n\n        decoRanges.push({\n          from: node.from,\n          to: node.to,\n          deco: Decoration.mark({\n            tagName: 'a',\n            attributes: {\n              class: 'cm-link',\n              href: url,\n              rel: 'noreferrer',\n              target: '_blank',\n              // See `linkMark`: a tabbable anchor inside the editing host traps Tab.\n              tabindex: '-1',\n            },\n          }),\n        });\n\n        // The angle brackets of `<url>` are markup, so they are hidden unless the cursor is inside.\n        if (parent?.name === 'Autolink' && !editingRange(state, parent, focus)) {\n          for (const mark of parent.getChildren('LinkMark')) {\n            atomicDecoRanges.push({ from: mark.from, to: mark.to, deco: hide });\n          }\n        }\n        break;\n      }\n\n      //\n      // HR\n      //\n\n      case 'HorizontalRule': {\n        if (!editingRange(state, node, focus)) {\n          decoRanges.push({ from: node.from, to: node.to, deco: horizontalRule });\n        }\n        break;\n      }\n\n      default: {\n        if (autoHideTags.has(node.name)) {\n          if (!editingRange(state, node.node.parent!, focus)) {\n            atomicDecoRanges.push({ from: node.from, to: node.to, deco: hide });\n          }\n        }\n      }\n    }\n  };\n\n  const leaveNode = (node: SyntaxNodeRef) => {\n    switch (node.name) {\n      case 'BulletList':\n      case 'OrderedList': {\n        leaveList();\n        break;\n      }\n    }\n  };\n\n  const tree = syntaxTree(state);\n  if (options.numberedHeadings?.from === undefined) {\n    for (const { from, to } of view.visibleRanges) {\n      tree.iterate({\n        from,\n        to,\n        enter: wrapWithCatch(enterNode, 'decorate.enter'),\n        leave: wrapWithCatch(leaveNode, 'decorate.leave'),\n      });\n    }\n  } else {\n    // NOTE: If line numbering then we must iterate from the start of document.\n    tree.iterate({\n      enter: wrapWithCatch(enterNode, 'decorate.enter'),\n      leave: wrapWithCatch(leaveNode, 'decorate.leave'),\n    });\n  }\n\n  // Sort and build decoration sets.\n  const sortRanges = (a: { from: number; to: number }, b: { from: number; to: number }) =>\n    a.from - b.from || a.to - b.to;\n\n  decoRanges.sort(sortRanges);\n  atomicDecoRanges.sort(sortRanges);\n\n  const deco = new RangeSetBuilder<Decoration>();\n  for (const { from, to, deco: d } of decoRanges) {\n    deco.add(from, to, d);\n  }\n\n  const atomicDeco = new RangeSetBuilder<Decoration>();\n  for (const { from, to, deco } of atomicDecoRanges) {\n    // Skip replace decorations that span line breaks (not allowed by CodeMirror plugins).\n    if (from < to && state.doc.lineAt(from).number !== state.doc.lineAt(to).number) {\n      continue;\n    }\n    atomicDeco.add(from, to, deco);\n  }\n\n  return {\n    deco: deco.finish(),\n    atomicDeco: atomicDeco.finish(),\n  };\n};\n\nconst forceUpdate = StateEffect.define<null>();\n","//\n// Copyright 2023 DXOS.org\n// Copyright CodeMirror\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type ChangeSpec } from '@codemirror/state';\nimport { type EditorView, hoverTooltip } from '@codemirror/view';\nimport { type SyntaxNode } from '@lezer/common';\n\nimport { mx, surfaceShadow } from '@dxos/ui-theme';\n\nimport { type RenderCallback } from '../../../types/index.ts';\n\nexport type LinkTooltipProps = {\n  render: RenderCallback<{ url: string }>;\n};\n\n// Internal object links render their own inline/block preview, so a raw-URI hover tooltip is noise.\nconst INTERNAL_SCHEMES = ['dxn:', 'echo:'];\n\n/**\n * Hover tooltip showing a rendered preview for markdown links (skips internal object URIs).\n */\nexport const linkTooltip = ({ render }: LinkTooltipProps) => {\n  return hoverTooltip((view, pos, side) => {\n    const syntax = syntaxTree(view.state).resolveInner(pos, side);\n    let link = null;\n    for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {\n      link = node.name === 'Link' ? node : null;\n    }\n\n    const url = link && link.getChild('URL');\n    if (!url || !link) {\n      return null;\n    }\n\n    const urlText = view.state.sliceDoc(url.from, url.to);\n    if (INTERNAL_SCHEMES.some((scheme) => urlText.startsWith(scheme))) {\n      return null;\n    }\n\n    return {\n      pos: link.from,\n      end: link.to,\n      above: true,\n      create: () => {\n        const el = document.createElement('div');\n        el.className = tooltipClassName;\n        render(el, { url: urlText }, view);\n        return { dom: el, offset: { x: 0, y: 4 } };\n      },\n    };\n  });\n};\n\n/** Brackets and newlines would terminate the inline link, so they are folded into the label text. */\nexport const escapeLinkLabel = (label: string): string => label.replace(/[[\\]]/g, '').replace(/\\s+/g, ' ').trim();\n\n/**\n * Rewrites link labels that have drifted from their target's current label.\n * The document holds the label so that it stays searchable and readable as plain markdown; this\n * reconciles it whenever the caller can resolve a newer one.\n *\n * @param resolve Returns the target's label, or `undefined` when it is unknown (left untouched).\n * @returns true if the document was changed.\n */\nexport const syncLinkLabels = (view: EditorView, resolve: (url: string) => string | undefined): boolean => {\n  const { state } = view;\n  const changes: ChangeSpec[] = [];\n  syntaxTree(state).iterate({\n    enter: (node) => {\n      if (node.name !== 'Link') {\n        return;\n      }\n\n      const marks = node.node.getChildren('LinkMark');\n      const urlNode = node.node.getChild('URL');\n      if (!urlNode || marks.length < 2) {\n        return;\n      }\n\n      const label = resolve(state.sliceDoc(urlNode.from, urlNode.to));\n      if (label === undefined) {\n        return;\n      }\n\n      const [from, to] = [marks[0].to, marks[1].from];\n      const escaped = escapeLinkLabel(label);\n      // An empty label leaves `[](url)`, which no longer parses as a Link — the node would then be\n      // invisible to this pass, so a later rename could never repair it.\n      if (escaped.length > 0 && state.sliceDoc(from, to) !== escaped) {\n        changes.push({ from, to, insert: escaped });\n      }\n    },\n  });\n\n  if (changes.length === 0) {\n    return false;\n  }\n\n  view.dispatch({ changes, userEvent: 'sync.link' });\n  return true;\n};\n\nconst tooltipClassName = mx(\n  'inline-flex items-center p-1 max-w-64 text-sm bg-inverse-surface text-inverse-fg rounded-sm',\n  surfaceShadow({ elevation: 'positioned' }),\n);\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView, keymap } from '@codemirror/view';\n\nexport type Substitution = Record<string, string>;\n\n/**\n * CodeMirror key binding → inserted text, e.g. `{ 'Alt--': '—' }`.\n * https://codemirror.net/docs/ref/#view.KeyBinding\n */\nexport type KeyBindings = Record<string, string>;\n\nexport type SubstitutionOptions = {\n  substitutions?: Substitution;\n  bindings?: KeyBindings;\n};\n\n/**\n * Substitutes typed sequences and CodeMirror key chords (e.g. `Alt--` → em dash).\n */\nexport const substitutions = ({\n  substitutions = markdownSubstitutions,\n  bindings = markdownBindings,\n}: SubstitutionOptions = {}): Extension => {\n  // Longest first so overlapping patterns match the full sequence.\n  const sorted = Object.entries(substitutions).sort(([a], [b]) => b.length - a.length);\n\n  return [\n    EditorView.inputHandler.of((view, from, _to, insert) => {\n      if (insert.length !== 1) {\n        return false;\n      }\n\n      const { state } = view;\n      const lineStart = state.doc.lineAt(from).from;\n      const textWithInsert = state.doc.sliceString(lineStart, from) + insert;\n\n      for (const [input, output] of sorted) {\n        if (!textWithInsert.endsWith(input)) {\n          continue;\n        }\n\n        const rangeFrom = from - input.length + 1;\n        if (rangeFrom < lineStart) {\n          continue;\n        }\n\n        view.dispatch(\n          state.update({\n            changes: { from: rangeFrom, to: from, insert: output },\n            selection: { anchor: rangeFrom + output.length },\n          }),\n        );\n        return true;\n      }\n\n      return false;\n    }),\n    keymap.of(\n      Object.entries(bindings).map(([key, output]) => ({\n        key,\n        preventDefault: true,\n        run: (view) => {\n          view.dispatch(view.state.replaceSelection(output));\n          return true;\n        },\n      })),\n    ),\n  ];\n};\n\n/**\n * Default typographic sequence substitutions.\n */\nexport const markdownSubstitutions: Substitution = {\n  '...': '…',\n  '->': '→',\n  '<-': '←',\n  '=>': '⇒',\n  '<=>': '⇔',\n  '<=': '≤',\n  '>=': '≥',\n  '+-': '±',\n  '!=': '≠',\n  '(c)': '©',\n  'BTC': '₿',\n  'EUR': '€',\n  'GBP': '£',\n  'JPY': '¥',\n};\n\n/**\n * Default key-chord substitutions.\n */\nexport const markdownBindings: KeyBindings = {\n  'Alt--': '—',\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { LanguageSupport, StreamLanguage, type StringStream } from '@codemirror/language';\nimport { type Highlighter, type Tag, highlightCode, tags } from '@lezer/highlight';\n\n/** Token names emitted by the tokenizer, mapped to standard tags so the editor's highlight style colours them. */\nconst tokenTable = {\n  comment: tags.lineComment,\n  // `@prefix`, `@base`, `PREFIX`, `BASE`, `GRAPH` and `a` (`rdf:type`).\n  keyword: tags.keyword,\n  iri: tags.url,\n  // The `sx` of `sx:quote`; the local name after the colon is the `name`.\n  prefix: tags.namespace,\n  name: tags.propertyName,\n  blank: tags.special(tags.variableName),\n  string: tags.string,\n  language: tags.annotation,\n  datatype: tags.typeOperator,\n  number: tags.number,\n  bool: tags.bool,\n  terminator: tags.punctuation,\n  separator: tags.separator,\n  bracket: tags.squareBracket,\n  paren: tags.paren,\n  // TriG's graph blocks.\n  brace: tags.brace,\n  invalid: tags.invalid,\n} satisfies Record<string, Tag>;\n\nexport type TurtleToken = keyof typeof tokenTable;\n\ntype TurtleState = {\n  /** The delimiter of a long string (`\"\"\"` or `'''`) that spans lines. */\n  long?: string;\n  /** The last token was a prefix, so a local name follows directly, even one a number would otherwise claim. */\n  local: boolean;\n};\n\nconst PUNCTUATION: Record<string, TurtleToken | undefined> = {\n  '.': 'terminator',\n  ';': 'separator',\n  ',': 'separator',\n  '[': 'bracket',\n  ']': 'bracket',\n  '(': 'paren',\n  ')': 'paren',\n  '{': 'brace',\n  '}': 'brace',\n};\n\nconst KEYWORDS = /^(?:PREFIX|BASE|GRAPH)\\b/i;\n// PN_PREFIX ':' PN_LOCAL, loosely: a local name may hold dots and dashes but does not end in a dot.\nconst PREFIXED_NAME = /^([A-Za-z][\\w.-]*)?:/;\nconst LOCAL_NAME = /^(?:[\\w\\-%]|\\.(?=[\\w\\-%]))+/;\n\nconst skipLongString = (stream: StringStream, state: TurtleState): TurtleToken => {\n  const delimiter = state.long ?? '\"\"\"';\n  if (stream.skipTo(delimiter)) {\n    stream.pos += delimiter.length;\n    state.long = undefined;\n  } else {\n    stream.skipToEnd();\n  }\n  return 'string';\n};\n\nconst token = (stream: StringStream, state: TurtleState): TurtleToken | null => {\n  if (state.long) {\n    return skipLongString(stream, state);\n  }\n  if (state.local) {\n    state.local = false;\n    if (stream.match(LOCAL_NAME)) {\n      return 'name';\n    }\n  }\n  if (stream.eatSpace()) {\n    return null;\n  }\n  if (stream.match('#')) {\n    stream.skipToEnd();\n    return 'comment';\n  }\n\n  const long = stream.match('\"\"\"') ? '\"\"\"' : stream.match(\"'''\") ? \"'''\" : undefined;\n  if (long) {\n    state.long = long;\n    return skipLongString(stream, state);\n  }\n  const quote = stream.peek();\n  if (quote === '\"' || quote === \"'\") {\n    stream.next();\n    let escaped = false;\n    let char: string | void;\n    while ((char = stream.next()) != null) {\n      if (char === quote && !escaped) {\n        return 'string';\n      }\n      escaped = !escaped && char === '\\\\';\n    }\n    return 'invalid';\n  }\n\n  if (stream.match(/^<[^<>\"{}|^`\\\\\\s]*>/)) {\n    return 'iri';\n  }\n  if (stream.match(/^@(?:prefix|base)\\b/)) {\n    return 'keyword';\n  }\n  // A language tag follows its string directly (`\"Founder\"@en`).\n  if (stream.match(/^@[A-Za-z]+(?:-[A-Za-z0-9]+)*/)) {\n    return 'language';\n  }\n  if (stream.match('^^')) {\n    return 'datatype';\n  }\n  if (stream.match(/^_:[\\w.-]*\\w/)) {\n    return 'blank';\n  }\n  if (stream.match(/^[+-]?(?:\\d+\\.?\\d*(?:[eE][+-]?\\d+)?|\\.\\d+(?:[eE][+-]?\\d+)?)/)) {\n    return 'number';\n  }\n  if (stream.match(/^(?:true|false)\\b/)) {\n    return 'bool';\n  }\n  if (stream.match(KEYWORDS)) {\n    return 'keyword';\n  }\n  if (stream.match(/^a(?=[\\s<[(_\"'])/)) {\n    return 'keyword';\n  }\n  if (stream.match(PREFIXED_NAME)) {\n    // The prefix and its colon; the local name, if any, is the next token.\n    state.local = true;\n    return 'prefix';\n  }\n  if (stream.match(LOCAL_NAME)) {\n    return 'name';\n  }\n\n  const char = stream.next();\n  return (char ? PUNCTUATION[char] : undefined) ?? 'invalid';\n};\n\n/** RDF 1.1 Turtle, and TriG's `{ … }` graph blocks, which Turtle's tokens cover otherwise. */\nexport const turtleLanguage = StreamLanguage.define<TurtleState>({\n  name: 'turtle',\n  startState: () => ({ local: false }),\n  copyState: (state) => ({ ...state }),\n  token,\n  tokenTable,\n  languageData: {\n    commentTokens: { line: '#' },\n    closeBrackets: { brackets: ['(', '[', '{', '\"', \"'\", '<'] },\n  },\n});\n\n/** Turtle (and TriG) language support; pair with the theme's syntax highlighting to colour it. */\nexport const turtle = (): LanguageSupport => new LanguageSupport(turtleLanguage);\n\n/** A run of source text and the classes the highlighter gave it (empty when unstyled). */\nexport type HighlightedSpan = { text: string; classes: string };\n\n/**\n * Highlights Turtle or TriG without an editor, one array of spans per line, for read-only snippets where mounting\n * a CodeMirror view per snippet would cost too much.\n */\nexport const highlightTurtle = (\n  source: string,\n  highlighter: Highlighter | readonly Highlighter[],\n): HighlightedSpan[][] => {\n  const lines: HighlightedSpan[][] = [[]];\n  highlightCode(\n    source,\n    turtleLanguage.parser.parse(source),\n    highlighter,\n    (text, classes) => lines[lines.length - 1].push({ text, classes }),\n    () => lines.push([]),\n  );\n  return lines;\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { xmlLanguage } from '@codemirror/lang-xml';\nimport { type Extension } from '@codemirror/state';\nimport { type ParseWrapper, parseMixed } from '@lezer/common';\nimport { type BlockParser } from '@lezer/markdown';\n\nimport { escapeRegExpSource } from '../../../util/index.ts';\nimport { createMarkdownExtensions } from '../markdown/index.ts';\nimport { type XmlWidgetRegistry } from './xml-tags.ts';\n\nexport type ExtendedMarkdownOptions = {\n  registry?: XmlWidgetRegistry;\n};\n\n/**\n * Extended markdown parser with mixed parser for custom rendering of XML tags.\n */\nexport const extendedMarkdown = ({ registry }: ExtendedMarkdownOptions = {}): Extension => {\n  return [\n    createMarkdownExtensions({\n      extensions: [\n        {\n          wrap: mixedParser(registry),\n          parseBlock: [\n            // Disable SetextHeading since it causes flickering when parsing/rendering tasks in chunks.\n            {\n              name: 'SetextHeading',\n              parse: () => false,\n            },\n            // Custom XML block parser that keeps registered tags as a single HTMLBlock\n            // even when their content contains blank lines.\n            ...xmlBlockParsers(registry),\n          ],\n        },\n      ],\n    }),\n  ];\n};\n\n/**\n * Creates block parsers for registered XML tags that may contain blank lines.\n *\n * By default, the markdown parser treats custom HTML tags as type 6 HTML blocks,\n * which end at blank lines. This causes tags like `<reasoning>...\\n\\n...</reasoning>`\n * to be split into separate blocks, preventing the XML mixed parser from seeing the\n * full element. This custom parser consumes all lines (including blanks) until the\n * matching closing tag, emitting a single HTMLBlock.\n */\nconst xmlBlockParsers = (registry?: XmlWidgetRegistry): BlockParser[] => {\n  const customTags = Object.keys(registry ?? {});\n  if (customTags.length === 0) {\n    return [];\n  }\n\n  const tagPattern = customTags.map(escapeRegExpSource).join('|');\n  const selfClosePattern = new RegExp(`^\\\\s*<(${tagPattern})(\\\\s[^>]*)?\\\\/>\\\\s*$`);\n  const openPattern = new RegExp(`^\\\\s*<(${tagPattern})(\\\\s[^>]*)?\\\\/?>`);\n\n  return [\n    {\n      name: 'XMLBlock',\n      before: 'HTMLBlock',\n      parse: (cx, line) => {\n        const match = openPattern.exec(line.text);\n        if (!match) {\n          return false;\n        }\n\n        // Self-closing tag (e.g., `<tag />` or `<tag id=\"x\" />`).\n        if (selfClosePattern.test(line.text)) {\n          const end = cx.lineStart + line.text.length;\n          cx.addElement(cx.elt('HTMLBlock', cx.lineStart, end));\n          cx.nextLine();\n          return true;\n        }\n\n        // Self-closing tag with trailing text (e.g., `<tag /> text`).\n        // Let markdown parse it as a normal paragraph.\n        if (match[0].trimEnd().endsWith('/>')) {\n          return false;\n        }\n\n        const tagName = match[1];\n        const closeTag = `</${tagName}>`;\n        const start = cx.lineStart;\n\n        // Check if closing tag is on the same line.\n        if (line.text.includes(closeTag)) {\n          cx.addElement(cx.elt('HTMLBlock', start, start + line.text.length));\n          cx.nextLine();\n          return true;\n        }\n\n        // Consume lines (including blank lines) until the closing tag.\n        let end = cx.lineStart + line.text.length;\n        while (cx.nextLine()) {\n          end = cx.lineStart + line.text.length;\n          if (line.text.includes(closeTag)) {\n            cx.addElement(cx.elt('HTMLBlock', start, end));\n            cx.nextLine();\n            return true;\n          }\n        }\n\n        // Unclosed tag (e.g., still streaming) — emit what we have.\n        cx.addElement(cx.elt('HTMLBlock', start, end));\n        return true;\n      },\n    },\n  ];\n};\n\n/**\n * Configure mixed parser to recognize custom tags.\n */\nconst mixedParser = (registry?: XmlWidgetRegistry): ParseWrapper => {\n  const customTags = Object.keys(registry ?? {});\n  const tagPattern = new RegExp(`<(${customTags.map(escapeRegExpSource).join('|')})`);\n\n  return parseMixed((node, input) => {\n    switch (node.name) {\n      // Ignore XML inside of fenced and inline code.\n      case 'FencedCode':\n      case 'InlineCode': {\n        return null;\n      }\n\n      // Parse multi-line HTML blocks.\n      // case 'XMLBlock':\n      case 'HTMLBlock': {\n        return {\n          parser: xmlLanguage.parser,\n        };\n      }\n\n      // Parse paragraphs that contain custom XML tags.\n      // TODO(burdon): Entire paragraph should be parsed as XML.\n      case 'Paragraph': {\n        const content = input.read(node.from, node.to);\n        if (tagPattern.test(content)) {\n          return {\n            parser: xmlLanguage.parser,\n          };\n        }\n\n        return null;\n      }\n    }\n\n    return null;\n  });\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Extension, type Range } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nimport { escapeRegExpSource } from '../../../util/index.ts';\n\nexport type XmlBlockDecorationOptions = {\n  /**\n   * Tag name to match (e.g. `'prompt'`).\n   */\n  tag: string;\n\n  /**\n   * Class added via `Decoration.line` to each line that intersects the element's content\n   * range. Use to style the bubble container (e.g. flex alignment, vertical margin).\n   */\n  lineClass?: string;\n\n  /**\n   * Additional class added only to the first line of the content range. Use to round the top\n   * of a block bubble and pad its top edge (single line receives both first and last classes).\n   */\n  firstLineClass?: string;\n\n  /**\n   * Additional class added only to the last line of the content range. Use to round the\n   * bottom of a block bubble and pad its bottom edge.\n   */\n  lastLineClass?: string;\n\n  /**\n   * Class added via `Decoration.mark` covering the inner content (between the open and\n   * close tags). Use to style the bubble surface (background, padding, rounding).\n   */\n  contentClass?: string;\n\n  /**\n   * When true, the open and close tag delimiters are hidden via `Decoration.replace`\n   * with no widget, so the rendered text is the inner content only.\n   */\n  hideTags?: boolean;\n};\n\n/**\n * Scans the doc for `<tag>…</tag>` blocks and decorates them without replacing them with a\n * widget — the source text remains in the document and can still be matched by other extensions\n * (e.g. `xmlFormatting`). Use this for \"bubble\"-style styling of XML blocks (chat prompts,\n * callouts, etc.) where the inner content should stay editable/copyable rather than living inside\n * a CodeMirror widget.\n *\n * Matching is done by direct text scan rather than a full-document XML parse: the transcript is\n * mostly markdown, and a single stray `<` anywhere upstream (code, `a < b`, an unclosed streaming\n * widget tag) corrupts a whole-document XML parse — which previously caused a well-formed prompt's\n * `</tag>` to go unrecognised, extending the bubble over the rest of the conversation. An\n * unterminated open tag (no matching `</tag>`, e.g. mid-stream) is left undecorated rather than\n * painting to the end of the document.\n */\n// TODO(burdon): Change to array to support multiple tags.\nexport const xmlBlockDecoration = ({\n  tag,\n  lineClass,\n  firstLineClass,\n  lastLineClass,\n  contentClass,\n  hideTags,\n}: XmlBlockDecorationOptions): Extension => {\n  const contentDecoration = contentClass ? Decoration.mark({ class: contentClass }) : undefined;\n  const hideDecoration = hideTags ? Decoration.replace({}) : undefined;\n\n  // Match an opening `<tag>` or `<tag …attrs>` (not self-closing); the paired `</tag>` is located\n  // by scanning forward so unrelated markup between blocks cannot break matching. `tag` is escaped\n  // so a name containing regex metacharacters (e.g. `foo.bar`) matches literally.\n  const escapedTag = escapeRegExpSource(tag);\n  const openTagRegExp = new RegExp(`<${escapedTag}(?:\\\\s[^>]*)?>`, 'g');\n  const closeTagStr = `</${tag}>`;\n\n  const buildDecorations = (view: EditorView): DecorationSet => {\n    const docLength = view.state.doc.length;\n    const text = view.state.sliceDoc(0, docLength);\n    if (!text.includes(`<${tag}`)) {\n      return Decoration.none;\n    }\n\n    const ranges: Range<Decoration>[] = [];\n    openTagRegExp.lastIndex = 0;\n    let match: RegExpExecArray | null;\n    while ((match = openTagRegExp.exec(text))) {\n      const openFrom = match.index;\n      const openTo = openFrom + match[0].length;\n      const closeFrom = text.indexOf(closeTagStr, openTo);\n      // Skip unterminated blocks: extending decoration to the document end would paint the bubble\n      // over everything that follows (e.g. the assistant response streaming in after the prompt).\n      if (closeFrom === -1) {\n        break;\n      }\n      const closeTo = closeFrom + closeTagStr.length;\n      const contentFrom = openTo;\n      const contentTo = closeFrom;\n\n      if (hideDecoration) {\n        ranges.push(hideDecoration.range(openFrom, openTo));\n        ranges.push(hideDecoration.range(closeFrom, closeTo));\n      }\n\n      if (contentDecoration && contentFrom < contentTo) {\n        ranges.push(contentDecoration.range(contentFrom, contentTo));\n      }\n\n      if (lineClass || firstLineClass || lastLineClass) {\n        // Collect every line that intersects the content range so that the first and last\n        // can be styled distinctly (e.g. rounding the top/bottom of a single block bubble).\n        const lines = [];\n        let pos = contentFrom;\n        while (pos < contentTo && pos <= docLength) {\n          const line = view.state.doc.lineAt(pos);\n          // Only lines that intersect visible content: a hidden close tag on its own line would\n          // otherwise get an empty rounded bubble line after the tag is replaced.\n          if (line.to > contentFrom && line.from < contentTo) {\n            lines.push(line);\n          }\n          if (line.to >= contentTo) {\n            break;\n          }\n          pos = line.to + 1;\n        }\n\n        lines.forEach((line, index) => {\n          const cls = [lineClass, index === 0 && firstLineClass, index === lines.length - 1 && lastLineClass]\n            .filter(Boolean)\n            .join(' ');\n          if (cls) {\n            ranges.push(Decoration.line({ class: cls }).range(line.from));\n          }\n        });\n      }\n\n      // Resume scanning past the close tag so content is never re-matched.\n      openTagRegExp.lastIndex = closeTo;\n    }\n\n    return Decoration.set(ranges, true);\n  };\n\n  return ViewPlugin.fromClass(\n    class {\n      decorations: DecorationSet;\n\n      constructor(view: EditorView) {\n        this.decorations = buildDecorations(view);\n      }\n\n      update(update: ViewUpdate) {\n        if (update.docChanged) {\n          this.decorations = buildDecorations(update.view);\n        }\n      }\n    },\n    {\n      decorations: (instance) => instance.decorations,\n    },\n  );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { xmlLanguage } from '@codemirror/lang-xml';\nimport { type Extension, type Range } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nexport type XmlFormattingOptions = {\n  /**\n   * Tag names whose elements should NOT receive xmlFormatting decorations. Use to\n   * opt-out tags rendered by another extension (e.g. `xmlBlockDecoration` for `<prompt>`)\n   * so the two don't double-wrap the same content with stacked styling.\n   *\n   * Skipping is recursive: descendants of a skipped element are also untouched, so a\n   * `<foo>` inside a skipped `<prompt>` still appears literally without xmlFormatting\n   * styling.\n   */\n  skip?: string[];\n};\n\n/**\n * Mark decoration extension that highlights XML tag delimiters\n * (e.g., `<tag>`, `</tag>`, `<tag attr=\"x\"/>`) with the `cm-xml-tag` class.\n *\n * Uses `@codemirror/lang-xml`'s Lezer parser standalone — only to compute\n * decoration ranges — without changing the editor's primary language. This\n * keeps the document plain text while still handling nesting and attributes\n * correctly.\n */\nexport const xmlFormatting = ({ skip }: XmlFormattingOptions = {}): Extension => {\n  const skipSet = skip && skip.length > 0 ? new Set(skip) : undefined;\n\n  const buildDecorations = (view: EditorView): DecorationSet => {\n    const text = view.state.sliceDoc(0, view.state.doc.length);\n    if (!text.includes('<')) {\n      return Decoration.none;\n    }\n\n    const tagNameAt = (node: { from: number; to: number }) => text.slice(node.from, node.to);\n    // A tag is only decorated when its `TagName` child parses as a real name. This gates out the\n    // malformed elements Lezer synthesises from a stray `<` in prose, which would otherwise span\n    // to the next `>` and paint the intervening text with `cm-xml-element`/`cm-xml-tag`.\n    const tagNameOf = (tagNode: { getChild: (name: string) => { from: number; to: number } | null }) => {\n      const tagNameNode = tagNode.getChild('TagName');\n      if (!tagNameNode) {\n        return undefined;\n      }\n      const value = tagNameAt(tagNameNode);\n      return VALID_TAG_NAME.test(value) ? value : undefined;\n    };\n\n    const tree = xmlLanguage.parser.parse(text);\n    const ranges: Range<Decoration>[] = [];\n    tree.iterate({\n      enter: (node) => {\n        const name = node.type.name;\n        if (name === 'SelfClosingTag' && node.from < node.to) {\n          const tagName = tagNameOf(node.node);\n          if (!tagName) {\n            return;\n          }\n          if (skipSet?.has(tagName)) {\n            return false;\n          }\n          // Self-closing tag is its own outer block and tag delimiter.\n          ranges.push(xmlElementMark.range(node.from, node.to));\n          ranges.push(xmlTagMark.range(node.from, node.to));\n          return;\n        }\n        if (name === 'Element' && node.from < node.to) {\n          const openTag = node.node.getChild('OpenTag');\n          // Require a real matching `CloseTag` (not `MismatchedCloseTag`) with a valid name: an\n          // unterminated or malformed element is left undecorated. Keep descending so any\n          // well-formed nested elements still get styled.\n          const closeTag = node.node.getChild('CloseTag');\n          const tagName = openTag ? tagNameOf(openTag) : undefined;\n          if (!openTag || !closeTag || !tagName) {\n            return;\n          }\n          if (skipSet?.has(tagName)) {\n            // Skip this element AND its descendants — another extension owns rendering.\n            return false;\n          }\n          ranges.push(xmlElementMark.range(node.from, node.to));\n          ranges.push(xmlTagMark.range(openTag.from, openTag.to));\n          ranges.push(xmlTagMark.range(closeTag.from, closeTag.to));\n          if (openTag.to < closeTag.from) {\n            ranges.push(xmlContentMark.range(openTag.to, closeTag.from));\n          }\n        }\n      },\n    });\n    return Decoration.set(ranges, true);\n  };\n\n  return [\n    ViewPlugin.fromClass(\n      class {\n        decorations: DecorationSet;\n\n        constructor(view: EditorView) {\n          this.decorations = buildDecorations(view);\n        }\n\n        update(update: ViewUpdate) {\n          if (update.docChanged) {\n            this.decorations = buildDecorations(update.view);\n          }\n        }\n      },\n      {\n        decorations: (instance) => instance.decorations,\n      },\n    ),\n\n    styles,\n  ];\n};\n\n/**\n * Well-formed XML tag name (a stray `<` in prose — e.g. `a < b` — has none, so it stays undecorated).\n */\nconst VALID_TAG_NAME = /^[A-Za-z_][\\w.-]*$/;\n\nconst xmlElementMark = Decoration.mark({ class: 'cm-xml-element' });\nconst xmlTagMark = Decoration.mark({ class: 'cm-xml-tag' });\nconst xmlContentMark = Decoration.mark({ class: 'cm-xml-content' });\n\nconst styles = EditorView.baseTheme({\n  '.cm-xml-element': {\n    backgroundColor: 'var(--color-current-surface)',\n    borderRadius: '0.25rem',\n    padding: '0.25rem',\n  },\n  '.cm-xml-tag': {\n    color: 'var(--color-blue-500)',\n    fontFamily: 'var(--font-mono)',\n  },\n  '.cm-xml-content': {},\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { EditorView, WidgetType } from '@codemirror/view';\n\nimport { getSize } from '@dxos/ui-theme';\n\n/**\n * Inline widget for echo/eid links (e.g., `[Label](echo:///123)`).\n * The <dx-anchor> tag is a web component that renders a link chip and popover.\n */\nexport class AnchorWidget extends WidgetType {\n  constructor(\n    readonly _label: string,\n    readonly _dxn: string,\n    /** Overrides the element's default (`hover`) preview trigger. */\n    readonly _trigger?: 'hover' | 'click',\n    /** Resolves a display label asynchronously (e.g. the object's name for a bare `#` link). */\n    readonly _resolveLabel?: () => Promise<string | undefined>,\n    /** Leading icon, e.g. `{ icon: 'ph--git-pull-request--regular', classNames: 'text-green-500' }`. */\n    readonly _icon?: { icon: string; classNames?: string },\n  ) {\n    super();\n  }\n\n  override eq(other: this) {\n    // Resolver presence participates: when the database arrives after the first build, the rebuilt\n    // widget gains a resolver, and an id-only match would keep the old DOM with the bare label.\n    return (\n      this._dxn === other._dxn &&\n      this._label === other._label &&\n      this._trigger === other._trigger &&\n      !!this._resolveLabel === !!other._resolveLabel &&\n      this._icon?.icon === other._icon?.icon &&\n      this._icon?.classNames === other._icon?.classNames\n    );\n  }\n\n  override toDOM(_view: EditorView) {\n    const root = document.createElement('dx-anchor');\n    root.classList.add('dx-tag-anchor');\n    root.setAttribute('eid', this._dxn);\n    if (this._trigger) {\n      root.setAttribute('trigger', this._trigger);\n    }\n    if (this._icon) {\n      // An icon element carries no text, so the anchor's `textContent` stays the label it reports.\n      const icon = root.appendChild(document.createElement('dx-icon'));\n      icon.setAttribute('icon', this._icon.icon);\n      icon.className = ['inline-block align-[-0.125em] me-1', getSize(4), this._icon.classNames]\n        .filter(Boolean)\n        .join(' ');\n    }\n    // The label lives in its own node so a resolved label replaces it without removing the icon.\n    const label = root.appendChild(document.createTextNode(this._label));\n    if (this._resolveLabel) {\n      void this._resolveLabel().then((resolved) => {\n        // The widget may have been culled/replaced; only retouch a live element.\n        if (resolved && root.isConnected) {\n          label.textContent = resolved;\n        }\n      });\n    }\n    return root;\n  }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\n\nimport { AnchorWidget } from './anchor.ts';\nimport { type LinkWidgetProps, linkWidgets, matchSchemes } from './link-widgets.ts';\nimport { type WidgetDef } from './widgets.ts';\n\n/** URL schemes that name an ECHO object. */\nexport const OBJECT_URL_SCHEMES = ['eid:', 'echo:'];\n\n/** A link widget's props, with the URL under the name the object machinery uses. */\nexport type ObjectLinkProps<TContext = unknown> = LinkWidgetProps<TContext> & { eid: string };\n\nexport type ObjectLinksOptions = {\n  /** URL scheme prefixes that make a link an object link. */\n  schemes?: string[];\n  /** Overrides the default chip's preview trigger. */\n  trigger?: 'hover' | 'click';\n  /** The inline widget for `[label](eid:…)`; the anchor chip by default. */\n  link?: WidgetDef<ObjectLinkProps>;\n  /** The block widget for `![label](eid:…)`; none by default. */\n  image?: WidgetDef<ObjectLinkProps>;\n};\n\n/**\n * Object links as widgets: an inline chip for `[label](eid:…)` (which carries the preview popover)\n * and, when the host provides one, a block for `![label](eid:…)`.\n */\nexport const objectLinks = ({\n  schemes = OBJECT_URL_SCHEMES,\n  trigger,\n  link = {\n    factory: ({ label, eid }) => new AnchorWidget(label, eid, trigger),\n  },\n  image,\n}: ObjectLinksOptions = {}): Extension =>\n  linkWidgets<ObjectLinkProps>({\n    match: matchSchemes(schemes),\n    link,\n    image,\n    // The URL under the name the object-side code reads.\n    props: (props) => ({ ...props, eid: props.url }),\n  });\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type EditorState } from '@codemirror/state';\nimport { type SyntaxNode } from '@lezer/common';\n\nimport { invariant } from '@dxos/invariant';\n\nexport type Tag = Record<string, any> & {\n  _tag: string;\n};\n\n/**\n * Parse XML Element.\n */\nexport const nodeToJson = (state: EditorState, node: SyntaxNode): Tag | undefined => {\n  invariant(node.type.name === 'Element', 'Node is not an Element');\n\n  // Find the opening tag.\n  const openTag = node.node.getChild('OpenTag') || node.node.getChild('SelfClosingTag');\n  if (openTag) {\n    // Extract tag name.\n    const tagName = openTag.getChild('TagName');\n    if (!tagName) {\n      return;\n    }\n\n    const tag: Tag = {\n      _tag: state.doc.sliceString(tagName.from, tagName.to),\n    };\n\n    // Extract attributes.\n    let attributeNode = openTag.getChild('Attribute');\n    while (attributeNode) {\n      const attrName = attributeNode.getChild('AttributeName');\n      if (attrName) {\n        const attr = state.doc.sliceString(attrName.from, attrName.to);\n\n        // Default for attributes without values.\n        let value: any = undefined;\n        const attrValue = attributeNode.getChild('AttributeValue');\n        if (attrValue) {\n          const rawValue = state.doc.sliceString(attrValue.from, attrValue.to);\n          if (\n            (rawValue.startsWith('\"') && rawValue.endsWith('\"')) ||\n            (rawValue.startsWith(\"'\") && rawValue.endsWith(\"'\"))\n          ) {\n            // Remove quotes if present.\n            value = rawValue.slice(1, -1);\n          } else {\n            value = rawValue;\n          }\n        }\n\n        tag[attr] = value;\n      }\n\n      // Get next sibling attribute.\n      attributeNode = attributeNode.nextSibling;\n    }\n\n    // Extract children for non-self-closing tags.\n    if (node.type.name === 'Element' && openTag.type.name !== 'SelfClosingTag') {\n      const children: any[] = [];\n      let child = node.node.firstChild;\n\n      const appendText = (raw: string) => {\n        if (raw.length === 0) {\n          return;\n        }\n        const last = children[children.length - 1];\n        if (typeof last === 'string') {\n          children[children.length - 1] = last + raw;\n        } else {\n          children.push(raw);\n        }\n      };\n\n      while (child) {\n        // Skip the opening and closing tags.\n        if (child.type.name !== 'OpenTag' && child.type.name !== 'CloseTag') {\n          if (child.type.name === 'Text') {\n            appendText(state.doc.sliceString(child.from, child.to));\n          } else if (child.type.name === 'EntityReference' || child.type.name === 'CharacterReference') {\n            appendText(decodeXmlEntity(state.doc.sliceString(child.from, child.to)));\n          } else if (child.type.name === 'Element') {\n            const data = nodeToJson(state, child);\n            if (data) {\n              children.push(data);\n            }\n          }\n        }\n        child = child.nextSibling;\n      }\n\n      // Trim only leading/trailing whitespace on the outer-boundary string segments —\n      // interior strings are preserved verbatim so meaningful whitespace around inline\n      // child elements (e.g. `<reasoning>foo <ref/> bar</reasoning>`) is not collapsed.\n      if (children.length > 0 && typeof children[0] === 'string') {\n        children[0] = children[0].trimStart();\n      }\n      if (children.length > 0) {\n        const lastIndex = children.length - 1;\n        const last = children[lastIndex];\n        if (typeof last === 'string') {\n          children[lastIndex] = last.trimEnd();\n        }\n      }\n      const trimmed = children.filter((value) => typeof value !== 'string' || value.length > 0);\n\n      if (trimmed.length > 0) {\n        tag.children = trimmed;\n      }\n    }\n\n    return tag;\n  }\n};\n\n/**\n * Decode the common XML named entities and numeric character references that\n * Lezer XML produces as `EntityReference` / `CharacterReference` nodes.\n */\nconst XML_NAMED_ENTITIES: Record<string, string> = {\n  '&lt;': '<',\n  '&gt;': '>',\n  '&amp;': '&',\n  '&quot;': '\"',\n  '&apos;': \"'\",\n};\n\nconst decodeXmlEntity = (raw: string): string => {\n  const named = XML_NAMED_ENTITIES[raw];\n  if (named !== undefined) {\n    return named;\n  }\n  const numeric = /^&#(x?)([0-9a-fA-F]+);$/.exec(raw);\n  if (numeric) {\n    const code = parseInt(numeric[2], numeric[1] ? 16 : 10);\n    if (Number.isFinite(code)) {\n      try {\n        return String.fromCodePoint(code);\n      } catch {\n        // Fall through and return the raw text on out-of-range code points.\n      }\n    }\n  }\n  return raw;\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\n// XML tags as widgets: a `<tag>` element in the markdown is matched by name against a registry and\n// replaced by the widget its definition names. The widget machinery — the decoration field, the\n// portal lifecycle, state and context effects, bookmark navigation — is `widgets`; this module is\n// the element matcher, including the tail scan that hides a streaming tag before its close arrives.\n\nimport { type EditorState, type Extension } from '@codemirror/state';\nimport { Decoration, type WidgetType } from '@codemirror/view';\n\nimport { type Range } from '../../../types/index.ts';\nimport { escapeRegExpSource } from '../../../util/index.ts';\nimport {\n  type WidgetDef,\n  type WidgetMatch,\n  type WidgetMatchContext,\n  type WidgetMatcher,\n  type WidgetProps,\n  createWidget,\n  widgetId,\n  widgetMatchersFacet,\n  widgetsCore,\n} from '../../widgets/index.ts';\nimport { nodeToJson } from './xml-util.ts';\n\n/**\n * Widget registry definition: how one tag renders.\n * Prefer an `id=\"...\"` attribute on the tag so `updateWidget` can target the instance; if omitted,\n * an id is derived from the tag’s document range (non-streaming) or opening position (streaming).\n * Streaming tags use `cm-xml-<from>` so the same portal id is kept when the closing tag arrives.\n */\nexport type XmlWidgetDef = WidgetDef<WidgetProps> & {\n  /**\n   * When true, the opening tag is flushed immediately and inner content streams character-by-character.\n   */\n  streaming?: boolean;\n};\n\n/** Tag name → definition. */\nexport type XmlWidgetRegistry = Record<string, XmlWidgetDef>;\n\n/**\n * The FIRST child, and only when it is a string.\n *\n * Enough for a tag whose content is one run of text; a tag holding a nested element gets everything\n * from that element onwards dropped, because the parser splits contents into alternating text and\n * element children. Use {@link getXmlInnerText} where nesting is possible.\n */\nexport const getXmlTextChild = (children: WidgetProps['children']): string | null => {\n  const child = children?.[0];\n  return typeof child === 'string' ? child : null;\n};\n\n/**\n * All text inside a tag, nested elements contributing their text but not their markup.\n *\n * A model writes prose that happens to fence a block in tags of its own — a reminder quoting a\n * `<checklist>`, say — and the reader wants the prose whole. Their tag NAMES are dropped rather\n * than reserialised: they delimit the block for the model, and are noise on screen.\n */\nexport const getXmlInnerText = (children: any[]): string | null => {\n  const text = (children ?? [])\n    .map((child) => (typeof child === 'string' ? child : (getXmlInnerText(child?.children ?? []) ?? '')))\n    .join('');\n  return text.length > 0 ? text : null;\n};\n\nexport type XmlTagsOptions = {\n  /** Tag registry. */\n  registry?: XmlWidgetRegistry;\n};\n\n/**\n * Implements custom XML tags via CodeMirror-native Widgets and portaled React/Solid components.\n *\n * - Decorations are created from XML tags that matched the provided registry.\n * - Native widgets are rendered inline; React/Solid widgets are portaled through the host's\n *   `widgetHost({ setWidgets })`.\n * - Widget state can be updated via `widgetUpdateEffect`, possibly BEFORE the widget is mounted.\n */\nexport const xmlTags = ({ registry = {} }: XmlTagsOptions = {}): Extension => [\n  widgetsCore,\n  widgetMatchersFacet.of(createXmlMatcher(registry)),\n];\n\nconst createXmlMatcher = (registry: XmlWidgetRegistry): WidgetMatcher => {\n  // Longest names first so `react-widget` wins over `react` in alternation.\n  const streamingTagNames = Object.entries(registry)\n    .filter(([, def]) => def.streaming)\n    .map(([name]) => name)\n    .sort((a, b) => b.length - a.length);\n\n  return {\n    nodes: ['Element'],\n    debug: Object.values(registry).some((def) => def.debug),\n    match: (node, { state, context, widgetStateMap, notifier }) => {\n      const args = nodeToJson(state, node.node);\n      if (!args) {\n        return undefined;\n      }\n      const def = registry[args._tag];\n      if (!def) {\n        return undefined;\n      }\n      // Skip unclosed streaming elements — the unclosed tag scan handles them.\n      if (def.streaming && !node.node.getChild('CloseTag')) {\n        return undefined;\n      }\n\n      const range = { from: node.node.from, to: node.node.to };\n      const id = widgetId(args.id, def.streaming ? `cm-xml-${range.from}` : `cm-xml-${range.from}-${range.to}`);\n      // NOTE: The widget state may already have been updated before the widget is mounted.\n      const props = { id, range, context, ...args, ...widgetStateMap[id] } satisfies WidgetProps;\n      const widget = createWidget({\n        def,\n        id,\n        props,\n        notifier,\n        signature: state.sliceDoc(range.from, range.to),\n      });\n      if (!widget) {\n        return undefined;\n      }\n      return {\n        ...range,\n        decoration: Decoration.replace({ widget, block: def.block, atomic: true, inclusive: true, tag: args._tag }),\n      };\n    },\n    tail: ({ state, range, context, widgetStateMap, notifier }) =>\n      streamingTagNames.length > 0\n        ? matchStreamingTail(state, range, registry, streamingTagNames, { context, widgetStateMap, notifier })\n        : undefined,\n  };\n};\n\n/**\n * Scans the document tail for an unclosed streaming tag and decorates it provisionally, so its\n * markup never renders raw while the rest of it arrives.\n */\nconst matchStreamingTail = (\n  state: EditorState,\n  range: Range,\n  registry: XmlWidgetRegistry,\n  streamingTagNames: string[],\n  { context, widgetStateMap, notifier }: Pick<WidgetMatchContext, 'context' | 'widgetStateMap' | 'notifier'>,\n): WidgetMatch | undefined => {\n  const tailText = state.sliceDoc(range.from, range.to);\n  const streamingPattern = streamingTagNames.map(escapeRegExpSource).join('|');\n  const tagPattern = new RegExp(`<(${streamingPattern})(\\\\s[^>]*)?>`, 'g');\n  let match: RegExpExecArray | null;\n\n  while ((match = tagPattern.exec(tailText)) !== null) {\n    const tagName = match[1];\n    const closeTag = `</${tagName}>`;\n    const afterOpen = match.index + match[0].length;\n\n    // Only process if there's no closing tag after this opening tag.\n    if (tailText.indexOf(closeTag, afterOpen) !== -1) {\n      continue;\n    }\n\n    const absoluteFrom = range.from + match.index;\n    const contentFrom = range.from + afterOpen;\n    const innerText = state.sliceDoc(contentFrom, range.to).trim();\n\n    const def = registry[tagName];\n    const props: WidgetProps = {\n      _tag: tagName,\n      context,\n      range: { from: absoluteFrom, to: range.to },\n      children: innerText ? [innerText] : undefined,\n    };\n\n    // Parse attributes from the opening tag.\n    const attrPattern = /(\\w+)=\"([^\"]*)\"/g;\n    let attrMatch: RegExpExecArray | null;\n    while ((attrMatch = attrPattern.exec(match[0])) !== null) {\n      props[attrMatch[1]] = attrMatch[2];\n    }\n\n    const id = widgetId(props.id, `cm-xml-${absoluteFrom}`);\n    const mergedProps = { ...props, id, ...widgetStateMap[id] };\n    const widget: WidgetType | undefined = createWidget({ def, id, props: mergedProps, notifier, streaming: true });\n\n    // Decorated even when the factory declined: a factory may return null while the tag is still\n    // empty (`<reasoning>` with no text yet), and leaving the range undecorated renders the raw\n    // markup to the reader for exactly as long as that lasts — the flash this scan exists to\n    // prevent. Without a widget it is an inline replace that simply hides the text; `block` needs\n    // a widget to size the line, so it is only claimed when there is one.\n    return {\n      from: absoluteFrom,\n      to: range.to,\n      streamingFrom: absoluteFrom,\n      decoration: Decoration.replace({\n        ...(widget ? { widget, block: def.block } : {}),\n        atomic: true,\n        inclusive: true,\n        tag: tagName,\n        streaming: true,\n        contentFrom,\n      }),\n    };\n  }\n\n  // A chunk boundary can land inside the opening tag itself, leaving a tail like `<reasoni` that\n  // the complete-tag scan above cannot match — and an undecorated tail renders as literal markup\n  // until the `>` arrives. Hidden without a widget: there is no tag name yet to build one from,\n  // and `block` needs a widget to size the line.\n  const partial = matchPartialOpenTag(tailText, streamingTagNames);\n  if (partial !== undefined) {\n    const absoluteFrom = range.from + partial.from;\n    return {\n      from: absoluteFrom,\n      to: range.to,\n      streamingFrom: absoluteFrom,\n      decoration: Decoration.replace({\n        atomic: true,\n        inclusive: true,\n        streaming: true,\n        contentFrom: range.to,\n        // The fragment, not the tag it may become: `tag` is what bookmark navigation matches\n        // against, and a tag that has not arrived yet must not be a jump target.\n        tag: partial.fragment,\n      }),\n    };\n  }\n\n  return undefined;\n};\n\n/**\n * Offset of a trailing `<` that could still become one of `tagNames`, or undefined.\n *\n * Only the document tail is considered: an unterminated `<` anywhere earlier is prose (`5 < 6`),\n * since a real tag would have been closed by the text that follows it. Requiring the fragment to be\n * a prefix of a registered name is what keeps `a < b` and `<div` out.\n */\nconst matchPartialOpenTag = (text: string, tagNames: string[]): { from: number; fragment: string } | undefined => {\n  const start = text.lastIndexOf('<');\n  if (start === -1) {\n    return undefined;\n  }\n\n  const fragment = text.slice(start + 1);\n  // A `>` means the tag is complete (or is not a tag at all); either way the scan above owns it.\n  if (fragment.includes('>')) {\n    return undefined;\n  }\n\n  // `<` alone is ambiguous — it becomes a tag or stays prose on the next character, and hiding the\n  // tail on that guess flickers the reader's own text.\n  return fragment.length > 0 && tagNames.some((name) => name.startsWith(fragment))\n    ? { from: start, fragment }\n    : undefined;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, Facet, RangeSet } from '@codemirror/state';\nimport { EditorView, GutterMarker, gutter } from '@codemirror/view';\n\n/**\n * A change-bar gutter marker: a vertical bar tinted with the author's colour. Full line height by\n * default; `rows` caps it to that many text rows (in `lh` units) — a BLOCK-insert proposal renders\n * as a widget occupying the top rows of its host line, and the bar must cover the proposal's rows\n * without tinting the host line's own text below it.\n */\nexport class ChangeBarMarker extends GutterMarker {\n  #colour: string;\n  #rows?: number;\n  #offsetRows?: number;\n\n  constructor(colour: string, rows?: number, offsetRows?: number) {\n    super();\n    this.#colour = colour;\n    this.#rows = rows;\n    this.#offsetRows = offsetRows;\n  }\n\n  override eq(other: ChangeBarMarker): boolean {\n    return other.#colour === this.#colour && other.#rows === this.#rows && other.#offsetRows === this.#offsetRows;\n  }\n\n  override toDOM(): HTMLElement {\n    const bar = document.createElement('div');\n    bar.className = 'cm-change-bar';\n    // Attribution colour is per-author (dynamic); the bar geometry lives in the theme class.\n    bar.style.background = this.#colour;\n    if (this.#rows !== undefined) {\n      bar.style.height = `calc(${this.#rows}lh)`;\n    }\n    // A proposal that STARTS with a newline renders its text below the break; shift the bar with it.\n    if (this.#offsetRows) {\n      bar.style.marginTop = `calc(${this.#offsetRows}lh)`;\n    }\n    return bar;\n  }\n}\n\n/**\n * Per-line change-bar markers for one suggestion source, derived from editor state. Contributed by the\n * suggestion extensions (`trackChanges`, `suggestions`) so each can colour its own lines by author.\n */\nexport type ChangeBarProvider = (state: EditorState) => RangeSet<GutterMarker>;\n\nconst changeBarTheme = EditorView.baseTheme({\n  '& .cm-change-bar-gutter': {\n    width: '3px',\n    paddingInline: '1px',\n  },\n  '& .cm-change-bar': {\n    width: '3px',\n    height: '100%',\n    borderRadius: '1px',\n  },\n});\n\n/**\n * Providers of per-line change bars. `enables` adds the single shared gutter (+ theme) once when any\n * provider is present — identical extension instances dedupe — so own edits (`trackChanges`) and foreign\n * suggestions (`suggestions`) share one bar column rather than stacking separate gutters. The gutter is\n * built inside `enables` so its `markers` can read this same facet.\n */\nconst changeBarProviders = Facet.define<ChangeBarProvider>({\n  enables: (self) => [\n    gutter({\n      class: 'cm-change-bar-gutter',\n      // Merge every contributor's per-line markers into the shared gutter (sorted so co-located markers\n      // from different sources interleave rather than throw).\n      markers: (view) => {\n        const sets = view.state.facet(self).map((provider) => provider(view.state));\n        return sets.length ? RangeSet.join(sets) : RangeSet.empty;\n      },\n    }),\n    changeBarTheme,\n  ],\n});\n\n/** Contribute per-line change bars to the shared author-coloured gutter (see {@link ChangeBarMarker}). */\nexport const changeBars = (provider: ChangeBarProvider): Extension => changeBarProviders.of(provider);\n\n/**\n * Keeps the change-bar column mounted with no markers. Hosts that toggle review overlays install this\n * once alongside their compartments, so entering/leaving Suggesting reconfigures marker providers\n * inside an existing gutter instead of adding and removing the column — which shifted the text and\n * flickered on every mode switch.\n */\nexport const changeBarGutter: Extension = changeBarProviders.of(() => RangeSet.empty);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, RangeSet, StateEffect, StateField } from '@codemirror/state';\nimport {\n  Decoration,\n  type DecorationSet,\n  EditorView,\n  type GutterMarker,\n  type Tooltip,\n  WidgetType,\n  hoverTooltip,\n} from '@codemirror/view';\n\nimport { Domino } from '@dxos/ui';\n\nimport { ChangeBarMarker, changeBars } from './change-bar.ts';\nimport {\n  type DiffHunk,\n  type GroupPolicy,\n  type Hunk,\n  computeCharHunks,\n  diffHunks,\n  groupHunks,\n  pairMarkupHunks,\n  rebaseHunksWith,\n} from './diff.ts';\n\n/** One author's proposed revision of the editor's document (the base). */\nexport type SuggestionSource = {\n  /** Stable author identity (e.g. an identity DID); distinguishes overlapping suggestions. */\n  author: string;\n  /** Author colour, supplied by the caller to match the collaboration awareness cursor palette. */\n  colour: string;\n  /** The author's full proposed content, diffed against the base (or the editor's document if no base). */\n  content: string;\n  /**\n   * The text this proposal was authored against (its branch's fork anchor). Diffing against it rather\n   * than the live document keeps the diff to what THIS author changed — otherwise text the reader has\n   * typed since the fork, which the proposal cannot contain, reads as the author deleting it.\n   * Falls back to {@link SuggestionsOptions.base}, then the editor's document.\n   */\n  base?: string;\n};\n\nexport type SuggestionsOptions = {\n  /** The proposals to overlay; each is diffed against the editor's document. */\n  sources: SuggestionSource[];\n  /**\n   * The accepted base (`main`) each source is diffed against. When given, a source is diffed\n   * `base` vs `source.content` and its hunks are rebased into the editor document's coordinates (see\n   * {@link rebaseHunks}) — so a foreign author's proposal decorates the right span even after the user's\n   * own branch (the editor document) has diverged from `base`. Omit to diff each source directly against\n   * the editor document (the standalone single-branch behaviour).\n   */\n  base?: string;\n  /**\n   * Show the hover Accept/Reject controls (default true). The ambient review overlay disables them\n   * (DESIGN §4, Decision 4: the companion cards are the review surface); the explicit compare path\n   * keeps them as its inline accept mechanism.\n   */\n  hoverControls?: boolean;\n  /**\n   * Optional grouping policy: coalesce an author's adjacent hunks into one reviewable unit (see\n   * {@link groupHunks}). Omit to render one control per raw hunk.\n   */\n  group?: GroupPolicy;\n  /**\n   * Invoked when a change is accepted. When provided, the container owns the mutation (routing the\n   * accept through a durable operation that merges the hunk to the parent), so the extension does NOT\n   * splice locally — it only dismisses the widget. Absent ⇒ the extension splices the source's text\n   * into the document directly (standalone use).\n   */\n  onAccept?: (hunk: DiffHunk, author: string) => void;\n  /**\n   * Invoked when a change is rejected. Lets the container route the reject through a durable operation\n   * (revert the hunk on the author's branch). The widget is dismissed either way.\n   */\n  onReject?: (hunk: DiffHunk, author: string) => void;\n  /** Invoked when a change is clicked in the document, so the host can reveal it elsewhere. */\n  onSelect?: (hunk: DiffHunk, author: string) => void;\n};\n\n/** A hunk tagged with the source it came from, so overlapping suggestions stay attributable. */\ntype TaggedHunk = DiffHunk & { author: string; colour: string };\n\n/**\n * Position-independent key so a dismissal survives offset shifts from unrelated edits. Scoped by\n * author so dismissing one author's change never hides an identical change by another.\n */\nexport const suggestionKey = (hunk: DiffHunk, author: string): string => `${author} ${hunk.removed} ${hunk.inserted}`;\n\n/** Adds a hunk to the dismissed (rejected, hidden) set without changing the document. */\nconst dismissEffect = StateEffect.define<string>();\n\ntype SuggestionState = {\n  dismissed: ReadonlySet<string>;\n  decorations: DecorationSet;\n  /** The currently visible (non-dismissed) hunks in document coordinates, for the tooltip layer. */\n  hunks: readonly TaggedHunk[];\n  /** Per-changed-line gutter change-bar markers, tinted with the author colour. */\n  changeBars: RangeSet<GutterMarker>;\n};\n\n/**\n * Google-Docs-style multi-author suggestion overlay: renders each change between the editor's\n * document (the base) and every {@link SuggestionSource} in place — the removed original struck\n * through, the proposed replacement shown beside it, tinted with the author's colour — with hover\n * Accept/Reject controls in the (non-clipped) tooltip layer. Overlapping suggestions from different\n * authors stack deterministically (by\n * offset, then author). Accept splices the source's version of that hunk into the document (merging\n * the change); Reject hides the suggestion without altering the document (a view-only dismissal for\n * the session).\n */\nexport const suggestions = ({\n  sources,\n  base,\n  group,\n  hoverControls = true,\n  onAccept,\n  onReject,\n  onSelect,\n}: SuggestionsOptions): Extension => {\n  const tagged = (state: EditorState): TaggedHunk[] => {\n    const doc = state.doc.toString();\n    // With an explicit base, diff each source against it and rebase the hunks into the (possibly\n    // diverged) document; without one, diff directly against the document (the original behaviour).\n    // The base↔doc char diff is the same for every source sharing a base, so compute each one once.\n    const charHunksFor = new Map<string, Hunk[]>();\n    const all: TaggedHunk[] = [];\n    for (const source of sources) {\n      const sourceBase = source.base ?? base;\n      const anchor = sourceBase ?? doc;\n      let charHunks: Hunk[] | undefined;\n      if (sourceBase !== undefined) {\n        charHunks = charHunksFor.get(sourceBase) ?? computeCharHunks(sourceBase, doc);\n        charHunksFor.set(sourceBase, charHunks);\n      }\n      // Edits the document already carries relative to the SAME anchor have been accepted, so they are\n      // no longer suggestions — matched by content (accepting shifts every later offset) and BEFORE\n      // grouping, since a group coalesces several hunks and would never match one of them.\n      const applied = sourceBase === undefined ? undefined : new Set(diffHunks(sourceBase, doc).map(editKey));\n      const authored = diffHunks(anchor, source.content);\n      const pending = applied === undefined ? authored : authored.filter((hunk) => !applied.has(editKey(hunk)));\n      // Matched delimiter pairs (e.g. `**` … `**`) become one hunk so the pair reviews and applies\n      // atomically — accepting half a pair leaves broken syntax.\n      const paired = pairMarkupHunks(pending, anchor);\n      const grouped = group ? groupHunks(paired, anchor, group) : paired;\n      const hunks = charHunks === undefined ? grouped : rebaseHunksWith(charHunks, grouped);\n      for (const hunk of hunks) {\n        all.push({ ...hunk, author: source.author, colour: source.colour });\n      }\n    }\n    // Deterministic stack order: earlier offset first, then by author so identical offsets are stable.\n    return all.sort(\n      (a, b) => a.from - b.from || a.to - b.to || (a.author < b.author ? -1 : a.author > b.author ? 1 : 0),\n    );\n  };\n\n  /**\n   * Has this change already landed in the document? Diffing a proposal against the revision its author\n   * wrote on keeps the diff to their edits, but it also keeps reporting them after they are accepted —\n   * the branch still differs from its own fork anchor. A change whose proposed text is already present\n   * (and whose replaced text is gone) has been applied, so it is no longer a suggestion.\n   */\n  const editKey = (hunk: DiffHunk): string => `${hunk.removed}\\u241f${hunk.inserted}`;\n\n  const build = (state: EditorState, dismissed: ReadonlySet<string>): SuggestionState => {\n    const ranges = [];\n    const visible: TaggedHunk[] = [];\n    // First author to touch a line owns its gutter bar colour (hunks arrive sorted by offset, author).\n    const lineColour = new Map<number, { colour: string; rows?: number; offsetRows?: number }>();\n    for (const hunk of tagged(state)) {\n      if (dismissed.has(suggestionKey(hunk, hunk.author))) {\n        continue;\n      }\n      visible.push(hunk);\n      if (hunk.to > hunk.from) {\n        ranges.push(deleteMark.range(hunk.from, hunk.to));\n      }\n      // The inline preview of the proposed text; a pure deletion has none (the strikethrough carries it).\n      if (hunk.inserted) {\n        // `side: 1` keeps the preview adjacent to the text it follows. At the very end of the document\n        // that would leave no caret position past the widget (a trailing suggestion makes the end of\n        // the document unreachable), so anchor it before the final position instead.\n        const side = hunk.to === state.doc.length ? -1 : 1;\n        ranges.push(Decoration.widget({ widget: new SuggestionWidget(hunk), side }).range(hunk.to));\n      }\n      // A whitespace-only change (a proposed paragraph break) has no visible text on the line it\n      // anchors to; barring that line marks prose the author never touched.\n      if (hunk.removed.trim().length === 0 && hunk.inserted.trim().length === 0) {\n        continue;\n      }\n      // A pure BLOCK insertion renders wholly inside its widget, occupying the TOP rows of its host\n      // line — a full-height gutter bar would tint the host line's own text too, so its bar is\n      // capped to the proposal's rows.\n      if (hunk.removed === '' && hunk.inserted.endsWith('\\n')) {\n        const from = state.doc.lineAt(hunk.from).from;\n        if (!lineColour.has(from)) {\n          const leading = hunk.inserted.length - hunk.inserted.replace(/^\\n+/, '').length;\n          const core = hunk.inserted.replace(/^\\n+/, '').replace(/\\n+$/, '');\n          if (core.length > 0) {\n            lineColour.set(from, {\n              colour: hunk.colour,\n              rows: core.split('\\n').length,\n              offsetRows: leading > 0 ? leading : undefined,\n            });\n          }\n        }\n        continue;\n      }\n      // Trim a trailing newline so a paragraph-break change does not tag the following (empty) line.\n      let end = hunk.to;\n      while (end > hunk.from && state.doc.sliceString(end - 1, end) === '\\n') {\n        end--;\n      }\n      for (let line = state.doc.lineAt(hunk.from).number; line <= state.doc.lineAt(end).number; line++) {\n        const from = state.doc.line(line).from;\n        if (!lineColour.has(from)) {\n          lineColour.set(from, { colour: hunk.colour });\n        }\n      }\n    }\n    const changeBarSet = RangeSet.of(\n      [...lineColour.entries()]\n        .sort(([a], [b]) => a - b)\n        .map(([from, { colour, rows, offsetRows }]) => new ChangeBarMarker(colour, rows, offsetRows).range(from)),\n    );\n    // `sort: true` orders the mixed mark/widget ranges (multiple sources interleave arbitrarily).\n    return { dismissed, decorations: Decoration.set(ranges, true), hunks: visible, changeBars: changeBarSet };\n  };\n\n  const field = StateField.define<SuggestionState>({\n    create: (state) => build(state, new Set()),\n    update: (value, transaction) => {\n      const dismissed = new Set(value.dismissed);\n      let changed = false;\n      for (const effect of transaction.effects) {\n        if (effect.is(dismissEffect)) {\n          dismissed.add(effect.value);\n          changed = true;\n        }\n      }\n      if (!transaction.docChanged && !changed) {\n        return value;\n      }\n      return build(transaction.state, dismissed);\n    },\n    provide: (self) => EditorView.decorations.from(self, (value) => value.decorations),\n  });\n\n  return [\n    field,\n    selectHandler(field, onSelect),\n    hoverControls ? suggestTooltip(field, onAccept, onReject) : [],\n    changeBars((state) => state.field(field).changeBars),\n    suggestTheme,\n  ];\n};\n\n/**\n * Reports the change under a click, so the host can reveal it on its other surfaces (the review\n * companion accents the matching card). The click is not consumed — the caret still lands where the\n * reader put it.\n */\nconst selectHandler = (\n  field: StateField<SuggestionState>,\n  onSelect?: (hunk: DiffHunk, author: string) => void,\n): Extension =>\n  EditorView.domEventHandlers({\n    mousedown: (event, view) => {\n      if (!onSelect) {\n        return false;\n      }\n      const pos = view.posAtCoords({ x: event.clientX, y: event.clientY });\n      if (pos === null) {\n        return false;\n      }\n      const hunk = view.state\n        .field(field)\n        .hunks.find((candidate) =>\n          candidate.from === candidate.to ? pos === candidate.from : pos >= candidate.from && pos <= candidate.to,\n        );\n      if (hunk) {\n        onSelect(hunk, hunk.author);\n      }\n      return false;\n    },\n  });\n\n/**\n * Accept/reject controls in the CodeMirror tooltip layer (`.cm-tooltip`), which renders outside\n * `.cm-scroller` and so is never clipped by its overflow (the failure of the old inline\n * `position: absolute` popover). Hover-triggered per hunk: hovering a change's struck text or its\n * inline preview surfaces the controls for that hunk.\n */\nconst suggestTooltip = (\n  field: StateField<SuggestionState>,\n  onAccept?: (hunk: DiffHunk, author: string) => void,\n  onReject?: (hunk: DiffHunk, author: string) => void,\n): Extension => {\n  // The controls are anchored to the hunk, not the pointer, and the same instance is returned for\n  // every position within it — a fresh tooltip per pointer position makes them flicker and jump as the\n  // pointer crosses the change.\n  const tooltips = new Map<string, Tooltip>();\n  return hoverTooltip(\n    (view, pos): Tooltip | null => {\n      // Match the hunk under the pointer: a range covers `[from, to]`; a pure insertion (from === to,\n      // rendered as a widget) is matched at its single offset.\n      const hunk = view.state\n        .field(field)\n        .hunks.find((candidate) =>\n          candidate.from === candidate.to ? pos === candidate.from : pos >= candidate.from && pos <= candidate.to,\n        );\n      if (!hunk) {\n        return null;\n      }\n\n      const key = `${suggestionKey(hunk, hunk.author)} ${hunk.from}:${hunk.to}`;\n      const cached = tooltips.get(key);\n      if (cached) {\n        return cached;\n      }\n\n      // Only the hunk being hovered is worth keeping; the document moves on.\n      tooltips.clear();\n      const tooltip: Tooltip = {\n        pos: hunk.from,\n        end: hunk.to,\n        above: true,\n        create: () => ({ dom: createControls(view, hunk, onAccept, onReject) }),\n      };\n      tooltips.set(key, tooltip);\n      return tooltip;\n    },\n    {\n      hoverTime: 75,\n      // Accepting changes the document and rejecting dismisses the hunk; either way the controls act on\n      // a change that no longer exists, so they must go rather than wait for the pointer to move.\n      hideOn: (transaction) => transaction.docChanged || transaction.effects.some((effect) => effect.is(dismissEffect)),\n    },\n  );\n};\n\n/** Builds the accept/reject control row for a hunk; shared by the tooltip layer. */\nconst createControls = (\n  view: EditorView,\n  hunk: TaggedHunk,\n  onAccept?: (hunk: DiffHunk, author: string) => void,\n  onReject?: (hunk: DiffHunk, author: string) => void,\n): HTMLElement =>\n  Domino.of('div')\n    .classNames('cm-suggest-controls')\n    .append(\n      // Each control is a square `dx-button` div wrapping a Phosphor icon.\n      Domino.of('div')\n        .classNames('dx-control dx-button dx-button-square cm-suggest-accept')\n        .attributes({ 'role': 'button', 'data-size': 'sm', 'title': 'Accept change' })\n        .append(Domino.svg('ph--check--regular'))\n        .on('mousedown', (event) => {\n          event.preventDefault();\n          if (onAccept) {\n            // The container owns the mutation (durable cherry-pick op); the op's edit flows back and\n            // re-diffs the hunk away. Dismiss for immediate feedback — do not splice locally (that\n            // would double-apply the change onto the parent).\n            view.dispatch({ effects: dismissEffect.of(suggestionKey(hunk, hunk.author)) });\n            onAccept(hunk, hunk.author);\n          } else {\n            view.dispatch({ changes: { from: hunk.from, to: hunk.to, insert: hunk.inserted } });\n          }\n        }),\n      Domino.of('div')\n        .classNames('dx-control dx-button dx-button-square cm-suggest-reject')\n        .attributes({ 'role': 'button', 'data-size': 'sm', 'title': 'Reject change' })\n        .append(Domino.svg('ph--x--regular'))\n        .on('mousedown', (event) => {\n          event.preventDefault();\n          view.dispatch({ effects: dismissEffect.of(suggestionKey(hunk, hunk.author)) });\n          onReject?.(hunk, hunk.author);\n        }),\n    ).root;\n\nexport type SuggestChangesOptions = {\n  /** The proposal (e.g. a branch's content) whose changes are suggested over the editor's document. */\n  proposal: string;\n  /**\n   * Author colour for the inline markers — pass the branch author's palette colour (e.g.\n   * `var(--color-<hue>-text)`) so the suggestion reads with the author's consistent colour. Defaults\n   * to the success colour for a standalone (author-agnostic) review.\n   */\n  colour?: string;\n  onAccept?: (hunk: DiffHunk) => void;\n  onReject?: (hunk: DiffHunk) => void;\n};\n\n/**\n * Single-source suggestion overlay — the common case of one proposal (e.g. a branch) reviewed against\n * the editor's document. A thin wrapper over {@link suggestions}.\n */\nexport const suggestChanges = ({\n  proposal,\n  colour = 'var(--color-success-text)',\n  onAccept,\n  onReject,\n}: SuggestChangesOptions): Extension =>\n  suggestions({\n    sources: [{ author: '', colour, content: proposal }],\n    onAccept: onAccept && ((hunk) => onAccept(hunk)),\n    onReject: onReject && ((hunk) => onReject(hunk)),\n  });\n\n/** The replaced original as a plain strikethrough; attribution is carried by the coloured insert. */\nconst deleteMark = Decoration.mark({ class: 'cm-suggest-delete' });\n\n/** Inline preview of a change's proposed text; the accept/reject controls live in the tooltip layer. */\nclass SuggestionWidget extends WidgetType {\n  #hunk: TaggedHunk;\n\n  constructor(hunk: TaggedHunk) {\n    super();\n    this.#hunk = hunk;\n  }\n\n  override eq(other: SuggestionWidget): boolean {\n    // The preview only renders the proposed text in the author's colour, so equality is content-only;\n    // author distinguishes overlapping suggestions with the same text.\n    return (\n      other.#hunk.inserted === this.#hunk.inserted &&\n      other.#hunk.colour === this.#hunk.colour &&\n      other.#hunk.author === this.#hunk.author\n    );\n  }\n\n  override toDOM(): HTMLElement {\n    // Only the CORE text carries the coloured chrome (underline/bar): leading and trailing newline\n    // runs render as plain unstyled text, so a proposal that starts or ends with a paragraph break\n    // still breaks the layout without drawing stray bar/underline fragments on empty rows — and a\n    // whitespace-only proposal (a bare paragraph break) previews as an unmarked break.\n    const inserted = this.#hunk.inserted;\n    const core = inserted.replace(/^\\n+/, '').replace(/\\n+$/, '');\n    const leading = inserted.slice(0, inserted.indexOf(core));\n    const trailing = core.length === 0 ? '' : inserted.slice(leading.length + core.length);\n    const root = Domino.of('span').classNames('cm-suggest-actions');\n    if (core.length === 0) {\n      return root.append(Domino.of('span').text(inserted)).root;\n    }\n    if (leading) {\n      root.append(Domino.of('span').text(leading));\n    }\n    root.append(\n      Domino.of('span')\n        .classNames('cm-suggest-insert')\n        // The author's colour carries attribution: coloured text underlined in the same colour.\n        .style({ color: this.#hunk.colour, borderBottomColor: this.#hunk.colour })\n        .text(core),\n    );\n    if (trailing) {\n      root.append(Domino.of('span').text(trailing));\n    }\n    return root.root;\n  }\n\n  // Pointer events must reach the editor: ignoring them left the preview inert, so hovering it never\n  // opened the accept/reject tooltip and clicking at or past a trailing preview never moved the caret.\n  // Everything else (input, composition) stays ignored — the preview is not editable text.\n  override ignoreEvent(event: Event): boolean {\n    return !(event instanceof MouseEvent || event instanceof PointerEvent);\n  }\n}\n\nconst suggestTheme = EditorView.baseTheme({\n  // The replaced original: a plain, de-emphasised strikethrough — no fill, so overlapping deletions\n  // from multiple authors on the same span don't stack into a coloured mess.\n  '& .cm-suggest-delete': {\n    textDecoration: 'line-through',\n    textDecorationColor: 'var(--color-separator)',\n    opacity: 0.6,\n  },\n  // The proposed text: shown in the author's colour and underlined in it (both set inline per hunk),\n  // so attribution reads from colour rather than a uniform green fill.\n  '& .cm-suggest-insert': {\n    borderBottom: '2px solid transparent',\n  },\n  '& .cm-suggest-actions': {\n    marginInlineStart: '2px',\n  },\n  // Accept/reject render in the CodeMirror tooltip layer (a `.cm-tooltip` sibling of `.cm-scroller`),\n  // so the controls are never clipped by the scroller's overflow.\n  '& .cm-suggest-controls': {\n    display: 'flex',\n    gap: '2px',\n    padding: '2px',\n    borderRadius: '4px',\n    background: 'var(--color-modal-surface)',\n    border: '1px solid var(--color-separator)',\n    boxShadow: '0 2px 6px color-mix(in srgb, black 25%, transparent)',\n    whiteSpace: 'nowrap',\n  },\n  // Compact popover buttons sized to the text.\n  '& .cm-suggest-accept, & .cm-suggest-reject': {\n    blockSize: '1.3em',\n    inlineSize: '1.3em',\n    minBlockSize: '0',\n  },\n  '& .cm-suggest-accept': {\n    color: 'var(--color-success-text)',\n  },\n  '& .cm-suggest-reject': {\n    color: 'var(--color-error-text)',\n  },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { Compartment, type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\nimport { type DiffHunk } from './diff.ts';\nimport { type SuggestionSource, suggestions } from './suggest.ts';\n\nexport type SuggestionsOverlay = {\n  /** Initially empty; install once alongside the editor's other extensions. */\n  extension: Extension;\n  /**\n   * Live-swaps the aggregated suggestion sources without remounting the editor. Pass `base` (the\n   * accepted `main`) when the editor is bound to a diverged document (e.g. the user's own suggestion\n   * branch in Suggesting mode) so each source is diffed against `base` and rebased into the document's\n   * coordinates; omit it to diff sources directly against the editor document.\n   */\n  reconfigure: (view: EditorView, sources: SuggestionSource[], enabled: boolean, base?: string) => void;\n};\n\n/**\n * Compartment-backed overlay of ALL active suggestion branches' changes for the ambient review\n * model: the editor's default view renders every open suggestion branch as a live suggestion layer,\n * and the controller reconfigures the aggregated `sources` in place as branches open/close/edit —\n * mirroring how `MarkdownArticle` reconfigures its compare overlay via `compareCompartment`, so\n * toggling the overlay never remounts the editor (which would rebind automerge and lose\n * scroll/selection).\n */\nexport const suggestionsOverlay = (\n  onAccept?: (hunk: DiffHunk, author: string) => void,\n  onReject?: (hunk: DiffHunk, author: string) => void,\n  onSelect?: (hunk: DiffHunk, author: string) => void,\n): SuggestionsOverlay => {\n  const compartment = new Compartment();\n  return {\n    extension: compartment.of([]),\n    reconfigure: (view: EditorView, sources: SuggestionSource[], enabled: boolean, base?: string) => {\n      view.dispatch({\n        effects: compartment.reconfigure(\n          // Hover accept/reject is disabled on the ambient path (Decision 4): cards are the surface.\n          enabled && sources.length > 0\n            ? suggestions({ sources, base, hoverControls: false, onAccept, onReject, onSelect })\n            : [],\n        ),\n      });\n    },\n  };\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, RangeSet, StateEffect, StateField } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, type GutterMarker, WidgetType, keymap } from '@codemirror/view';\n\nimport { Domino } from '@dxos/ui';\n\nimport { ChangeBarMarker, changeBars } from './change-bar.ts';\nimport { computeCharHunks } from './diff.ts';\n\nexport type TrackChangesOptions = {\n  /**\n   * The accepted base (`main`) the branch is diffed against. The editor's document is the branch, so\n   * insertions and deletions are computed relative to this. Replace it live with {@link setBase} (e.g.\n   * when a merge lands on main) rather than rebuilding the extension.\n   */\n  main: string;\n  /** The branch author's palette colour (e.g. `var(--color-cyan-text)`) applied to the tracked marks. */\n  colour: string;\n};\n\n/**\n * Replaces the tracked base without rebuilding the extension, so a merge to main re-diffs the live\n * document. The base is a {@link StateField} fed by this effect rather than a closure constant, matching\n * the production shape (see the bind-to-branch spike).\n */\nexport const setBase = StateEffect.define<string>();\n\n/**\n * Google-Docs-style \"Suggesting mode\" via bind-to-branch: the editor is bound to the branch document\n * and edits accrue natively to it, while the diff against {@link TrackChangesOptions.main} is decorated\n * over the live buffer — insertions as author-coloured underlined marks, deletions as phantom\n * strikethrough widgets rendering the removed base text (which is absent from the document). A\n * replacement renders both, `~~old~~new`. Single author; foreign authors compose via the separate\n * `suggestions()` overlay.\n */\nexport const trackChanges = ({ main, colour }: TrackChangesOptions): Extension => {\n  // The author colour is constant for the instance, so bind it to the insertion mark once. Structural\n  // styling lives in the theme class; only the dynamic attribution colour is inline (as in `suggest.ts`).\n  const insertMark = Decoration.mark({\n    class: 'cm-track-insert',\n    attributes: { style: `color:${colour};text-decoration-color:${colour}` },\n  });\n\n  // One-entry memo of the last (base, doc) → decorations. Rebuilding is a whole-document char diff, so\n  // reuse it when the same state recurs (undo/redo to a prior revision, or a doc-changing transaction\n  // that lands back on a cached buffer). The key is length-prefixed so no base/doc pair is ambiguous.\n  let cache: { base: string; doc: string; state: TrackState } | undefined;\n\n  const build = (base: string, state: EditorState): TrackState => {\n    const doc = state.doc.toString();\n    if (cache && cache.base === base && cache.doc === doc) {\n      return cache.state;\n    }\n    const decorations = [];\n    const phantomRanges = [];\n    const phantomPositions: number[] = [];\n    // Line starts carrying a change, for the gutter change-bar (deduped, one bar per line).\n    const changedLines = new Set<number>();\n    const markLines = (from: number, to: number) => {\n      // Trim a trailing newline so an inserted/removed paragraph break does not tag the following\n      // (empty) line — otherwise a new-paragraph suggestion draws a bar two lines deep.\n      let end = to;\n      while (end > from && state.doc.sliceString(end - 1, end) === '\\n') {\n        end--;\n      }\n      for (let line = state.doc.lineAt(from).number; line <= state.doc.lineAt(end).number; line++) {\n        changedLines.add(state.doc.line(line).from);\n      }\n    };\n    for (const hunk of computeCharHunks(base, doc)) {\n      // Deletion: base text absent from the branch. Anchor the phantom at `fromB` with `side: -1` so it\n      // renders before a co-located insertion (giving `~~old~~new`).\n      if (hunk.toA > hunk.fromA) {\n        const widget = Decoration.widget({\n          widget: new PhantomDeleteWidget(base.slice(hunk.fromA, hunk.toA), colour),\n          side: -1,\n        }).range(hunk.fromB);\n        decorations.push(widget);\n        phantomRanges.push(widget);\n        phantomPositions.push(hunk.fromB);\n        markLines(hunk.fromB, hunk.fromB);\n      }\n      // Insertion: branch text absent from the base.\n      if (hunk.toB > hunk.fromB) {\n        decorations.push(insertMark.range(hunk.fromB, hunk.toB));\n        markLines(hunk.fromB, hunk.toB);\n      }\n    }\n    const result: TrackState = {\n      base,\n      // `sort: true` orders the interleaved widget/mark ranges (co-located phantom before its mark).\n      decorations: Decoration.set(decorations, true),\n      atomic: Decoration.set(phantomRanges, true),\n      phantoms: phantomPositions,\n      changeBars: RangeSet.of(\n        [...changedLines].sort((a, b) => a - b).map((from) => new ChangeBarMarker(colour).range(from)),\n      ),\n    };\n    cache = { base, doc, state: result };\n    return result;\n  };\n\n  const field = StateField.define<TrackState>({\n    create: (state) => build(main, state),\n    update: (value, transaction) => {\n      let base = value.base;\n      let baseChanged = false;\n      for (const effect of transaction.effects) {\n        if (effect.is(setBase)) {\n          base = effect.value;\n          baseChanged = true;\n        }\n      }\n      if (!transaction.docChanged && !baseChanged) {\n        return value;\n      }\n      return build(base, transaction.state);\n    },\n    provide: (self) => EditorView.decorations.from(self, (value) => value.decorations),\n  });\n\n  return [\n    field,\n    trackChangesKeymap(field),\n    trackChangesAtomic(field),\n    changeBars((state) => state.field(field).changeBars),\n    trackChangesTheme,\n  ];\n};\n\ntype TrackState = {\n  base: string;\n  decorations: DecorationSet;\n  /** Phantom widgets only, for the atomic-range facet (they are zero-width, so this is inert today). */\n  atomic: DecorationSet;\n  /** Document offsets carrying a phantom, for the keymap guard. */\n  phantoms: readonly number[];\n  /** Per-changed-line gutter change-bar markers, tinted with the author colour. */\n  changeBars: RangeSet<GutterMarker>;\n};\n\n/**\n * Phantom render of text present in `main` but deleted on the branch — struck through and\n * non-editable, since the text has no extent in the document.\n */\nclass PhantomDeleteWidget extends WidgetType {\n  #text: string;\n  #colour: string;\n\n  constructor(text: string, colour: string) {\n    super();\n    this.#text = text;\n    this.#colour = colour;\n  }\n\n  override eq(other: PhantomDeleteWidget): boolean {\n    return other.#text === this.#text && other.#colour === this.#colour;\n  }\n\n  override toDOM(): HTMLElement {\n    return (\n      Domino.of('span')\n        .classNames('cm-track-delete')\n        .attributes({ contenteditable: 'false' })\n        // Attribution colour is per-author (dynamic); the strikethrough itself is in the theme class.\n        .style({ color: this.#colour, textDecorationColor: this.#colour })\n        .text(this.#text).root\n    );\n  }\n\n  override ignoreEvent(): boolean {\n    return true;\n  }\n}\n\n/**\n * A phantom has zero document extent, so the caret can only sit beside it — a plain Backspace beside a\n * phantom therefore deletes the real branch character hidden behind the strikethrough rather than the\n * phantom. Swallow it so tracked deletions read as protected units. Arrow keys need no override: the\n * zero-width phantom is stepped over in a single motion already.\n */\nconst trackChangesKeymap = (field: StateField<TrackState>): Extension =>\n  keymap.of([\n    {\n      key: 'Backspace',\n      run: (view) => {\n        const selection = view.state.selection.main;\n        return selection.empty && view.state.field(field).phantoms.includes(selection.head);\n      },\n    },\n  ]);\n\n/** Register the phantoms as atomic ranges so cursor motion treats each as one unit. */\nconst trackChangesAtomic = (field: StateField<TrackState>): Extension =>\n  EditorView.atomicRanges.of((view) => view.state.field(field, false)?.atomic ?? Decoration.none);\n\nconst trackChangesTheme = EditorView.baseTheme({\n  // Insertion: author-coloured (inline) and underlined so it reads as added, not merely tinted.\n  '& .cm-track-insert': {\n    textDecoration: 'underline',\n    textDecorationThickness: '1px',\n    textUnderlineOffset: '2px',\n  },\n  // Deletion: struck through and de-emphasised; the removed base text, rendered as a phantom.\n  // `pre-wrap` preserves newlines in a multi-line/block deletion (a plain span collapses them to a\n  // single space), so a removed paragraph reads with its line breaks intact.\n  '& .cm-track-delete': {\n    textDecoration: 'line-through',\n    opacity: 0.7,\n    whiteSpace: 'pre-wrap',\n  },\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { StateEffect, StateField } from '@codemirror/state';\n\nexport const modalStateEffect = StateEffect.define<boolean>();\n\n/**\n * Determines if a modal dialog (e.g., popover) is active.\n */\nexport const modalStateField = StateField.define<boolean>({\n  create: () => false,\n  update: (value, tr) => {\n    let newValue = value;\n    for (const effect of tr.effects) {\n      if (effect.is(modalStateEffect)) {\n        newValue = effect.value;\n      }\n    }\n\n    return newValue;\n  },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, Prec, StateEffect, StateField } from '@codemirror/state';\nimport { EditorView, RectangleMarker, keymap, layer } from '@codemirror/view';\n\nimport { type Block, type BlockExtent } from './types.ts';\n\n// Amount (px) the highlight extends left of the content, matching the outline boxes and drag preview.\nconst BOX_INSET_X = 8;\nconst BOX_PADDING = 2;\n\n/**\n * Operations a document supplies so whole blocks can be selected, reordered, and cut/copy/pasted. The\n * same contract backs markdown top-level blocks and outliner items — nothing here is markdown-specific.\n */\nexport type BlockOps = {\n  /** Blocks in document order; ranges non-overlapping. Should be memoized per state. */\n  getBlocks: (state: EditorState) => Block[];\n  /**\n   * Moves the blocks at `sourceIndices` to the slot before `dropIndex` (the end when\n   * `dropIndex === blocks.length`), preserving their relative order, as a single edit.\n   */\n  moveBlocks: (view: EditorView, sourceIndices: number[], dropIndex: number) => void;\n  /** Serializes blocks to clipboard text (markdown joins with a blank line; the outliner with newlines). */\n  serialize: (state: EditorState, blocks: Block[]) => string;\n  /**\n   * Removes the blocks at `indices` and, when `text` is non-null, inserts it as block(s) at the first\n   * removed slot — as a single edit. `text === null` is a pure delete (cut); non-null is a replace (paste).\n   */\n  replaceBlocks: (view: EditorView, indices: number[], text: string | null) => void;\n};\n\n//\n// State: the block selection is an ordered set of block anchors (each block's `from`), kept sorted.\n// Positions — not indices — so the set survives edits (mapped through changes).\n//\n\n/** Replaces the whole block selection. */\nexport const setBlockSelection = StateEffect.define<readonly number[]>();\n\n/** Adds/removes a single block anchor, keeping the rest. */\nexport const toggleBlockSelection = StateEffect.define<number>();\n\nconst hasSelectionEffect = (effects: readonly StateEffect<any>[]): boolean =>\n  effects.some((effect) => effect.is(setBlockSelection) || effect.is(toggleBlockSelection));\n\nexport const blockSelectionField = StateField.define<readonly number[]>({\n  create: () => [],\n  update: (value, tr) => {\n    // Our own effects take precedence and are the only way to grow the selection.\n    if (hasSelectionEffect(tr.effects)) {\n      let next = value;\n      for (const effect of tr.effects) {\n        if (effect.is(setBlockSelection)) {\n          next = [...effect.value].sort((a, b) => a - b);\n        } else if (effect.is(toggleBlockSelection)) {\n          const set = new Set(next);\n          set.has(effect.value) ? set.delete(effect.value) : set.add(effect.value);\n          next = [...set].sort((a, b) => a - b);\n        }\n      }\n      return next;\n    }\n\n    if (value.length === 0) {\n      return value;\n    }\n\n    // Keep anchors aligned through edits (a consumer re-resolves them against `getBlocks`). Caret moves\n    // (keyboard nav) keep the selection so it can be extended; a plain text click clears it (see below).\n    if (tr.docChanged) {\n      return value.map((pos) => tr.changes.mapPos(pos, -1));\n    }\n\n    return value;\n  },\n});\n\n/** Resolves the selection anchors to `{ block, index }` in document order, dropping stale anchors. */\nexport const getSelectedBlocks = (\n  state: EditorState,\n  getBlocks: BlockOps['getBlocks'],\n): { block: Block; index: number }[] => {\n  const anchors = state.field(blockSelectionField, false);\n  if (!anchors || anchors.length === 0) {\n    return [];\n  }\n  const byFrom = new Map(getBlocks(state).map((block, index) => [block.from, { block, index }]));\n  const result: { block: Block; index: number }[] = [];\n  for (const anchor of anchors) {\n    const match = byFrom.get(anchor);\n    if (match) {\n      result.push(match);\n    }\n  }\n  return result;\n};\n\n//\n// Highlight (layer): a full-width tinted box behind each selected block, aligned with the outline boxes.\n//\n\nconst buildSelectionMarkers = (\n  view: EditorView,\n  getBlocks: BlockOps['getBlocks'],\n  getExtent?: BlockExtent,\n): RectangleMarker[] => {\n  const selected = getSelectedBlocks(view.state, getBlocks);\n  if (selected.length === 0) {\n    return [];\n  }\n\n  const contentRect = view.contentDOM.getBoundingClientRect();\n  const gutterOffset = contentRect.left - view.scrollDOM.getBoundingClientRect().left;\n  const markers: RectangleMarker[] = [];\n  for (const { block } of selected) {\n    // The extent is the range the block visually occupies (its subtree for the outliner).\n    const extent = getExtent ? getExtent(view.state, block) : block;\n    const from = Math.max(extent.from, view.viewport.from);\n    const to = Math.min(extent.to, view.viewport.to);\n    if (from > to) {\n      continue;\n    }\n    const firstLine = view.lineBlockAt(from);\n    const lastLine = view.lineBlockAt(to);\n    markers.push(\n      new RectangleMarker(\n        'cm-blockSelected',\n        gutterOffset - BOX_INSET_X,\n        firstLine.top - BOX_PADDING,\n        contentRect.width + BOX_INSET_X,\n        lastLine.bottom - firstLine.top + BOX_PADDING * 2,\n      ),\n    );\n  }\n  return markers;\n};\n\nconst selectionChanged = (a: EditorState, b: EditorState): boolean =>\n  a.field(blockSelectionField, false) !== b.field(blockSelectionField, false);\n\nconst selectionTheme = EditorView.theme({\n  '.cm-blockSelectedLayer': {\n    pointerEvents: 'none',\n  },\n  '.cm-blockSelected': {\n    boxSizing: 'border-box',\n    backgroundColor: 'var(--color-cm-highlight-surface)',\n  },\n});\n\n//\n// Clipboard: cut/copy/paste act on the block selection; when it is empty they fall through to native\n// text behavior. Mod-C/X/V route through these same DOM events, so no separate keymap is needed.\n//\n\nconst clipboardHandlers = (ops: BlockOps): Extension =>\n  EditorView.domEventHandlers({\n    copy: (event, view) => {\n      const selected = getSelectedBlocks(view.state, ops.getBlocks);\n      if (selected.length === 0) {\n        return false;\n      }\n      event.clipboardData?.setData(\n        'text/plain',\n        ops.serialize(\n          view.state,\n          selected.map((entry) => entry.block),\n        ),\n      );\n      event.preventDefault();\n      return true;\n    },\n    cut: (event, view) => {\n      const selected = getSelectedBlocks(view.state, ops.getBlocks);\n      if (selected.length === 0) {\n        return false;\n      }\n      event.clipboardData?.setData(\n        'text/plain',\n        ops.serialize(\n          view.state,\n          selected.map((entry) => entry.block),\n        ),\n      );\n      event.preventDefault();\n      // `replaceBlocks` clears the block selection within its own transaction.\n      ops.replaceBlocks(\n        view,\n        selected.map((entry) => entry.index),\n        null,\n      );\n      return true;\n    },\n    paste: (event, view) => {\n      const selected = getSelectedBlocks(view.state, ops.getBlocks);\n      if (selected.length === 0) {\n        return false;\n      }\n      const text = event.clipboardData?.getData('text/plain') ?? '';\n      event.preventDefault();\n      // Replace the selected blocks with the pasted text (`replaceBlocks` clears the block selection).\n      ops.replaceBlocks(\n        view,\n        selected.map((entry) => entry.index),\n        text,\n      );\n      return true;\n    },\n  });\n\n// A plain press in the text clears the block selection (handle presses go through the gutter and don't\n// hit `.cm-content`; shift-presses are left to extend the block selection via the gutter).\nconst clearOnTextPress = EditorView.domEventHandlers({\n  mousedown: (event, view) => {\n    if (event.shiftKey) {\n      return false;\n    }\n    const target = event.target;\n    const anchors = view.state.field(blockSelectionField, false);\n    if (anchors && anchors.length > 0 && target instanceof Node && view.contentDOM.contains(target)) {\n      view.dispatch({ effects: setBlockSelection.of([]) });\n    }\n    return false;\n  },\n});\n\n// Escape clears the block selection. High precedence so it wins over other Escape bindings while a\n// selection is active, but falls through (returns false) when there is nothing selected.\nconst clearOnEscape = Prec.high(\n  keymap.of([\n    {\n      key: 'Escape',\n      run: (view) => {\n        const anchors = view.state.field(blockSelectionField, false);\n        if (!anchors || anchors.length === 0) {\n          return false;\n        }\n        view.dispatch({ effects: setBlockSelection.of([]) });\n        return true;\n      },\n    },\n  ]),\n);\n\n/**\n * Draws a border/background box behind each selected block, in a below-text layer re-measured on edits,\n * scrolling, and viewport changes. Separate from the selection state and clipboard so it can travel with\n * the drag grip (which populates the selection) independently of `createBlockSelection`. The blocks are\n * supplied by the caller (see `blockSelectionHighlight` for markdown top-level blocks).\n */\nexport const createBlockSelectionHighlight = (getBlocks: BlockOps['getBlocks'], getExtent?: BlockExtent): Extension => [\n  blockSelectionField,\n  selectionTheme,\n  layer({\n    above: false,\n    class: 'cm-blockSelectedLayer',\n    update: (update) =>\n      update.docChanged ||\n      update.viewportChanged ||\n      update.geometryChanged ||\n      selectionChanged(update.startState, update.state),\n    markers: (view) => buildSelectionMarkers(view, getBlocks, getExtent),\n  }),\n];\n\n/**\n * Whole-block selection state and cut/copy/paste over the selected blocks. The border/background behind\n * the selection is drawn by `createBlockSelectionHighlight`, and the gutter grip gestures that populate\n * the selection live in `createBlockDrag` (the handle is a gutter marker) — compose all three together.\n */\nexport const createBlockSelection = (ops: BlockOps): Extension => [\n  blockSelectionField,\n  clipboardHandlers(ops),\n  clearOnTextPress,\n  clearOnEscape,\n];\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension, StateEffect, StateField } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';\n\nimport { Domino } from '@dxos/ui';\n\nimport { blockSelectionField, getSelectedBlocks, setBlockSelection, toggleBlockSelection } from './selection.ts';\nimport { type Block, type BlockExtent } from './types.ts';\n\nexport type BlockDragOptions = {\n  /**\n   * Enumerates the draggable blocks for a state, in document order. Ranges must be non-overlapping so\n   * the gutter, drop-index, and preview geometry stay unambiguous.\n   */\n  getBlocks: (state: EditorState) => Block[];\n  /** Whether a block gets a grip; a block that fails still selects and moves with a selection. */\n  canDrag?: (state: EditorState, block: Block) => boolean;\n  /**\n   * Moves the blocks at `sourceIndices` to the slot before `dropIndex` (the end of the document when\n   * `dropIndex === blocks.length`), preserving their relative order, as a single edit. `indent` (from\n   * `getDropIndent`, when provided) is the leading-space count the moved block(s) should re-root to.\n   */\n  moveBlocks: (view: EditorView, sourceIndices: number[], dropIndex: number, indent?: number) => void;\n  /**\n   * Resolves the drop target's indentation live during a drag: the leading-space count the dragged block(s)\n   * would re-root to at `dropIndex` for the current `pointerY`, plus the horizontal pixel `offset` to shift\n   * the preview so it previews that level. Omit for blocks whose drop never re-indents (markdown). The\n   * outliner uses it to keep an item in its branch vs. outdenting it to a sibling of an ancestor.\n   */\n  getDropIndent?: (\n    view: EditorView,\n    sourceIndices: number[],\n    dropIndex: number,\n    pointerY: number,\n  ) => { indent: number; offset: number } | null;\n  /**\n   * Pin the drag preview to the block's left edge so it only tracks vertically (default `true`).\n   * When `false`, the preview follows the pointer on both axes.\n   */\n  clampX?: boolean;\n  /**\n   * Maps a block to the range it visually occupies for the collapse/preview/placeholder — its subtree for\n   * the outliner. Defaults to the block's own range (markdown blocks). The grips and drop-index logic still\n   * use `getBlocks` (own, non-overlapping ranges).\n   */\n  getExtent?: BlockExtent;\n  /**\n   * Collapse only the dragged block's content, keeping its trailing line break (default `false`, which\n   * collapses through to the next block, consuming a blank-line separator). Set `true` for single-newline\n   * separated blocks (the outliner): a block-replace that ends at the next line's start makes CodeMirror\n   * attribute that line's `Decoration.line` (its indent) to the removed block, so the following item would\n   * lose its indentation while dragging. Keeping the break ends the collapse at the content, preserving it.\n   */\n  keepTrailingBreak?: boolean;\n};\n\n// Merges sorted ranges, combining any that overlap or touch (nested subtree extents from a parent and its\n// selected child would otherwise produce overlapping block-replace decorations).\nconst mergeRanges = (ranges: { from: number; to: number }[]): { from: number; to: number }[] => {\n  const sorted = [...ranges].sort((a, b) => a.from - b.from);\n  const merged: { from: number; to: number }[] = [];\n  for (const range of sorted) {\n    const last = merged.at(-1);\n    if (last && range.from <= last.to) {\n      last.to = Math.max(last.to, range.to);\n    } else {\n      merged.push({ ...range });\n    }\n  }\n  return merged;\n};\n\n// Space (px) between the text bounds and the box border, applied above and below. Matches the outline\n// layer so the drag preview lifts off exactly over the source block.\nconst BOX_PADDING = 2;\n\n// Amount (px) the preview box extends left of the content. Kept at 0 so the box, the cloned content,\n// and the drop placeholder all share the content's left edge (they otherwise drift by this much).\nconst BOX_INSET_X = 0;\n\n// Fine adjustment of the cloned preview content relative to the source (0 = flush over the source line).\nconst PREVIEW_OFFSET = { x: 0, y: 0 };\n\n// Distance (px) from the viewport's top/bottom edge within which a drag auto-scrolls the editor.\nconst AUTOSCROLL_ZONE = 48;\n\n// Maximum auto-scroll speed (px per animation frame).\nconst AUTOSCROLL_MAX_SPEED = 14;\n\n// Pointer travel (px) past which an armed handle press becomes a drag rather than a click.\nconst DRAG_THRESHOLD = 4;\n\n// The gutters on each side of the content, where the grip (left) and menu (right) float, centered within.\n// `GUTTER_WIDTH` (2rem) is the strip width;\n// `CONTENT_WIDTH` reserves one on each side (`4rem` = 2×2rem) and centers the column — keep the two in sync.\n// Shared by the grip (`drag.ts`), menu (`menu.ts`), and the outliner content (`outliner.ts`).\n// Both utilities need `!`: CodeMirror's own `.ͼ1 .cm-content` rule sets `margin: 0` at a higher\n// specificity, which otherwise drops the centering and collapses both gutters onto one side.\nexport const GUTTER_WIDTH = 32; // 2rem\nexport const CONTENT_WIDTH = 'max-w-[min(50rem,100%-4rem)]! mx-auto!';\n\nconst GRIP_ICON = 'ph--dots-six-vertical--regular';\n\n// A grip's target placement — the POINT it centres on, not its top-left corner — computed in the measure\n// read phase and applied in the write phase. The element is centred on it by a CSS\n// `translate(-50%, -50%)` rather than by subtracting half of a hard-coded size here, so the grip's box can follow\n// the theme without the grip drifting off its row.\ntype GripPosition = { index: number; anchor: number; left: number; top: number };\n\n// The `from` of the block under the pointer (a hovered block shows its grip), or null.\nconst setHoveredBlock = StateEffect.define<number | null>();\nconst hoveredBlockField = StateField.define<number | null>({\n  create: () => null,\n  update: (value, tr) => {\n    for (const effect of tr.effects) {\n      if (effect.is(setHoveredBlock)) {\n        return effect.value;\n      }\n    }\n    return value != null && tr.docChanged ? tr.changes.mapPos(value, -1) : value;\n  },\n});\n\n// Tracks the block under the pointer (by its vertical position, so it works over the text, the gutter,\n// or the margin) and publishes it to `hoveredBlockField` so that block's grip can show. Listens on the\n// scroller — which spans the gutter and content — so moving from the text toward the grip doesn't clear\n// it; the hover clears only when the pointer leaves the editor. Dispatches only when the block changes.\nconst createHoverPlugin = (getBlocks: BlockDragOptions['getBlocks'], dragPlugin: ReturnType<typeof createDragPlugin>) =>\n  ViewPlugin.fromClass(\n    class {\n      constructor(readonly view: EditorView) {\n        view.scrollDOM.addEventListener('mousemove', this.#onMove);\n        view.scrollDOM.addEventListener('mouseleave', this.#onLeave);\n      }\n\n      destroy() {\n        this.view.scrollDOM.removeEventListener('mousemove', this.#onMove);\n        this.view.scrollDOM.removeEventListener('mouseleave', this.#onLeave);\n      }\n\n      #set(anchor: number | null) {\n        if (anchor !== (this.view.state.field(hoveredBlockField, false) ?? null)) {\n          this.view.dispatch({ effects: setHoveredBlock.of(anchor) });\n        }\n      }\n\n      #onMove = (event: MouseEvent) => {\n        // While a drag is in flight the pointer sweeps across rows; updating the hovered block here would\n        // flicker other rows' grips on and off. The drag plugin owns which grip shows during a drag.\n        if (this.view.plugin(dragPlugin)?.dragging) {\n          return;\n        }\n        // Map the pointer's row (content-center x + pointer y) to a block, so any horizontal position works.\n        const contentRect = this.view.contentDOM.getBoundingClientRect();\n        const pos = this.view.posAtCoords({ x: contentRect.left + contentRect.width / 2, y: event.clientY });\n        this.#set(\n          pos == null\n            ? null\n            : (getBlocks(this.view.state).find((block) => pos >= block.from && pos <= block.to)?.from ?? null),\n        );\n      };\n\n      #onLeave = () => this.#set(null);\n    },\n  );\n\n// The single block whose grip is shown: the block under the pointer, else the block at the caret (only\n// one handle is ever visible). Grabbing it drags the whole block selection when it is part of one.\nconst activeBlockIndex = (state: EditorState, getBlocks: BlockDragOptions['getBlocks']): number | null => {\n  const blocks = getBlocks(state);\n  const hovered = state.field(hoveredBlockField, false);\n  if (hovered != null) {\n    const hoverIndex = blocks.findIndex((block) => block.from === hovered);\n    if (hoverIndex >= 0) {\n      return hoverIndex;\n    }\n  }\n  const head = state.selection.main.head;\n  const cursorIndex = blocks.findIndex((block) => head >= block.from && head <= block.to);\n  return cursorIndex >= 0 ? cursorIndex : null;\n};\n\n// Builds a grip element: a control-sized box (the row's hit area, as `Field.Block` centres a control in a\n// row) around a 24px button holding the glyph, the shape of a form's disclosure button. Shared by the floating overlay and the drag preview;\n// callers attach behavior and position the box.\nconst createGripElement = (): HTMLElement =>\n  Domino.of('div')\n    .classNames('cm-blockDragHandle')\n    .append(\n      Domino.of('div')\n        .classNames('grid size-6 place-items-center rounded-xs hover:bg-hover-surface')\n        .append(Domino.of('div').classNames('cm-blockDragHandleIcon').append(Domino.svg(GRIP_ICON))),\n    ).root;\n\n//\n// The in-progress drag's decorations, or `null` when idle: each dragged block (with its trailing blank\n// line) is collapsed out of the document (`collapses`), and a gap (`placeholderPos`) opens at the drop\n// slot so the content closes over the lifted blocks and reopens at the landing spot.\n//\ntype DragDecoState = {\n  collapses: { from: number; to: number }[];\n  placeholderPos: number;\n  // Height of the bordered box (the blocks) and the total reserved height (blocks + trailing blanks); the\n  // difference is empty space below the box, matching the collapsed separators.\n  blockHeight: number;\n  totalHeight: number;\n  // Horizontal pixel offset for the drop level (see `getDropIndent`), so the placeholder lands where the\n  // dragged content lands.\n  indentOffset: number;\n} | null;\n\nconst setDragDeco = StateEffect.define<DragDecoState>();\n\n/**\n * Block-level spacer at the drop position. The outer element reserves the full collapsed height so\n * nothing shifts; the inner box carries the border and matches the blocks, leaving the blanks as empty\n * space below.\n */\nclass PlaceholderWidget extends WidgetType {\n  constructor(\n    readonly blockHeight: number,\n    readonly totalHeight: number,\n    readonly indentOffset: number,\n  ) {\n    super();\n  }\n\n  override eq(other: PlaceholderWidget): boolean {\n    return (\n      other.blockHeight === this.blockHeight &&\n      other.totalHeight === this.totalHeight &&\n      other.indentOffset === this.indentOffset\n    );\n  }\n\n  // Without this the height map assumes a default-height widget until the async measure pass corrects\n  // it, and the correction can shift scroll bookkeeping for a frame at drag start.\n  override get estimatedHeight(): number {\n    return this.totalHeight;\n  }\n\n  override toDOM(): HTMLElement {\n    const element = document.createElement('div');\n    element.className = 'cm-blockDropPlaceholder';\n    element.style.height = `${this.totalHeight}px`;\n    const box = element.appendChild(document.createElement('div'));\n    box.className = 'cm-blockDropPlaceholderBox';\n    box.style.height = `${this.blockHeight}px`;\n    // Shift the box to the drop level (plus the preview's own x nudge) so its left edge aligns exactly with\n    // the dragged content's left edge; the right edge stays at the content's right.\n    const shift = this.indentOffset + PREVIEW_OFFSET.x;\n    box.style.marginLeft = `${shift}px`;\n    box.style.width = `calc(100% - ${shift}px)`;\n    return element;\n  }\n}\n\nconst dragDecoField = StateField.define<DecorationSet>({\n  create: () => Decoration.none,\n  update: (value, tr) => {\n    let next = value.map(tr.changes);\n    for (const effect of tr.effects) {\n      if (effect.is(setDragDeco)) {\n        if (effect.value == null) {\n          next = Decoration.none;\n        } else {\n          const { collapses, placeholderPos, blockHeight, totalHeight, indentOffset } = effect.value;\n          next = Decoration.set(\n            [\n              // Collapse each dragged block (and its trailing blank line) out of the document flow.\n              ...collapses.map((range) => Decoration.replace({ block: true }).range(range.from, range.to)),\n              // Open the gap at the drop slot (before the target line, or after the last line).\n              Decoration.widget({\n                widget: new PlaceholderWidget(blockHeight, totalHeight, indentOffset),\n                block: true,\n                side: placeholderPos >= tr.state.doc.length ? 1 : -1,\n              }).range(placeholderPos),\n            ],\n            // Sort: the placeholder and collapses may interleave.\n            true,\n          );\n        }\n      }\n    }\n    return next;\n  },\n  provide: (field) => EditorView.decorations.from(field),\n});\n\n/** Manages the armed press → click / drag lifecycle, the drop placeholder, and pointer tracking. */\nconst createDragPlugin = (\n  getBlocks: BlockDragOptions['getBlocks'],\n  moveBlocks: BlockDragOptions['moveBlocks'],\n  clampX: boolean,\n  getExtent?: BlockExtent,\n  keepTrailingBreak = false,\n  getDropIndent?: BlockDragOptions['getDropIndent'],\n) =>\n  ViewPlugin.fromClass(\n    class {\n      // Armed but not yet dragging: the press that may become a click or a drag.\n      #armed: { index: number; shiftKey: boolean; x: number; y: number } | null = null;\n\n      // Active drag state (null until a press crosses the drag threshold).\n      #sourceIndices: number[] | null = null;\n      #dropIndex: number | null = null;\n      // Resolved drop indentation for the current pointer (from `getDropIndent`), or null when the block\n      // kind does not re-indent on drop. `offset` shifts the preview to preview the target level.\n      #dropIndent: { indent: number; offset: number } | null = null;\n      #collapseHeights: number[] = [];\n      #placeholderKey: string | null = null;\n      #preview: HTMLElement | null = null;\n      // The cloned content inside the preview; indented (within the fixed container) to preview the drop level.\n      #previewContent: HTMLElement | null = null;\n      #previewOrigin: { left: number; top: number } | null = null;\n      // A grip that rides along with the preview so a drag handle stays visible while dragging (the static\n      // overlay grip is hidden during a drag). `#gripOrigin` is its resting viewport position at grab time.\n      #previewGrip: HTMLElement | null = null;\n      #gripOrigin: { left: number; top: number } | null = null;\n      // The first dragged block's viewport rect (the preview lifts off from here) and the grab point.\n      #blockRect: { left: number; top: number; width: number; height: number; layoutHeight: number } | null = null;\n      #grabX = 0;\n      #grabY = 0;\n      #lastPointer: { clientX: number; clientY: number } | null = null;\n      #scrollFrame: number | null = null;\n\n      constructor(readonly view: EditorView) {}\n\n      // True once a press has crossed the drag threshold and a reorder is in flight.\n      get dragging(): boolean {\n        return this.#sourceIndices != null;\n      }\n\n      // The block indices being dragged (null when not dragging).\n      get sourceIndices(): number[] | null {\n        return this.#sourceIndices;\n      }\n\n      // Abort an in-flight drag when a concurrent/external edit lands — the stored indices would otherwise\n      // resolve against a changed document on drop.\n      update(update: ViewUpdate) {\n        if (this.#sourceIndices && update.docChanged) {\n          this.#stop();\n        }\n      }\n\n      destroy() {\n        // Skip the placeholder-clearing dispatch: destroy can run mid-update where dispatching would throw.\n        this.#stop(false);\n      }\n\n      // Called from the gutter mousedown: arm a press that resolves to a click or a drag.\n      arm(index: number, event: MouseEvent) {\n        this.#armed = { index, shiftKey: event.shiftKey, x: event.clientX, y: event.clientY };\n        window.addEventListener('mousemove', this.#onArmedMove);\n        window.addEventListener('mouseup', this.#onArmedUp);\n      }\n\n      #disarm() {\n        window.removeEventListener('mousemove', this.#onArmedMove);\n        window.removeEventListener('mouseup', this.#onArmedUp);\n        this.#armed = null;\n      }\n\n      #onArmedMove = (event: MouseEvent) => {\n        if (!this.#armed) {\n          return;\n        }\n        if (Math.abs(event.clientX - this.#armed.x) + Math.abs(event.clientY - this.#armed.y) > DRAG_THRESHOLD) {\n          const { index } = this.#armed;\n          this.#disarm();\n          this.#beginDrag(index, event);\n        }\n      };\n\n      #onArmedUp = () => {\n        if (this.#armed) {\n          const { index, shiftKey } = this.#armed;\n          this.#disarm();\n          this.#click(index, shiftKey);\n        }\n      };\n\n      // A press without drag: move the caret to the end of the block (collapsing any text selection) and\n      // update the block selection in the same transaction (so the field doesn't treat the caret move as a\n      // clear). A plain press toggles a single-block selection — selecting only this block, or clearing it\n      // when it is already the sole selection; shift toggles it within the set. The block's `from` is its\n      // selection identity; the caret goes to its `to`.\n      #click(index: number, shiftKey: boolean) {\n        const block = getBlocks(this.view.state)[index];\n        if (!block) {\n          return;\n        }\n        const anchor = block.from;\n        let effect;\n        if (shiftKey) {\n          effect = toggleBlockSelection.of(anchor);\n        } else {\n          const current = this.view.state.field(blockSelectionField, false) ?? [];\n          const isSoleSelection = current.length === 1 && current[0] === anchor;\n          effect = setBlockSelection.of(isSoleSelection ? [] : [anchor]);\n        }\n        this.view.dispatch({ selection: { anchor: block.to }, effects: effect });\n      }\n\n      // Enter drag mode: drag the whole block selection when the grabbed block is part of it, else just\n      // the grabbed block. Grabbing a block outside the current selection shifts the selection to it, so\n      // the dragged block shows as selected immediately.\n      #beginDrag(index: number, event: MouseEvent) {\n        const selected = getSelectedBlocks(this.view.state, getBlocks).map((entry) => entry.index);\n        const indices = selected.length > 1 && selected.includes(index) ? selected : [index];\n        if (!selected.includes(index)) {\n          const block = getBlocks(this.view.state)[index];\n          if (block) {\n            this.view.dispatch({ effects: setBlockSelection.of([block.from]) });\n          }\n        }\n        this.#sourceIndices = indices;\n        this.#dropIndex = indices[0];\n        this.#grabX = event.clientX;\n        this.#grabY = event.clientY;\n        this.#lastPointer = { clientX: event.clientX, clientY: event.clientY };\n        this.#blockRect = this.#measureBlock(indices[0]);\n        this.#collapseHeights = indices.map((sourceIndex) => this.#measureCollapseHeight(sourceIndex));\n        // Measure and clone before the collapse decoration removes the source from the DOM.\n        this.#preview = this.#buildPreview(indices);\n        if (this.#preview) {\n          this.#preview.style.transform = 'none';\n          const rect = this.#preview.getBoundingClientRect();\n          this.#previewOrigin = { left: rect.left, top: rect.top };\n        }\n        // A grip rides with the preview: hidden as the static overlay grip, re-shown here at the same\n        // resting spot (gutter-centered, on the first row) and translated by the pointer delta each move.\n        this.#gripOrigin = this.#measureGripOrigin(indices[0]);\n        if (this.#gripOrigin) {\n          this.#previewGrip = createGripElement();\n          this.#previewGrip.classList.add('cm-blockDragPreviewHandle');\n          this.view.dom.appendChild(this.#previewGrip);\n        }\n        this.#positionPreview(event.clientX, event.clientY);\n        this.#updateDragDeco(this.#dropIndex);\n        this.view.scrollDOM.classList.add('cm-blockDragging');\n        window.addEventListener('mousemove', this.#onMove);\n        window.addEventListener('mouseup', this.#onUp);\n        this.#scrollFrame = requestAnimationFrame(this.#autoScrollTick);\n      }\n\n      #stop(clearPlaceholder = true) {\n        this.#disarm();\n        window.removeEventListener('mousemove', this.#onMove);\n        window.removeEventListener('mouseup', this.#onUp);\n        if (this.#scrollFrame != null) {\n          cancelAnimationFrame(this.#scrollFrame);\n          this.#scrollFrame = null;\n        }\n        this.view.scrollDOM.classList.remove('cm-blockDragging');\n        if (clearPlaceholder && this.#placeholderKey != null) {\n          this.view.dispatch({ effects: setDragDeco.of(null) });\n        }\n        this.#placeholderKey = null;\n        this.#collapseHeights = [];\n        this.#preview?.remove();\n        this.#preview = null;\n        this.#previewContent = null;\n        this.#previewOrigin = null;\n        this.#previewGrip?.remove();\n        this.#previewGrip = null;\n        this.#gripOrigin = null;\n        this.#blockRect = null;\n        this.#sourceIndices = null;\n        this.#dropIndex = null;\n        this.#dropIndent = null;\n        this.#lastPointer = null;\n      }\n\n      // The range the block at `sourceIndex` visually occupies (its subtree for the outliner, else its own\n      // range).\n      #extentOf(sourceIndex: number): Block | null {\n        const block = getBlocks(this.view.state)[sourceIndex];\n        if (!block) {\n          return null;\n        }\n        return getExtent ? getExtent(this.view.state, block) : block;\n      }\n\n      // Start of the first block after `pos`, or the document end when there is none — the point a collapse\n      // extends to so it swallows the trailing blank/separator.\n      #nextStartAfter(pos: number): number {\n        const next = getBlocks(this.view.state).find((block) => block.from > pos);\n        return next ? next.from : this.view.state.doc.length;\n      }\n\n      // Rendered height of the region a block's collapse hides: the block (its extent) plus the blank line\n      // below it.\n      #measureCollapseHeight(sourceIndex: number): number {\n        const extent = this.#extentOf(sourceIndex);\n        const firstRect = extent && this.#lineRect(extent.from);\n        if (!extent || !firstRect) {\n          return 0;\n        }\n        const nextStart = this.#nextStartAfter(extent.to);\n        if (nextStart >= this.view.state.doc.length) {\n          const lastRect = this.#lineRect(extent.to);\n          return lastRect ? lastRect.bottom - firstRect.top : 0;\n        }\n        const nextTop = this.#lineRect(nextStart)?.top;\n        return nextTop != null ? nextTop - firstRect.top : 0;\n      }\n\n      // The `.cm-line` DOM rect at `pos`, or null if the line isn't rendered.\n      #lineRect(pos: number): DOMRect | null {\n        const { node } = this.view.domAtPos(pos);\n        const element = node instanceof Element ? node : node.parentElement;\n        const lineElement = element?.closest('.cm-line');\n        return lineElement ? lineElement.getBoundingClientRect() : null;\n      }\n\n      // The block's box rect in viewport coords, measured from its rendered `.cm-line` rects (the\n      // `lineBlockAt` geometry drifts by a pixel or two). Falls back to `lineBlockAt` when off-screen.\n      #measureBlock(sourceIndex: number): {\n        left: number;\n        top: number;\n        width: number;\n        height: number;\n        layoutHeight: number;\n      } | null {\n        const extent = this.#extentOf(sourceIndex);\n        if (!extent) {\n          return null;\n        }\n\n        const contentRect = this.view.contentDOM.getBoundingClientRect();\n        const firstRect = this.#lineRect(extent.from);\n        const lastRect = this.#lineRect(extent.to);\n        if (firstRect && lastRect) {\n          const layoutHeight = lastRect.bottom - firstRect.top;\n          return {\n            left: contentRect.left - BOX_INSET_X,\n            top: firstRect.top - BOX_PADDING,\n            width: contentRect.width + BOX_INSET_X,\n            height: layoutHeight + BOX_PADDING * 2,\n            layoutHeight,\n          };\n        }\n\n        const firstLine = this.view.lineBlockAt(extent.from);\n        const lastLine = this.view.lineBlockAt(extent.to);\n        const layoutHeight = lastLine.bottom - firstLine.top;\n        return {\n          left: contentRect.left - BOX_INSET_X,\n          top: contentRect.top + firstLine.top - BOX_PADDING,\n          width: contentRect.width + BOX_INSET_X,\n          height: layoutHeight + BOX_PADDING * 2,\n          layoutHeight,\n        };\n      }\n\n      // Clones the dragged blocks' rendered lines into a floating preview inside `.cm-editor`. A single\n      // block is sized to its exact rect (pixel-perfect lift-off); multiple blocks stack at natural height.\n      #buildPreview(sourceIndices: number[]): HTMLElement | null {\n        const { state } = this.view;\n        if (!this.#blockRect) {\n          return null;\n        }\n\n        const preview = document.createElement('div');\n        preview.className = 'cm-blockDragPreview';\n        preview.style.width = `${this.#blockRect.width}px`;\n        if (sourceIndices.length === 1) {\n          preview.style.height = `${this.#blockRect.height}px`;\n        }\n        preview.style.paddingLeft = `${BOX_INSET_X}px`;\n\n        const content = document.createElement('div');\n        content.className = 'cm-lineWrapping';\n        content.style.width = `${this.view.contentDOM.clientWidth}px`;\n        content.style.transform = `translate(${PREVIEW_OFFSET.x}px, ${PREVIEW_OFFSET.y}px)`;\n\n        const seen = new Set<Element>();\n        sourceIndices.forEach((sourceIndex, order) => {\n          const extent = this.#extentOf(sourceIndex);\n          if (!extent) {\n            return;\n          }\n          if (order > 0) {\n            const spacer = content.appendChild(document.createElement('div'));\n            spacer.style.height = `${this.view.defaultLineHeight}px`;\n          }\n          const startLine = state.doc.lineAt(extent.from).number;\n          const endLine = state.doc.lineAt(extent.to).number;\n          for (let lineNumber = startLine; lineNumber <= endLine; lineNumber++) {\n            const { node } = this.view.domAtPos(state.doc.line(lineNumber).from);\n            const element = node instanceof Element ? node : node.parentElement;\n            const lineElement = element?.closest('.cm-line');\n            if (lineElement && !seen.has(lineElement)) {\n              seen.add(lineElement);\n              content.appendChild(lineElement.cloneNode(true));\n            }\n          }\n        });\n\n        preview.appendChild(content);\n        // Held so the drop-level preview indents the content within the (unmoved) container.\n        this.#previewContent = content;\n        return this.view.dom.appendChild(preview);\n      }\n\n      // The grip's resting viewport position for the block at `sourceIndex`: gutter-centered horizontally\n      // (matching the static overlay grip) and centered on the block's first row.\n      #measureGripOrigin(sourceIndex: number): { left: number; top: number } | null {\n        const block = getBlocks(this.view.state)[sourceIndex];\n        const coords = block && this.view.coordsAtPos(block.from);\n        if (!coords) {\n          return null;\n        }\n        const contentRect = this.view.contentDOM.getBoundingClientRect();\n        return {\n          left: contentRect.left - GUTTER_WIDTH / 2,\n          top: (coords.top + coords.bottom) / 2,\n        };\n      }\n\n      #positionPreview(clientX: number, clientY: number) {\n        const dx = clampX ? 0 : clientX - this.#grabX;\n        const dy = clientY - this.#grabY;\n        if (this.#preview && this.#previewOrigin && this.#blockRect) {\n          const left = this.#blockRect.left + dx - this.#previewOrigin.left;\n          const top = this.#blockRect.top + dy - this.#previewOrigin.top;\n          this.#preview.style.transform = `translate(${left}px, ${top}px)`;\n        }\n        // Preview the target indent level (from `getDropIndent`) by indenting the content WITHIN the\n        // container — the container and grip stay put, only the text shifts to show where it will land.\n        if (this.#previewContent) {\n          const indentOffset = this.#dropIndent?.offset ?? 0;\n          this.#previewContent.style.transform = `translate(${PREVIEW_OFFSET.x + indentOffset}px, ${PREVIEW_OFFSET.y}px)`;\n        }\n        if (this.#previewGrip && this.#gripOrigin) {\n          this.#previewGrip.style.left = `${this.#gripOrigin.left + dx}px`;\n          this.#previewGrip.style.top = `${this.#gripOrigin.top + dy}px`;\n        }\n      }\n\n      // The slot the pointer is over: the first non-source block whose vertical midpoint is below the\n      // pointer. The dragged (collapsed) blocks are skipped — including blocks inside a dragged extent\n      // (a parent's children share its collapse but are not source indices) — so the drop gap, and thus\n      // the placeholder, always lands on a visible block, never inside the lifted group where the\n      // collapse decoration would absorb it.\n      #dropIndexAt(clientY: number): number {\n        const blocks = getBlocks(this.view.state);\n        const sources = new Set(this.#sourceIndices ?? []);\n        const extents = [...sources]\n          .map((sourceIndex) => this.#extentOf(sourceIndex))\n          .filter((extent): extent is Block => extent != null);\n        const collapsed = (block: Block) =>\n          extents.some((extent) => block.from >= extent.from && block.from <= extent.to);\n        for (let index = 0; index < blocks.length; index++) {\n          if (sources.has(index) || collapsed(blocks[index])) {\n            continue;\n          }\n          const top = this.view.coordsAtPos(blocks[index].from);\n          const bottom = this.view.coordsAtPos(blocks[index].to);\n          if (!top || !bottom) {\n            continue;\n          }\n          if (clientY < (top.top + bottom.bottom) / 2) {\n            return index;\n          }\n        }\n        return blocks.length;\n      }\n\n      #onMove = (event: MouseEvent) => {\n        this.#lastPointer = { clientX: event.clientX, clientY: event.clientY };\n        this.#updateFromPointer();\n      };\n\n      #updateFromPointer() {\n        if (!this.#lastPointer || this.#sourceIndices == null) {\n          return;\n        }\n        this.#dropIndex = this.#dropIndexAt(this.#lastPointer.clientY);\n        this.#dropIndent =\n          getDropIndent?.(this.view, this.#sourceIndices, this.#dropIndex, this.#lastPointer.clientY) ?? null;\n        this.#updateDragDeco(this.#dropIndex);\n        this.#positionPreview(this.#lastPointer.clientX, this.#lastPointer.clientY);\n      }\n\n      #autoScrollDelta(clientY: number): number {\n        const rect = this.view.scrollDOM.getBoundingClientRect();\n        const fromTop = clientY - rect.top;\n        const fromBottom = rect.bottom - clientY;\n        if (fromTop < AUTOSCROLL_ZONE) {\n          const intensity = Math.min(1, (AUTOSCROLL_ZONE - fromTop) / AUTOSCROLL_ZONE);\n          return -Math.max(1, Math.round(intensity * AUTOSCROLL_MAX_SPEED));\n        }\n        if (fromBottom < AUTOSCROLL_ZONE) {\n          const intensity = Math.min(1, (AUTOSCROLL_ZONE - fromBottom) / AUTOSCROLL_ZONE);\n          return Math.max(1, Math.round(intensity * AUTOSCROLL_MAX_SPEED));\n        }\n        return 0;\n      }\n\n      #autoScrollTick = () => {\n        this.#scrollFrame = null;\n        if (this.#sourceIndices == null || !this.#lastPointer) {\n          return;\n        }\n        const delta = this.#autoScrollDelta(this.#lastPointer.clientY);\n        if (delta !== 0) {\n          const scroller = this.view.scrollDOM;\n          const before = scroller.scrollTop;\n          scroller.scrollTop = before + delta;\n          if (scroller.scrollTop !== before) {\n            this.#updateFromPointer();\n          }\n        }\n        this.#scrollFrame = requestAnimationFrame(this.#autoScrollTick);\n      };\n\n      #onUp = () => {\n        const sources = this.#sourceIndices;\n        const drop = this.#dropIndex;\n        const indent = this.#dropIndent?.indent;\n        this.#stop();\n        if (sources != null && drop != null) {\n          moveBlocks(this.view, sources, drop, indent);\n        }\n      };\n\n      // Collapses the dragged blocks out of the document and opens a gap at the drop slot. Skips the\n      // dispatch when the drop position and collapse set are unchanged, so it only re-renders on a move.\n      #updateDragDeco(dropIndex: number) {\n        const sources = this.#sourceIndices;\n        if (sources == null) {\n          return;\n        }\n        const blocks = getBlocks(this.view.state);\n        // Collapse each dragged block's extent (its subtree for the outliner) plus its trailing separator,\n        // merged so nested/adjacent extents don't produce overlapping block-replace decorations.\n        const collapses = mergeRanges(\n          sources\n            .map((sourceIndex) => {\n              const extent = this.#extentOf(sourceIndex);\n              // Collapse through the separator (next block start) for blank-line separated blocks, or stop\n              // at the content end for single-newline blocks so the next line keeps its decoration (indent).\n              return extent\n                ? { from: extent.from, to: keepTrailingBreak ? extent.to : this.#nextStartAfter(extent.to) }\n                : null;\n            })\n            .filter((range): range is { from: number; to: number } => range != null),\n        );\n        const totalHeight = this.#collapseHeights.reduce((sum, height) => sum + height, 0);\n        const blockHeight =\n          totalHeight - (this.#blockRect ? this.#collapseHeights[0] - this.#blockRect.layoutHeight : 0);\n        if (totalHeight <= 0) {\n          return;\n        }\n\n        // A side:-1 block widget sitting inside a collapse (including at its end) is absorbed by the\n        // block-replace and renders nothing — the gap would vanish for that update. Snap such positions\n        // to that collapse's START, which renders before it — the source's own slot, a no-op drop, so\n        // the position is right.\n        let placeholderPos = dropIndex < blocks.length ? blocks[dropIndex].from : this.view.state.doc.length;\n        const atCollapse = collapses.find((range) => placeholderPos > range.from && placeholderPos <= range.to);\n        if (atCollapse) {\n          placeholderPos = atCollapse.from;\n        }\n        // The drop level (quantized to indent units) shifts the placeholder so it aligns with the content.\n        const indentOffset = this.#dropIndent?.offset ?? 0;\n        const key = `${placeholderPos}:${indentOffset}:${collapses.map((range) => range.from).join(',')}`;\n        if (key === this.#placeholderKey) {\n          return;\n        }\n\n        this.#placeholderKey = key;\n        this.view.dispatch({\n          effects: setDragDeco.of({ collapses, placeholderPos, blockHeight, totalHeight, indentOffset }),\n        });\n      }\n    },\n  );\n\nconst dragTheme = EditorView.theme({\n  // The grip is a floating overlay (see `createGripOverlay`) pinned just left of the content, so it tracks\n  // a centered content column — a CodeMirror gutter is stuck at the scroller edge and can't. Positioned\n  // via fixed coordinates (viewport space), refreshed on layout/scroll. The inner box supplies the hover\n  // affordance; this only pins/sizes it.\n  '.cm-blockDragHandle': {\n    position: 'fixed',\n    zIndex: '5',\n    cursor: 'grab',\n    // `left`/`top` name the centre point; the browser centres the real box on it, whatever its size.\n    transform: 'translate(-50%, -50%)',\n    // A control-sized box centring the button, so the hit area matches a row control.\n    display: 'grid',\n    placeItems: 'center',\n    width: 'var(--dx-control)',\n    height: 'var(--dx-control)',\n  },\n  '.cm-blockDragHandleIcon': {\n    display: 'grid',\n    placeContent: 'center',\n    // `size-3`: the glyph is 1em.\n    fontSize: '0.75rem',\n    color: 'var(--color-fg-muted, currentColor)',\n    opacity: '0.4',\n    transition: 'opacity 0.2s',\n  },\n  '.cm-blockDragHandle:hover .cm-blockDragHandleIcon': {\n    opacity: '1',\n  },\n  // The grip carried by the drag preview: above the preview, fully opaque, non-interactive.\n  '.cm-blockDragPreviewHandle': {\n    zIndex: '7',\n    cursor: 'grabbing',\n    pointerEvents: 'none',\n  },\n  '.cm-blockDragPreviewHandle .cm-blockDragHandleIcon': {\n    opacity: '1',\n  },\n  '.cm-scroller.cm-blockDragging': {\n    cursor: 'grabbing',\n  },\n  // Hide the static overlay grips synchronously at drag start (they are children of the scroller; the\n  // preview grip rides on `view.dom`): the overlay only removes them in the async measure pass, which\n  // would leave both grips visible for a frame.\n  '.cm-scroller.cm-blockDragging .cm-blockDragHandle': {\n    display: 'none',\n  },\n  '.cm-scroller.cm-blockDragging .cm-content': {\n    userSelect: 'none',\n  },\n  // Outer container reserves the full collapsed height (blocks + blanks) but is invisible; the blanks end\n  // up as empty space below the bordered box.\n  '.cm-blockDropPlaceholder': {\n    pointerEvents: 'none',\n  },\n  // Left/width are set inline per drop (they track the drop level so the box aligns with the content).\n  // Unpainted: the drop slot reads as empty space matching the anticipated dropped block.\n  '.cm-blockDropPlaceholderBox': {\n    boxSizing: 'border-box',\n  },\n  '.cm-blockDragPreview': {\n    position: 'absolute',\n    top: '0',\n    left: '0',\n    zIndex: '6',\n    pointerEvents: 'none',\n    boxSizing: 'border-box',\n    overflow: 'hidden',\n    opacity: '0.9',\n    // Matches the height reserved by BOX_PADDING so the content sits symmetrically (no extra bottom gap).\n    padding: `${BOX_PADDING}px 0`,\n    backgroundColor: 'var(--color-base-surface, Canvas)',\n    boxShadow: '0 8px 24px rgb(0 0 0 / 0.35)',\n  },\n});\n\n/**\n * Drag-to-reorder for arbitrary document blocks, with click/shift-click block selection. The gutter shows\n * a grip on the block under the pointer, the block at the caret, and every selected block; pressing a grip\n * toggles the block selection, dragging it reorders the block (or the whole selection). A floating\n * preview, a drop placeholder, and edge auto-scroll accompany the drag. Blocks and reorder semantics come\n * from the caller (see `blocks` for markdown blocks, and the outliner for task lines). Pairs with\n * `createBlockSelection`, which owns the selection state, highlight, and clipboard.\n */\nexport const createBlockDrag = ({\n  getBlocks,\n  canDrag,\n  moveBlocks,\n  clampX = true,\n  getExtent,\n  keepTrailingBreak = false,\n  getDropIndent,\n}: BlockDragOptions): Extension => {\n  const dragPlugin = createDragPlugin(getBlocks, moveBlocks, clampX, getExtent, keepTrailingBreak, getDropIndent);\n  return [\n    dragTheme,\n    dragDecoField,\n    hoveredBlockField,\n    createHoverPlugin(getBlocks, dragPlugin),\n    dragPlugin,\n    createGripOverlay(getBlocks, dragPlugin, canDrag),\n  ];\n};\n\n/**\n * Floating grip overlay, one per active block (caret / hovered / selected), pinned just left of the\n * content's left edge — unlike a CodeMirror gutter (stuck at the scroller edge) it tracks a `max-width` +\n * `mx-auto` centered content column. Positions come from `coordsAtPos` (real DOM geometry, not the height\n * map) and refresh on layout and scroll; presses arm the drag plugin. Grips whose row is scrolled out of\n * the editor are dropped.\n */\nconst createGripOverlay = (\n  getBlocks: BlockDragOptions['getBlocks'],\n  dragPlugin: ReturnType<typeof createDragPlugin>,\n  canDrag?: BlockDragOptions['canDrag'],\n): Extension =>\n  ViewPlugin.fromClass(\n    class {\n      // Grip elements keyed by the block anchor they mark; rebuilt as the active set changes.\n      #grips = new Map<number, HTMLElement>();\n      constructor(readonly view: EditorView) {\n        view.scrollDOM.addEventListener('scroll', this.#onScroll);\n        this.#schedule();\n      }\n\n      update(update: ViewUpdate) {\n        const selectionChanged =\n          update.startState.field(blockSelectionField, false) !== update.state.field(blockSelectionField, false);\n        const hoverChanged =\n          update.startState.field(hoveredBlockField, false) !== update.state.field(hoveredBlockField, false);\n        if (\n          update.geometryChanged ||\n          update.docChanged ||\n          update.viewportChanged ||\n          update.selectionSet ||\n          selectionChanged ||\n          hoverChanged\n        ) {\n          this.#schedule();\n        }\n      }\n\n      destroy() {\n        this.view.scrollDOM.removeEventListener('scroll', this.#onScroll);\n        for (const grip of this.#grips.values()) {\n          grip.remove();\n        }\n        this.#grips.clear();\n      }\n\n      #onScroll = () => this.#schedule();\n\n      // A keyed measure runs in CodeMirror's measure cycle — after layout, including the initial one — so\n      // `coordsAtPos` is valid (unlike a bare `rAF` fired before first layout). Requests coalesce by key.\n      #measure = {\n        key: this,\n        read: (view: EditorView): GripPosition[] => this.#read(view),\n        write: (positions: GripPosition[]) => this.#write(positions),\n      };\n\n      #schedule() {\n        this.view.requestMeasure(this.#measure);\n      }\n\n      #read(view: EditorView): GripPosition[] {\n        // While dragging, the static grip is hidden — the drag preview carries its own handle instead.\n        if (view.plugin(dragPlugin)?.dragging) {\n          return [];\n        }\n        const index = activeBlockIndex(view.state, getBlocks);\n        if (index == null) {\n          return [];\n        }\n        const block = getBlocks(view.state)[index];\n        if (!block || (canDrag && !canDrag(view.state, block))) {\n          return [];\n        }\n        const coords = view.coordsAtPos(block.from);\n        const scrollRect = view.scrollDOM.getBoundingClientRect();\n        // Drop the grip when its first row is scrolled out of the editor viewport.\n        if (!coords || coords.bottom <= scrollRect.top || coords.top >= scrollRect.bottom) {\n          return [];\n        }\n        // Centered within the 3rem gutter immediately left of the content.\n        const contentRect = view.contentDOM.getBoundingClientRect();\n        const left = contentRect.left - GUTTER_WIDTH / 2;\n        return [{ index, anchor: block.from, left, top: (coords.top + coords.bottom) / 2 }];\n      }\n\n      #write(positions: GripPosition[]) {\n        const active = new Set<number>();\n        for (const { index, anchor, left, top } of positions) {\n          active.add(index);\n          let grip = this.#grips.get(index);\n          if (!grip) {\n            grip = this.#createGrip();\n            this.#grips.set(index, grip);\n          }\n          grip.dataset.anchor = String(anchor);\n          grip.style.left = `${left}px`;\n          grip.style.top = `${top}px`;\n        }\n        for (const [index, grip] of this.#grips) {\n          if (!active.has(index)) {\n            grip.remove();\n            this.#grips.delete(index);\n          }\n        }\n      }\n\n      #createGrip(): HTMLElement {\n        const grip = createGripElement();\n        grip.addEventListener('mousedown', (event) => {\n          if (event.button !== 0) {\n            return;\n          }\n          // Resolve the block from the grip's anchor at press time (indices shift as the doc changes).\n          const anchor = Number(grip.dataset.anchor);\n          const sourceIndex = getBlocks(this.view.state).findIndex((entry) => entry.from === anchor);\n          if (sourceIndex < 0) {\n            return;\n          }\n          this.view.plugin(dragPlugin)?.arm(sourceIndex, event);\n          event.preventDefault();\n        });\n        this.view.scrollDOM.appendChild(grip);\n        return grip;\n      }\n    },\n  );\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type EditorState, type Extension, type TransactionSpec } from '@codemirror/state';\nimport { type EditorView } from '@codemirror/view';\n\nimport { createBlockDrag } from './drag.ts';\nimport { type BlockOps, createBlockSelection, createBlockSelectionHighlight, setBlockSelection } from './selection.ts';\nimport { type Block } from './types.ts';\n\nexport type BlockOptions = {\n  /**\n   * Pin the drag preview to the block's left edge so it only tracks vertically (default `true`).\n   * When `false`, the preview follows the pointer on both axes.\n   */\n  clampX?: boolean;\n};\n\n/**\n * Whole-block editing over the top-level markdown blocks: click/shift-click the gutter grip to select,\n * drag-to-reorder, cut/copy/paste, with a border/background behind the selection. Composes\n * `blockSelectionHighlight` (the box behind selected blocks), `blockSelection` (selection state and\n * clipboard), and `blockDrag` (the gutter grip that populates the selection and drag-to-move). The grip\n * lives in `blockDrag` and drives the selection, so these are used together (or via this `blocks()`).\n */\nexport const blocks = ({ clampX }: BlockOptions = {}): Extension => [\n  blockSelectionHighlight(),\n  blockSelection(),\n  blockDrag({ clampX }),\n];\n\n/**\n * Draws a border/background box behind each selected top-level markdown block. See\n * `createBlockSelectionHighlight`.\n */\nexport const blockSelectionHighlight = (): Extension => createBlockSelectionHighlight(findBlocks);\n\n/**\n * Whole-block selection state and block cut/copy/paste over the top-level markdown blocks. The gutter\n * grip gesture that populates the selection is provided by `blockDrag` and the highlight by\n * `blockSelectionHighlight`, so pair them (or use `blocks()`, which composes them). See\n * `createBlockSelection`.\n */\nexport const blockSelection = (): Extension => createBlockSelection(markdownBlockOps);\n\n/**\n * Adds a gutter drag grip to the active markdown block and moves the block (or the whole block\n * selection) on drop. See `createBlockDrag`.\n */\nexport const blockDrag = ({ clampX }: Pick<BlockOptions, 'clampX'> = {}): Extension =>\n  createBlockDrag({ getBlocks: findBlocks, moveBlocks, clampX });\n\n/**\n * Top-level markdown blocks (headings, paragraphs, lists, blockquotes, fenced code, …) from the\n * syntax tree, so a list or code fence moves and boxes as a single unit. Memoized per state — the\n * highlight layer, the gutter, and the drag plugin all query it on the same state.\n */\nconst blockCache = new WeakMap<EditorState, Block[]>();\n\nexport const findBlocks = (state: EditorState): Block[] => {\n  const cached = blockCache.get(state);\n  if (cached) {\n    return cached;\n  }\n\n  const blocks: Block[] = [];\n  const cursor = syntaxTree(state).topNode.cursor();\n  if (cursor.firstChild()) {\n    do {\n      if (cursor.to > cursor.from) {\n        blocks.push({ from: cursor.from, to: cursor.to });\n      }\n    } while (cursor.nextSibling());\n  }\n\n  blockCache.set(state, blocks);\n  return blocks;\n};\n\n// Merges sorted ranges, combining any that overlap or touch.\nconst mergeRanges = (ranges: { from: number; to: number }[]): { from: number; to: number }[] => {\n  const sorted = [...ranges].sort((a, b) => a.from - b.from);\n  const merged: { from: number; to: number }[] = [];\n  for (const range of sorted) {\n    const last = merged.at(-1);\n    if (last && range.from <= last.to) {\n      last.to = Math.max(last.to, range.to);\n    } else {\n      merged.push({ ...range });\n    }\n  }\n  return merged;\n};\n\n// Delete ranges for the given blocks: each block plus its blank-line separator (the one below it, down to\n// the next block; for the last block the one above it instead, so no trailing blank is orphaned), merged\n// so adjacent selections form one range.\nconst deleteRanges = (state: EditorState, blocks: Block[], indices: number[]): { from: number; to: number }[] => {\n  const count = blocks.length;\n  return mergeRanges(\n    indices.map((index) => ({\n      from: index === count - 1 && index > 0 ? blocks[index - 1].to : blocks[index].from,\n      to: index + 1 < count ? blocks[index + 1].from : state.doc.length,\n    })),\n  );\n};\n\n/**\n * Transaction that moves the blocks at `sourceIndices` to the slot before `dropIndex` (the end of the\n * document when `dropIndex === blocks.length`), preserving their relative order and blank-line\n * separation, as a single undo step. Places the caret at the end of the moved content and clears the\n * block selection. Returns `null` for a no-op (empty/invalid sources, or a drop inside the moved region).\n * Pure — exported for tests.\n */\nexport const moveBlocksSpec = (\n  state: EditorState,\n  sourceIndices: number[],\n  dropIndex: number,\n): TransactionSpec | null => {\n  const blocks = findBlocks(state);\n  const count = blocks.length;\n  const sources = [...new Set(sourceIndices)].filter((index) => index >= 0 && index < count).sort((a, b) => a - b);\n  // Dropping onto a selected block is a no-op.\n  if (sources.length === 0 || sources.includes(dropIndex)) {\n    return null;\n  }\n\n  const text = sources.map((index) => state.doc.sliceString(blocks[index].from, blocks[index].to)).join('\\n\\n');\n  const deletes = deleteRanges(state, blocks, sources);\n  const insertAt = dropIndex < count ? blocks[dropIndex].from : state.doc.length;\n  // Dropping inside the removed region is a no-op.\n  if (deletes.some((range) => insertAt > range.from && insertAt < range.to)) {\n    return null;\n  }\n\n  const insert = dropIndex < count ? `${text}\\n\\n` : `\\n\\n${text}`;\n  const changes = [...deletes.map((range) => ({ from: range.from, to: range.to })), { from: insertAt, insert }];\n  // The inserted text starts where `insertAt` maps to (dropping any deletes before it), then `text` sits\n  // at its start (or after the `\\n\\n` prefix when appending).\n  const changeSet = state.changes(changes);\n  const textStart = changeSet.mapPos(insertAt, -1) + (dropIndex < count ? 0 : 2);\n  // Keep the moved blocks selected: their new anchors are the start of each block within the inserted\n  // text, joined by `\\n\\n` (block length + 2). The caret lands at the end of the moved content.\n  const anchors: number[] = [];\n  let anchor = textStart;\n  for (const index of sources) {\n    anchors.push(anchor);\n    anchor += blocks[index].to - blocks[index].from + 2;\n  }\n  const caret = textStart + text.length;\n  return {\n    changes,\n    selection: { anchor: Math.min(caret, changeSet.newLength) },\n    effects: setBlockSelection.of(anchors),\n    userEvent: 'move.block',\n  };\n};\n\nconst moveBlocks = (view: EditorView, sourceIndices: number[], dropIndex: number): void => {\n  const spec = moveBlocksSpec(view.state, sourceIndices, dropIndex);\n  if (spec) {\n    view.dispatch(spec);\n  }\n};\n\n/**\n * Transaction that removes the blocks at `indices` and, when `text` is non-null, inserts it as block(s)\n * at the first removed slot — a single edit. `null` is a pure delete (cut); non-null is a replace\n * (paste). Returns `null` for a no-op. Pure — exported for tests.\n */\nexport const replaceBlocksSpec = (\n  state: EditorState,\n  indices: number[],\n  text: string | null,\n): TransactionSpec | null => {\n  const blocks = findBlocks(state);\n  const count = blocks.length;\n  const sources = [...new Set(indices)].filter((index) => index >= 0 && index < count).sort((a, b) => a - b);\n  if (sources.length === 0) {\n    return null;\n  }\n\n  const deletes = deleteRanges(state, blocks, sources);\n  const changes: { from: number; to: number; insert?: string }[] = deletes.map((range) => ({\n    from: range.from,\n    to: range.to,\n  }));\n  let anchor = deletes[0].from;\n  if (text != null && text.length > 0) {\n    // Replace the first removed range with the pasted text (keeping a trailing blank unless at doc end).\n    const insert = deletes[0].to >= state.doc.length ? text : `${text}\\n\\n`;\n    changes[0] = { from: deletes[0].from, to: deletes[0].to, insert };\n    anchor = deletes[0].from + text.length;\n  }\n\n  const changeSet = state.changes(changes);\n  return {\n    changes,\n    selection: { anchor: Math.min(anchor, changeSet.newLength) },\n    effects: setBlockSelection.of([]),\n    userEvent: text == null ? 'delete.block' : 'input.paste',\n  };\n};\n\nconst replaceBlocks = (view: EditorView, indices: number[], text: string | null): void => {\n  const spec = replaceBlocksSpec(view.state, indices, text);\n  if (spec) {\n    view.dispatch(spec);\n  }\n};\n\nexport const markdownBlockOps: BlockOps = {\n  getBlocks: findBlocks,\n  moveBlocks,\n  serialize: (state, blocks) => blocks.map((block) => state.doc.sliceString(block.from, block.to)).join('\\n\\n'),\n  replaceBlocks,\n};\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { codeFolding, foldGutter } from '@codemirror/language';\nimport { type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\nimport { Domino, mx } from '@dxos/ui';\n\n/**\n * https://codemirror.net/examples/gutter\n */\nexport const folding = (): Extension => {\n  return [\n    codeFolding({\n      placeholderDOM: () => Domino.of('span').root,\n    }),\n    foldGutter({\n      // NOTE: We can't animate since the element is remounted on state change.\n      markerDOM: (open) => {\n        return Domino.of('div')\n          .classNames('flex h-full justify-center items-center')\n          .append(\n            Domino.of('svg', Domino.SVG)\n              .classNames(mx('w-4 h-4 cursor-pointer', open && 'rotate-90'))\n              .append(\n                Domino.of('use', Domino.SVG).attributes({\n                  href: Domino.icon('ph--caret-right--regular'),\n                }),\n              ),\n          ).root;\n      },\n    }),\n    styles,\n  ];\n};\n\nconst styles = EditorView.theme({\n  '.cm-foldGutter': {\n    opacity: 0.3,\n    transition: 'opacity 0.3s',\n    width: '32px',\n  },\n  '.cm-foldGutter:hover': {\n    opacity: 1,\n  },\n});\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { codeFolding, foldedRanges, foldEffect, unfoldEffect } from '@codemirror/language';\nimport { type EditorState, type Extension, RangeSetBuilder } from '@codemirror/state';\nimport { EditorView, GutterMarker, gutter } from '@codemirror/view';\n\nimport { Domino } from '@dxos/ui';\n\n/**\n * Turn folding.\n *\n * Makes each turn's response a foldable region. `codeFolding` (from `@codemirror/language`) hides the\n * folded ranges and renders the placeholder, but folds are driven **only** by this extension's own\n * gutter — never through CodeMirror's `foldable()`/`foldGutter`/`foldService`. That matters because\n * `foldable()` unconditionally falls back to the language's `syntaxFolding` (markdown `foldNodeProp`\n * — headings, fenced code, …) whenever no fold service matches a line; routing folds through\n * `foldEffect` on our own ranges instead means only turns from the caller-provided {@link TurnSource}\n * can ever fold. The section model (scan text, read a tree, derive from a message array) lives with\n * the host and can change without touching the fold UI.\n *\n * Folding starts at the end of a turn's head, so the head (e.g. the user prompt) stays visible while\n * its response collapses.\n */\n\n/** A foldable turn response. */\nexport type Turn = {\n  /** Fold range start — the end of the head line, so the head stays visible. */\n  from: number;\n  /** Fold range end — the end of the response that follows the head. */\n  to: number;\n  /** Start offset of the head line; the gutter marker anchors here. */\n  headLineFrom: number;\n};\n\n/** Yields the foldable turn ranges for a document. */\nexport type TurnSource = (state: EditorState) => Turn[];\n\nexport type TurnFoldingOptions = {\n  /** Source of foldable ranges (e.g. scanned from text or derived from the message model). */\n  source: TurnSource;\n};\n\n/** Turn-response folding driven by a custom gutter and TurnSource, bypassing markdown syntax folding. */\nexport const turnFolding = ({ source }: TurnFoldingOptions): Extension => {\n  // The gutter's `markers` runs per update; memoize the per-state turn scan.\n  const cache = new WeakMap<EditorState, Turn[]>();\n  const turnsOf = (state: EditorState): Turn[] => {\n    let turns = cache.get(state);\n    if (!turns) {\n      turns = source(state);\n      cache.set(state, turns);\n    }\n    return turns;\n  };\n\n  return [\n    // Hides folded ranges + renders the placeholder; folds are dispatched only by the gutter below.\n    codeFolding({\n      placeholderDOM: () => Domino.of('span').classNames('hidden').root,\n    }),\n    gutter({\n      class: 'cm-turn-gutter',\n      markers: (view) => {\n        const builder = new RangeSetBuilder<GutterMarker>();\n        for (const turn of turnsOf(view.state)) {\n          builder.add(turn.headLineFrom, turn.headLineFrom, new TurnMarker(isFolded(view.state, turn)));\n        }\n        return builder.finish();\n      },\n      // A fold toggle changes neither the doc nor the viewport, so re-measure on our fold effects\n      // to keep the caret direction in sync.\n      lineMarkerChange: (update) =>\n        update.transactions.some((tr) => tr.effects.some((effect) => effect.is(foldEffect) || effect.is(unfoldEffect))),\n      domEventHandlers: {\n        mousedown: (view, line) => {\n          const turn = turnsOf(view.state).find((candidate) => candidate.headLineFrom === line.from);\n          if (!turn) {\n            return false;\n          }\n          const effect = isFolded(view.state, turn) ? unfoldEffect : foldEffect;\n          view.dispatch({ effects: effect.of({ from: turn.from, to: turn.to }) });\n          return true;\n        },\n      },\n    }),\n    styles,\n  ];\n};\n\n/** Whether `turn`'s range is currently folded. */\nconst isFolded = (state: EditorState, turn: Turn): boolean => {\n  let folded = false;\n  foldedRanges(state).between(turn.from, turn.to, (from, to) => {\n    if (from <= turn.from && to >= turn.to) {\n      folded = true;\n    }\n  });\n  return folded;\n};\n\n/** Folds every turn from `source` (used for a host \"collapse all\"). Folds only turns, never syntax. */\nexport const foldTurns = (view: EditorView, source: TurnSource): void => {\n  const effects = source(view.state)\n    .filter((turn) => turn.to > turn.from)\n    .map((turn) => foldEffect.of({ from: turn.from, to: turn.to }));\n  if (effects.length > 0) {\n    view.dispatch({ effects });\n  }\n};\n\n/** Unfolds every turn from `source` (used for a host \"expand all\"). */\nexport const unfoldTurns = (view: EditorView, source: TurnSource): void => {\n  const effects = source(view.state).map((turn) => unfoldEffect.of({ from: turn.from, to: turn.to }));\n  if (effects.length > 0) {\n    view.dispatch({ effects });\n  }\n};\n\n/** Gutter marker for a turn head: a caret whose direction reflects the fold state. */\nclass TurnMarker extends GutterMarker {\n  #folded: boolean;\n\n  constructor(folded: boolean) {\n    super();\n    this.#folded = folded;\n  }\n\n  override eq(other: TurnMarker): boolean {\n    return this.#folded === other.#folded;\n  }\n\n  override toDOM(): HTMLElement {\n    return Domino.of('div')\n      .classNames('flex h-full items-center justify-center cursor-pointer')\n      .append(\n        Domino.of('svg', Domino.SVG)\n          .classNames('w-4 h-4')\n          .append(\n            Domino.of('use', Domino.SVG).attributes({\n              href: Domino.icon(this.#folded ? 'ph--caret-right--regular' : 'ph--caret-down--regular'),\n            }),\n          ),\n      ).root;\n  }\n}\n\n/**\n * Default head element: a `<prompt>…</prompt>` element (content is escaped, so it never nests),\n * plus any run of self-closing tags immediately following it.\n *\n * Those trailing tags are per-turn affordances belonging to the head — a toolbar rendered under the\n * prompt, say — so they must stay visible when the response folds. Absorbing them into the head also\n * keeps the fold starting below them, rather than swallowing the toolbar that triggers the fold.\n */\nexport const PROMPT_ELEMENT = /<prompt\\b[^>]*>[\\s\\S]*?<\\/prompt>(?:\\s*<[a-zA-Z][\\w-]*\\b[^>]*\\/>)*/g;\n\n/**\n * Builds a {@link TurnSource} that scans the document for head elements matching `element`, folding\n * the response after each — from the end of that head's line up to the next head (or the end of the\n * document), trailing whitespace trimmed. `element` selects the turn head (e.g. {@link PROMPT_ELEMENT}\n * for `<prompt>`); pass another pattern to key turns off a different tag.\n */\nexport const createTurnSource = (element: RegExp): TurnSource => {\n  // Clone with the global flag so `exec` iterates (and never shares `lastIndex` with the caller).\n  const pattern = new RegExp(element.source, element.flags.includes('g') ? element.flags : `${element.flags}g`);\n  return (state) => {\n    const doc = state.doc;\n    const text = doc.toString();\n    const heads: number[] = [];\n    let match: RegExpExecArray | null;\n    pattern.lastIndex = 0;\n    while ((match = pattern.exec(text)) !== null) {\n      heads.push(match.index, match.index + match[0].length);\n    }\n\n    const turns: Turn[] = [];\n    for (let index = 0; index < heads.length; index += 2) {\n      const headStart = heads[index];\n      const headEnd = heads[index + 1];\n      // Fold starts after the head's last line (so the whole head stays visible), but the gutter\n      // marker anchors to the head's first line so the toggle sits at the top of a multi-line head.\n      const from = doc.lineAt(headEnd).to;\n      const headLineFrom = doc.lineAt(headStart).from;\n      const nextHeadStart = index + 2 < heads.length ? heads[index + 2] : doc.length;\n      let to = nextHeadStart;\n      while (to > from && /\\s/.test(text[to - 1])) {\n        to--;\n      }\n      if (to > from) {\n        turns.push({ from, to, headLineFrom });\n      }\n    }\n    return turns;\n  };\n};\n\nconst styles = EditorView.theme({\n  '.cm-turn-gutter': {\n    width: '1.5rem',\n    color: 'var(--dx-description, currentColor)',\n    opacity: '0.4',\n    transition: 'opacity 0.2s',\n  },\n  '.cm-turn-gutter:hover': {\n    opacity: '1',\n  },\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { ensureSyntaxTree, syntaxTree } from '@codemirror/language';\nimport { type EditorState, type Extension, StateField, type Transaction } from '@codemirror/state';\nimport { Facet } from '@codemirror/state';\nimport { type SyntaxNode } from '@lezer/common';\n\nimport { invariant } from '@dxos/invariant';\n\nimport { type Range } from '../../../types/index.ts';\n\n/**\n * Represents a single item in the tree.\n */\nexport interface Item {\n  type: 'root' | 'bullet' | 'task' | 'unknown';\n  index: number;\n  level: number;\n  node: SyntaxNode;\n  parent?: Item;\n  nextSibling?: Item;\n  prevSibling?: Item;\n  children: Item[];\n  /**\n   * Actual range.\n   * Starts at the start of the line containing the item and ends at the end of the line before the\n   * first child or next sibling.\n   */\n  lineRange: Range;\n  /**\n   * Range of the editable content.\n   * This doesn't include the list or task marker or indentation.\n   */\n  contentRange: Range;\n}\n\nexport const itemToJSON = ({ type, index, level, lineRange, contentRange, children }: Item): any => {\n  return { type, index, level, lineRange, contentRange, children: children.map(itemToJSON) };\n};\n\n/**\n * Tree assumes the entire document is a single contiguous well-formed hierarchy of markdown LiteItem nodes.\n */\nexport class Tree implements Item {\n  type: Item['type'] = 'root';\n  index = -1;\n  level = -1;\n  node: Item['node'];\n  lineRange: Item['lineRange'];\n  contentRange: Item['contentRange'];\n  children: Item['children'] = [];\n\n  constructor(node: SyntaxNode) {\n    this.node = node;\n    this.lineRange = { from: node.from, to: node.to };\n    this.contentRange = this.lineRange;\n  }\n\n  toJSON() {\n    return itemToJSON(this);\n  }\n\n  get root(): Item {\n    return this;\n  }\n\n  traverse<T = any>(cb: (item: Item, level: number) => T | void): T | undefined;\n  traverse<T = any>(item: Item, cb: (item: Item, level: number) => T | void): T | undefined;\n  traverse<T = any>(\n    itemOrCb: Item | ((item: Item, level: number) => T | undefined | void),\n    maybeCb?: (item: Item, level: number) => T | undefined | void,\n  ): T | undefined {\n    if (typeof itemOrCb === 'function') {\n      return traverse<T>(this, itemOrCb);\n    } else {\n      return traverse<T>(itemOrCb, maybeCb!);\n    }\n  }\n\n  /**\n   * Return the closest item.\n   */\n  find(pos: number): Item | undefined {\n    return this.traverse<Item>((item) => (item.lineRange.from <= pos && item.lineRange.to >= pos ? item : undefined));\n  }\n\n  /**\n   * Return the first child, next sibling, or parent's next sibling.\n   */\n  next(item: Item, enter = true): Item | undefined {\n    if (enter && item.children.length > 0) {\n      return item.children[0];\n    }\n\n    if (item.nextSibling) {\n      return item.nextSibling;\n    }\n\n    if (item.parent) {\n      return this.next(item.parent, false);\n    }\n\n    return undefined;\n  }\n\n  /**\n   * Return the previous sibling, or parent.\n   */\n  prev(item: Item): Item | undefined {\n    if (item.prevSibling) {\n      return this.lastDescendant(item.prevSibling);\n    }\n\n    return item.parent?.type === 'root' ? undefined : item.parent;\n  }\n\n  /**\n   * Return the last descendant of the item, or the item itself if it has no children.\n   */\n  lastDescendant(item: Item): Item {\n    return item.children.length > 0 ? this.lastDescendant(item.children.at(-1)!) : item;\n  }\n}\n\nexport const getRange = (tree: Tree, item: Item): [number, number] => {\n  const lastDescendant = tree.lastDescendant(item);\n  return [item.lineRange.from, lastDescendant.lineRange.to];\n};\n\n/**\n * Traverse the tree, calling the callback for each item.\n * If the callback returns a value, the traversal is stopped and the value is returned.\n */\nexport const traverse = <T = any>(root: Item, cb: (item: Item, level: number) => T | void): T | undefined => {\n  const t = (item: Item, level: number): T | undefined => {\n    if (item.type !== 'root') {\n      const value = cb(item, level);\n      if (value != null) {\n        return value;\n      }\n    }\n\n    for (const child of item.children) {\n      const value = t(child, level + 1);\n      if (value != null) {\n        return value;\n      }\n    }\n\n    return undefined;\n  };\n\n  return t(root, root.type === 'root' ? -1 : 0);\n};\n\nexport const getListItemContent = (state: EditorState, item: Item): string => {\n  return state.doc.sliceString(item.contentRange.from, item.contentRange.to);\n};\n\nexport const listItemToString = (item: Item, level = 0) => {\n  const indent = '  '.repeat(level);\n  const data = {\n    i: item.index,\n    n: item.nextSibling?.index ?? '∅',\n    p: item.prevSibling?.index ?? '∅',\n    level: item.level,\n    node: format([item.node.from, item.node.to]),\n    line: format([item.lineRange.from, item.lineRange.to]),\n    content: format([item.contentRange.from, item.contentRange.to]),\n  };\n\n  return `${indent}${item.type[0].toUpperCase()}(${Object.entries(data)\n    .map(([k, v]) => `${k}=${v}`)\n    .join(', ')})`;\n};\n\nconst format = (value: any) =>\n  JSON.stringify(value, (key: string, value: any) => {\n    if (typeof value === 'number') {\n      return value.toString().padStart(3, ' ');\n    }\n    return value;\n  }).replaceAll('\"', '');\n\nexport const treeFacet = Facet.define<Tree, Tree>({\n  combine: (values) => values[0],\n});\n\nexport type TreeOptions = {};\n\n/**\n * Creates a shadow tree of `ListItem` nodes whenever the document changes.\n * This adds overhead relative to the markdown AST, but allows for efficient traversal of the list items.\n * NOTE: Requires markdown parser to be enabled.\n */\nexport const outlinerTree = (_options: TreeOptions = {}): Extension => {\n  // Outlines are small, so the whole document is parsed before the tree is read: a partial parse leaves\n  // the last item with its content start inside the marker, and anything positioned by it lands under\n  // the checkbox.\n  const PARSE_BUDGET_MS = 50;\n\n  const buildTree = (state: EditorState): Tree => {\n    let tree: Tree | undefined;\n    let parent: Item | undefined;\n    let current: Item | undefined;\n    let prev: Item | undefined;\n    let level = -1;\n    let index = -1;\n\n    // Array to track previous siblings at each level.\n    const prevSiblings: (Item | undefined)[] = [];\n\n    (ensureSyntaxTree(state, state.doc.length, PARSE_BUDGET_MS) ?? syntaxTree(state)).iterate({\n      enter: (node) => {\n        switch (node.name) {\n          case 'Document': {\n            tree = new Tree(node.node);\n            current = tree;\n            break;\n          }\n          case 'BulletList': {\n            invariant(current);\n            invariant(tree);\n            // A list under the document is its own island: it hangs off the root beside earlier lists\n            // rather than under the last item parsed, and the prose between lists belongs to no item.\n            parent = node.node.parent?.name === 'Document' ? tree : current;\n            if (parent === tree) {\n              tree.lineRange.to = tree.node.from;\n            } else {\n              current.lineRange.to = current.node.from;\n            }\n            prevSiblings[++level] = undefined;\n            break;\n          }\n          case 'ListItem': {\n            invariant(parent);\n\n            // Include all content up to the next sibling, or to the end of the enclosing list: what\n            // follows the list (prose, another list) is not this item's continuation.\n            const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;\n            const listEnd = node.node.parent?.to ?? state.doc.length;\n            const docRange: Range = {\n              from: state.doc.lineAt(node.from).from,\n              to: nextSibling ? Math.min(nextSibling.from - 1, listEnd) : listEnd,\n            };\n\n            current = {\n              type: 'unknown',\n              index: ++index,\n              level,\n              node: node.node,\n              lineRange: docRange,\n              contentRange: { ...docRange },\n              parent,\n              prevSibling: prevSiblings[level],\n              children: [],\n            };\n\n            // Update sibling refs.\n            if (current.prevSibling) {\n              current.prevSibling.nextSibling = current;\n            }\n\n            // Update previous siblings array at current level.\n            prevSiblings[level] = current;\n\n            // Update previous item (not sibling); never past its own list's end.\n            if (prev) {\n              prev.lineRange.to = prev.contentRange.to = Math.min(prev.lineRange.to, current.lineRange.from - 1);\n            }\n            prev = current;\n\n            // Update parent.\n            parent.children.push(current);\n            if (parent.lineRange.to === parent.node.from) {\n              parent.lineRange.to = parent.contentRange.to = current.lineRange.from - 1;\n            }\n\n            break;\n          }\n          case 'ListMark': {\n            invariant(current);\n            current.type = 'bullet';\n            // Content starts after the marker and its space; a bare `-` being typed has no space yet, so\n            // the start is clamped to the item's end rather than pointing past the document.\n            current.contentRange.from = Math.min(node.to + 1, current.contentRange.to);\n            break;\n          }\n          case 'Task': {\n            invariant(current);\n            current.type = 'task';\n            break;\n          }\n          case 'TaskMarker': {\n            invariant(current);\n            current.contentRange.from = Math.min(node.to + 1, current.contentRange.to);\n            break;\n          }\n        }\n      },\n      leave: (node) => {\n        if (node.name === 'BulletList') {\n          invariant(parent);\n          prevSiblings[level--] = undefined;\n          parent = parent.parent;\n        }\n      },\n    });\n\n    invariant(tree);\n    return tree;\n  };\n\n  return [\n    StateField.define<Tree | undefined>({\n      create: (state) => {\n        return buildTree(state);\n      },\n      update: (value: Tree | undefined, tr: Transaction) => {\n        // The parser also finishes asynchronously in a transaction of its own, with no document change.\n        if (!tr.docChanged && syntaxTree(tr.state) === syntaxTree(tr.startState)) {\n          return value;\n        }\n\n        return buildTree(tr.state);\n      },\n      provide: (field) => treeFacet.from(field),\n    }),\n  ];\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { getIndentUnit } from '@codemirror/language';\nimport { type EditorState, type Extension } from '@codemirror/state';\nimport { type Command, EditorView } from '@codemirror/view';\n\nimport { bulletListIndentationWidth } from '../../language/markdown/styles.ts';\nimport {\n  type Block,\n  type BlockOps,\n  blockSelectionField,\n  createBlockDrag,\n  createBlockSelection,\n  setBlockSelection,\n} from '../blocks/index.ts';\nimport { type Item, type Tree, getRange, treeFacet } from './tree.ts';\n\n/**\n * Flattens the tree into draggable units in document order, one per item. Each unit spans only the\n * item's own lines (up to its first child), so the ranges are non-overlapping and every item — parent\n * or leaf — gets its own gutter handle. Memoized per state so the gutter and drag plugin share it.\n */\nconst itemCache = new WeakMap<EditorState, Item[]>();\nconst flattenItems = (state: EditorState): Item[] => {\n  const cached = itemCache.get(state);\n  if (cached) {\n    return cached;\n  }\n\n  const tree = state.facet(treeFacet);\n  const items: Item[] = [];\n  tree.traverse((item) => {\n    items.push(item);\n  });\n\n  itemCache.set(state, items);\n  return items;\n};\n\nconst getBlocks = (state: EditorState): Block[] =>\n  flattenItems(state).map((item) => ({\n    from: item.lineRange.from,\n    // The item's own lines end just before its first child (or at its line range end for a leaf).\n    to: item.children.length > 0 ? item.children[0].lineRange.from - 1 : item.lineRange.to,\n  }));\n\n// The range a block visually occupies for selection/drag: its whole subtree (item + descendants), so\n// selecting or dragging a parent covers its children. `getBlocks` still yields per-item own-lines.\n// Exported for tests.\nexport const getExtent = (state: EditorState, block: Block): Block => {\n  const tree = state.facet(treeFacet);\n  const item = tree.find(block.from);\n  if (!item) {\n    return block;\n  }\n  const [from, to] = getRange(tree, item);\n  return { from, to };\n};\n\n/** Leading-whitespace width of the line at `pos` (indentation depth in characters). */\nconst indentAt = (state: EditorState, pos: number): number => {\n  const text = state.doc.lineAt(pos).text;\n  return text.length - text.trimStart().length;\n};\n\n/**\n * Resolves the drop indentation for a single-item drag (see DESIGN \"Drag reindent — drop level rules\").\n * The dragged subtree stays in the branch of the item above the drop slot by default, outdenting to a\n * sibling of the (shallower) drop target only while the pointer is over that target's row. Returns the\n * target leading-space count and the horizontal pixel offset that previews it. Exported for tests.\n */\nexport const getDropIndent = (\n  view: EditorView,\n  sourceIndices: number[],\n  dropIndex: number,\n  pointerY: number,\n): { indent: number; offset: number } | null => {\n  // Multi-item group moves keep their own relative indentation (no single re-root).\n  if (sourceIndices.length !== 1) {\n    return null;\n  }\n\n  const { state } = view;\n  const items = flattenItems(state);\n  const count = items.length;\n  const unit = getIndentUnit(state);\n  const sourceItem = items[sourceIndices[0]];\n  const sourceIndent = indentAt(state, sourceItem.lineRange.from);\n\n  // The item directly above the drop slot, skipping the dragged subtree (the source's children are\n  // collapsed with it, so they cannot serve as the indent reference).\n  const [sourceFrom, sourceTo] = getRange(state.facet(treeFacet), sourceItem);\n  let aboveIndex = dropIndex - 1;\n  while (\n    aboveIndex >= 0 &&\n    items[aboveIndex].lineRange.from >= sourceFrom &&\n    items[aboveIndex].lineRange.from <= sourceTo\n  ) {\n    aboveIndex--;\n  }\n  const above = aboveIndex >= 0 ? items[aboveIndex] : null;\n  const target = dropIndex < count ? items[dropIndex] : null;\n\n  const aboveIndent = above ? indentAt(state, above.lineRange.from) : 0;\n  // Valid range for the slot: a sibling of the target (shallowest) up to a child of the item above.\n  const minIndent = target ? indentAt(state, target.lineRange.from) : 0;\n  const maxIndent = above ? aboveIndent + unit : minIndent;\n  const clamp = (value: number) => Math.max(minIndent, Math.min(maxIndent, value));\n\n  // Default: keep the dragged item at its own level (staying in its branch), clamped to the slot's range.\n  let indent = clamp(sourceIndent);\n  // Outdent to a sibling of the target only at a real branch boundary and while over the target row.\n  if (target && minIndent < indent) {\n    const top = view.coordsAtPos(target.lineRange.from)?.top;\n    if (top != null && pointerY >= top) {\n      indent = minIndent;\n    }\n  }\n\n  // Preview offset: one indentation width per indent unit of delta from the source's own indent.\n  const offset = ((indent - sourceIndent) / unit) * bulletListIndentationWidth;\n  return { indent, offset };\n};\n\n/**\n * Re-roots `text` (a moved subtree) from `fromIndent` to `toIndent` by shifting every line's leading\n * whitespace, preserving the subtree's internal relative indentation. Keeps the moved item valid at\n * its new depth (e.g. dragging a nested task to the top level).\n */\nconst reindent = (text: string, fromIndent: number, toIndent: number): string => {\n  const delta = toIndent - fromIndent;\n  if (delta === 0) {\n    return text;\n  }\n  return text\n    .split('\\n')\n    .map((line) => {\n      const existing = line.length - line.trimStart().length;\n      const next = Math.max(0, existing + delta);\n      return ' '.repeat(next) + line.slice(existing);\n    })\n    .join('\\n');\n};\n\n/**\n * Moves the item at `sourceIndex` (with its whole subtree) to the slot before `dropIndex`, or to the\n * end of the document when `dropIndex === items.length`. Expressed as a minimal delete + insert so it\n * is a single undo step and syncs as a small edit.\n */\nconst moveItem = (view: EditorView, sourceIndex: number, dropIndex: number, indent?: number): void => {\n  const { state } = view;\n  const tree: Tree = state.facet(treeFacet);\n  const items = flattenItems(state);\n  const count = items.length;\n  if (sourceIndex < 0 || sourceIndex >= count || dropIndex === sourceIndex) {\n    return;\n  }\n\n  const source = items[sourceIndex];\n  const [from, to] = getRange(tree, source);\n\n  // Insert before the drop-target item, or append at the end.\n  const insertAt = dropIndex < count ? items[dropIndex].lineRange.from : state.doc.length;\n  // Dropping inside the item's own subtree (including immediately after its own line) is a no-op.\n  if (insertAt > from && insertAt <= to) {\n    return;\n  }\n\n  const sourceIndent = indentAt(state, from);\n  // The drop level resolved live during the drag (`getDropIndent`), else the drop-target's own indent.\n  const targetIndent = indent ?? (dropIndex < count ? indentAt(state, items[dropIndex].lineRange.from) : 0);\n  const text = reindent(state.doc.sliceString(from, to), sourceIndent, targetIndent);\n\n  // Remove the subtree together with one adjacent newline so no blank line is orphaned.\n  const hasFollowing = to < state.doc.length;\n  const deleteFrom = hasFollowing ? from : Math.max(0, from - 1);\n  const deleteTo = hasFollowing ? Math.min(to + 1, state.doc.length) : to;\n  const insert = dropIndex < count ? `${text}\\n` : `\\n${text}`;\n\n  // Sort the two changes by position (CodeMirror requires ascending, non-overlapping specs).\n  const changes =\n    insertAt <= deleteFrom\n      ? [\n          { from: insertAt, insert },\n          { from: deleteFrom, to: deleteTo },\n        ]\n      : [\n          { from: deleteFrom, to: deleteTo },\n          { from: insertAt, insert },\n        ];\n\n  // Caret at the end of the moved item; keep the moved item selected (its new anchor is where the moved\n  // text starts — the subtree's first line).\n  const changeSet = state.changes(changes);\n  const textStart = changeSet.mapPos(insertAt, -1) + (dropIndex < count ? 0 : 1);\n  const caret = textStart + text.length;\n  view.dispatch({\n    changes,\n    selection: { anchor: Math.min(caret, changeSet.newLength) },\n    effects: setBlockSelection.of([textStart]),\n    userEvent: 'move.item',\n  });\n};\n\n// Merges sorted ranges, combining any that overlap or touch. Exported for the menu commands.\nexport const mergeRanges = (ranges: { from: number; to: number }[]): { from: number; to: number }[] => {\n  const sorted = [...ranges].sort((a, b) => a.from - b.from);\n  const merged: { from: number; to: number }[] = [];\n  for (const range of sorted) {\n    const last = merged.at(-1);\n    if (last && range.from <= last.to) {\n      last.to = Math.max(last.to, range.to);\n    } else {\n      merged.push({ ...range });\n    }\n  }\n  return merged;\n};\n\n// Delete ranges for the given items: each item's own lines plus its separating newline (the one below,\n// down to the next item; for the last item the one above instead, so no trailing blank is orphaned),\n// merged so adjacent selections form one range.\nconst deleteRanges = (state: EditorState, blocks: Block[], indices: number[]): { from: number; to: number }[] => {\n  const count = blocks.length;\n  return mergeRanges(\n    indices.map((index) => ({\n      from: index === count - 1 && index > 0 ? blocks[index - 1].to : blocks[index].from,\n      to: index + 1 < count ? blocks[index + 1].from : state.doc.length,\n    })),\n  );\n};\n\n/**\n * Moves the items at `sourceIndices` to the slot before `dropIndex`. A single item moves with its whole\n * subtree (re-indented — see `moveItem`); multiple items move by their own lines as a group (a\n * best-effort for multi-selection reorder).\n */\n// Exported for tests (the drag path calls this via `outlinerBlockOps`).\nexport const moveBlocks = (view: EditorView, sourceIndices: number[], dropIndex: number, indent?: number): void => {\n  const { state } = view;\n  const items = flattenItems(state);\n  const count = items.length;\n  const sources = [...new Set(sourceIndices)].filter((index) => index >= 0 && index < count).sort((a, b) => a - b);\n  if (sources.length === 0 || sources.includes(dropIndex)) {\n    return;\n  }\n  if (sources.length === 1) {\n    // Single-item drags re-root the whole subtree to the drag's resolved drop level (`getDropIndent`).\n    moveItem(view, sources[0], dropIndex, indent);\n    return;\n  }\n\n  const blocks = getBlocks(state);\n  const text = sources.map((index) => state.doc.sliceString(blocks[index].from, blocks[index].to)).join('\\n');\n  const deletes = deleteRanges(state, blocks, sources);\n  const insertAt = dropIndex < count ? blocks[dropIndex].from : state.doc.length;\n  if (deletes.some((range) => insertAt > range.from && insertAt < range.to)) {\n    return;\n  }\n\n  const insert = dropIndex < count ? `${text}\\n` : `\\n${text}`;\n  const changes = [...deletes.map((range) => ({ from: range.from, to: range.to })), { from: insertAt, insert }];\n  const changeSet = state.changes(changes);\n  const textStart = changeSet.mapPos(insertAt, -1) + (dropIndex < count ? 0 : 1);\n  // Keep the moved items selected: their new anchors are the start of each item within the inserted\n  // text, joined by a single newline (item length + 1). The caret lands at the end of the moved content.\n  const anchors: number[] = [];\n  let anchor = textStart;\n  for (const index of sources) {\n    anchors.push(anchor);\n    anchor += blocks[index].to - blocks[index].from + 1;\n  }\n  const caret = textStart + text.length;\n  view.dispatch({\n    changes,\n    selection: { anchor: Math.min(caret, changeSet.newLength) },\n    effects: setBlockSelection.of(anchors),\n    userEvent: 'move.item',\n  });\n};\n\n/** Removes the items at `indices`, optionally replacing the first slot with `text` (paste). */\n// Exported for tests (the drag/clipboard path calls this via `outlinerBlockOps`).\nexport const replaceBlocks = (view: EditorView, indices: number[], text: string | null): void => {\n  const { state } = view;\n  const blocks = getBlocks(state);\n  const count = blocks.length;\n  const sources = [...new Set(indices)].filter((index) => index >= 0 && index < count).sort((a, b) => a - b);\n  if (sources.length === 0) {\n    return;\n  }\n\n  const deletes = deleteRanges(state, blocks, sources);\n  const changes = deletes.map(\n    (range) => ({ from: range.from, to: range.to }) as { from: number; to: number; insert?: string },\n  );\n  let anchor = deletes[0].from;\n  if (text != null && text.length > 0) {\n    const insert = deletes[0].to >= state.doc.length ? text : `${text}\\n`;\n    changes[0] = { from: deletes[0].from, to: deletes[0].to, insert };\n    anchor = deletes[0].from + text.length;\n  }\n\n  view.dispatch({\n    changes,\n    selection: { anchor: Math.min(anchor, state.doc.length) },\n    effects: setBlockSelection.of([]),\n    userEvent: text == null ? 'delete.block' : 'input.paste',\n  });\n};\n\nconst outlinerBlockOps: BlockOps = {\n  getBlocks,\n  moveBlocks,\n  serialize: (state, selected) => selected.map((block) => state.doc.sliceString(block.from, block.to)).join('\\n'),\n  replaceBlocks,\n};\n\n/**\n * Block selection, drag-to-reorder, and cut/copy/paste for outliner items. Single-item drag relocates\n * the whole subtree (re-indented); the generic block-selection and clipboard from the `blocks` extensions\n * operate on item lines. The grip is a floating overlay pinned to the content's left edge (see\n * `createBlockDrag`), so it needs no outliner-specific gutter styling.\n */\n/** An empty item has nothing to move; its grip would only crowd the add button and the placeholder. */\nexport const canDragItem = (state: EditorState, block: Block): boolean => {\n  const item = state.facet(treeFacet).find(block.from);\n  return item != null && item.contentRange.from !== item.contentRange.to;\n};\n\nexport const outlinerDnd = (): Extension => [\n  // The selection highlight is drawn by `outliner.ts` as a line decoration (aligned to the actual rows);\n  // the `blocks` RectangleMarker layer is skipped here because it drifts against the outliner's row CSS.\n  createBlockSelection(outlinerBlockOps),\n  createBlockDrag({ getBlocks, canDrag: canDragItem, moveBlocks, getExtent, keepTrailingBreak: true, getDropIndent }),\n];\n\n//\n// Selection commands over the block selection (each item's line-start anchor). Replaces the outliner's\n// former `selectionFacet`, so keyboard selection and the grip share one model.\n//\n\nconst itemAnchors = (state: EditorState): number[] => flattenItems(state).map((item) => item.lineRange.from);\n\n/** Select every item; pressing again (when all are selected) clears the selection. */\nexport const selectAllItems: Command = (view) => {\n  const anchors = itemAnchors(view.state);\n  const current = view.state.field(blockSelectionField, false) ?? [];\n  view.dispatch({ effects: setBlockSelection.of(current.length === anchors.length ? [] : anchors) });\n  return true;\n};\n\n/** Clear the block selection. */\nexport const selectNoneItems: Command = (view) => {\n  view.dispatch({ effects: setBlockSelection.of([]) });\n  return true;\n};\n\n// Extend the block selection by one item toward `delta`, moving the caret into the target item.\nconst extendSelection = (view: EditorView, delta: -1 | 1): boolean => {\n  const items = flattenItems(view.state);\n  const caret = view.state.selection.main.head;\n  const index = items.findIndex((item) => caret >= item.lineRange.from && caret <= item.lineRange.to);\n  if (index < 0) {\n    return false;\n  }\n  const target = Math.min(items.length - 1, Math.max(0, index + delta));\n  const anchors = new Set(view.state.field(blockSelectionField, false) ?? []);\n  anchors.add(items[index].lineRange.from);\n  anchors.add(items[target].lineRange.from);\n  view.dispatch({\n    selection: { anchor: items[target].contentRange.from },\n    effects: setBlockSelection.of([...anchors].sort((a, b) => a - b)),\n  });\n  return true;\n};\n\nexport const selectUp: Command = (view) => extendSelection(view, -1);\nexport const selectDown: Command = (view) => extendSelection(view, 1);\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { getIndentUnit } from '@codemirror/language';\nimport { Annotation, type ChangeSpec, EditorSelection, type EditorState, type Extension } from '@codemirror/state';\nimport { type Command, type EditorView, keymap } from '@codemirror/view';\n\nimport { escapeLinkLabel } from '../../language/markdown/index.ts';\nimport { blockSelectionField, setBlockSelection } from '../blocks/index.ts';\nimport { mergeRanges, selectAllItems, selectDown, selectNoneItems, selectUp } from './dnd.ts';\nimport { type Item, getRange, treeFacet } from './tree.ts';\n\n//\n// Menu / action scope.\n//\n\n/**\n * The items a menu action operates on: the block selection expanded to whole subtrees, or — with no\n * selection — the caret's item. Menu commands (delete, toggle, ...) act over this scope so that a\n * selected subtree is the unit of the action rather than a single line.\n */\nexport const getActionScope = (state: EditorState): Item[] => {\n  const tree = state.facet(treeFacet);\n  const anchors = state.field(blockSelectionField, false) ?? [];\n  const found =\n    anchors.length > 0 ? anchors.map((anchor) => tree.find(anchor)) : [tree.find(state.selection.main.from)];\n  return found.filter((item): item is Item => item != null);\n};\n\n//\n// Exit.\n//\n\n/** Marks the transaction that turns an empty item into a blank line, so the edit filter admits it. */\nexport const exitItemAnnotation = Annotation.define<boolean>();\n\n/**\n * Enter on an empty, childless item ends the list there: the marker goes, a blank line separates the\n * list from what follows, and the caret lands on the line below it. The separator is what makes the\n * next text a paragraph of its own rather than the item's lazy continuation. Mid-list this splits the\n * list in two. Anything else is left to the markdown continuation.\n */\nexport const exitEmptyItem: Command = (view: EditorView) => {\n  const pos = view.state.selection.main.from;\n  const tree = view.state.facet(treeFacet);\n  const item = tree.find(pos);\n  if (!item || item.children.length > 0 || item.contentRange.from !== item.contentRange.to) {\n    return false;\n  }\n\n  const line = view.state.doc.lineAt(item.contentRange.from);\n  view.dispatch({\n    changes: { from: line.from, to: line.to, insert: '\\n' },\n    selection: EditorSelection.cursor(line.from + 1),\n    annotations: exitItemAnnotation.of(true),\n  });\n  return true;\n};\n\n//\n// Indentation comnmands.\n//\n\nexport const indentItemMore: Command = (view: EditorView) => {\n  const pos = view.state.selection.main.from;\n  const tree = view.state.facet(treeFacet);\n  const current = tree.find(pos);\n  if (current) {\n    const previous = tree.prev(current);\n    if (previous && current.level <= previous.level) {\n      // Indent the current line and all descendants so the whole subtree moves with its parent.\n      // NOTE: The markdown extension doesn't provide an indentation service.\n      const insert = ' '.repeat(getIndentUnit(view.state));\n      const changes: ChangeSpec[] = [];\n      tree.traverse(current, (item) => {\n        const line = view.state.doc.lineAt(item.lineRange.from);\n        changes.push({ from: line.from, insert });\n      });\n\n      if (changes.length > 0) {\n        view.dispatch({ changes });\n      }\n    }\n  }\n\n  return true;\n};\n\nexport const indentItemLess: Command = (view: EditorView) => {\n  const pos = view.state.selection.main.from;\n  const tree = view.state.facet(treeFacet);\n  const current = tree.find(pos);\n  if (current) {\n    if (current.level > 0) {\n      // Unindent current line and all descendants.\n      // NOTE: The markdown extension doesn't provide an indentation service.\n      const indentUnit = getIndentUnit(view.state);\n      const changes: ChangeSpec[] = [];\n      tree.traverse(current, (item) => {\n        const line = view.state.doc.lineAt(item.lineRange.from);\n        changes.push({ from: line.from, to: line.from + indentUnit });\n      });\n\n      if (changes.length > 0) {\n        view.dispatch({ changes });\n      }\n    }\n  }\n\n  return true;\n};\n\n//\n// Moving commands.\n//\n\nexport const moveItemDown: Command = (view: EditorView) => {\n  const pos = view.state.selection.main.from;\n  const tree = view.state.facet(treeFacet);\n  const current = tree.find(pos);\n  if (current && current.nextSibling) {\n    const next = current.nextSibling;\n    const currentContent = view.state.doc.sliceString(...getRange(tree, current));\n    const nextContent = view.state.doc.sliceString(...getRange(tree, next));\n    const changes: ChangeSpec[] = [\n      {\n        from: current.lineRange.from,\n        to: current.lineRange.from + currentContent.length,\n        insert: nextContent,\n      },\n      {\n        from: next.lineRange.from,\n        to: next.lineRange.from + nextContent.length,\n        insert: currentContent,\n      },\n    ];\n\n    view.dispatch({\n      changes,\n      selection: EditorSelection.cursor(pos + nextContent.length + 1),\n      scrollIntoView: true,\n    });\n  }\n\n  return true;\n};\n\nexport const moveItemUp: Command = (view: EditorView) => {\n  const pos = view.state.selection.main.from;\n  const tree = view.state.facet(treeFacet);\n  const current = tree.find(pos);\n  if (current && current.prevSibling) {\n    const prev = current.prevSibling;\n    const currentContent = view.state.doc.sliceString(...getRange(tree, current));\n    const prevContent = view.state.doc.sliceString(...getRange(tree, prev));\n    const changes: ChangeSpec[] = [\n      {\n        from: prev.lineRange.from,\n        to: prev.lineRange.from + prevContent.length,\n        insert: currentContent,\n      },\n      {\n        from: current.lineRange.from,\n        to: current.lineRange.from + currentContent.length,\n        insert: prevContent,\n      },\n    ];\n\n    view.dispatch({\n      changes,\n      selection: EditorSelection.cursor(pos - prevContent.length - 1),\n      scrollIntoView: true,\n    });\n  }\n\n  return true;\n};\n\n//\n// Link commands.\n//\n\n/** Content of the action scope's first item, excluding its indentation and list/task marker. */\nexport const getItemText = (state: EditorState): string | undefined => {\n  const [item] = getActionScope(state);\n  return item && state.doc.sliceString(item.contentRange.from, item.contentRange.to);\n};\n\nexport type ItemLink = {\n  label: string;\n  url: string;\n};\n\n/** A whole item consisting of one markdown link, e.g. the result of {@link replaceItemWithLink}. */\nconst ITEM_LINK = /^\\[[^\\]]*\\]\\([^)]+\\)$/;\n\n/**\n * Whether the action scope's first item is already a single markdown link.\n *\n * Converting such an item again would replace the link with a link to a NEW object titled after the\n * old link's label, silently orphaning the first one — so callers gate the convert action on this.\n */\nexport const isItemLink = (state: EditorState): boolean => {\n  const text = getItemText(state)?.trim();\n  return !!text && ITEM_LINK.test(text);\n};\n\n/**\n * Replaces the action scope's first item with a markdown link to `url`.\n * A task marker is demoted to a bullet since completion state now belongs to the linked object.\n */\nexport const replaceItemWithLink = (view: EditorView, { label, url }: ItemLink): boolean => {\n  const { state } = view;\n  const [item] = getActionScope(state);\n  if (!item) {\n    return false;\n  }\n\n  const marker = ' '.repeat(getIndentUnit(state) * item.level) + '- ';\n  const insert = `[${escapeLinkLabel(label)}](${url})`;\n  view.dispatch({\n    changes: [\n      { from: item.lineRange.from, to: item.contentRange.from, insert: marker },\n      { from: item.contentRange.from, to: item.contentRange.to, insert },\n    ],\n    selection: EditorSelection.cursor(item.lineRange.from + marker.length + insert.length),\n    effects: setBlockSelection.of([]),\n    userEvent: 'input.link',\n  });\n\n  return true;\n};\n\n//\n// Misc commands.\n//\n\n/** Deletes the action scope — each selected item's whole subtree (or the caret item's) — and clears the selection. */\nexport const deleteItem: Command = (view: EditorView) => {\n  const { state } = view;\n  const tree = state.facet(treeFacet);\n  const items = getActionScope(state);\n  if (items.length === 0) {\n    return true;\n  }\n\n  // Each item's whole subtree plus its trailing newline; merged so a selected parent and child (or\n  // adjacent selections) collapse into one non-overlapping range.\n  const ranges = mergeRanges(\n    items.map((item) => {\n      const [from, to] = getRange(tree, item);\n      return { from, to: Math.min(to + 1, state.doc.length) };\n    }),\n  );\n\n  view.dispatch({\n    changes: ranges.map((range) => ({ from: range.from, to: range.to })),\n    selection: EditorSelection.cursor(ranges[0].from),\n    effects: setBlockSelection.of([]),\n    userEvent: 'delete.item',\n  });\n\n  return true;\n};\n\n/** Toggles every item in the action scope between task and bullet, uniformly (by the first item's type). */\nexport const toggleTask: Command = (view: EditorView) => {\n  const { state } = view;\n  const items = getActionScope(state);\n  if (items.length === 0) {\n    return true;\n  }\n\n  const unit = getIndentUnit(state);\n  const toTask = items[0].type !== 'task';\n  const changes: ChangeSpec[] = items\n    .filter((item) => (toTask ? item.type !== 'task' : item.type === 'task'))\n    .map((item) => ({\n      from: item.lineRange.from,\n      to: item.contentRange.from,\n      insert: ' '.repeat(unit * item.level) + (toTask ? '- [ ] ' : '- '),\n    }));\n\n  if (changes.length > 0) {\n    view.dispatch({ changes });\n  }\n\n  return true;\n};\n\n/** Keymap binding outliner keys: Tab indent, Enter continuation, arrow navigation, move, delete, and toggle-task. */\nexport const commands = (): Extension =>\n  keymap.of([\n    //\n    // Indentation.\n    //\n    {\n      key: 'Tab',\n      preventDefault: true,\n      run: indentItemMore,\n      shift: indentItemLess,\n    },\n\n    //\n    // Continuation.\n    //\n    {\n      key: 'Enter',\n      run: exitEmptyItem,\n      shift: (view) => {\n        const pos = view.state.selection.main.from;\n        const insert = '\\n  '; // TODO(burdon): Fix parsing.\n        view.dispatch({\n          changes: [{ from: pos, to: pos, insert }],\n          selection: EditorSelection.cursor(pos + insert.length),\n        });\n        return true;\n      },\n    },\n\n    //\n    // Navigation.\n    //\n    {\n      key: 'ArrowDown',\n      // Jump to next item (default moves to end of currentline).\n      run: (view) => {\n        const tree = view.state.facet(treeFacet);\n        const item = tree.find(view.state.selection.main.from);\n        if (\n          item &&\n          view.state.doc.lineAt(item.lineRange.to).number - view.state.doc.lineAt(item.lineRange.from).number === 0\n        ) {\n          const next = tree.next(item);\n          if (next) {\n            view.dispatch({ selection: EditorSelection.cursor(next.contentRange.from) });\n            return true;\n          }\n        }\n\n        return false;\n      },\n    },\n\n    //\n    // Line selection.\n    // TODO(burdon): Shortcut to select current item?\n    //\n    {\n      key: 'Mod-a',\n      preventDefault: true,\n      run: selectAllItems,\n    },\n    {\n      key: 'Escape',\n      preventDefault: true,\n      run: selectNoneItems,\n    },\n    {\n      key: 'ArrowUp',\n      shift: selectUp,\n    },\n    {\n      key: 'ArrowDown',\n      shift: selectDown,\n    },\n\n    //\n    // Move.\n    //\n    {\n      key: 'Alt-ArrowDown',\n      preventDefault: true,\n      run: moveItemDown,\n    },\n    {\n      key: 'Alt-ArrowUp',\n      preventDefault: true,\n      run: moveItemUp,\n    },\n    //\n    // Delete.\n    //\n    {\n      key: 'Mod-Backspace',\n      preventDefault: true,\n      run: deleteItem,\n    },\n    //\n    // Misc.\n    //\n    {\n      key: 'Alt-t',\n      preventDefault: true,\n      run: toggleTask,\n    },\n  ]);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { EditorSelection, type Extension } from '@codemirror/state';\nimport {\n  type Command,\n  Decoration,\n  type DecorationSet,\n  EditorView,\n  ViewPlugin,\n  type ViewUpdate,\n} from '@codemirror/view';\n\nimport { type CleanupFn, addEventListener } from '@dxos/async';\nimport { Domino } from '@dxos/ui';\n\nimport { PlaceholderWidget } from '../../completion/placeholder.ts';\nimport { GUTTER_WIDTH } from '../blocks/index.ts';\nimport { treeFacet } from './tree.ts';\n\nexport type GhostOptions = {\n  /** Placeholder shown in an empty item. */\n  item?: string;\n  /** Accessible label of the add-task button shown beside a blank line. */\n  add?: string;\n};\n\nconst DEFAULTS: Required<GhostOptions> = { item: 'Enter task', add: 'Add task' };\n\nconst TASK_MARKER = '- [ ] ';\n\n// The marker as written, so the placeholder's slot comes from the line text rather than from the tree:\n// the tree can lag the parser by a transaction, and a stale content start would sit under the checkbox.\nconst EMPTY_ITEM_REGEX = /^\\s*- (?:\\[[ x]\\] )?$/;\n\n/** Turns the caret's blank line into an empty task and places the caret in it. */\nexport const insertTaskAtLine: Command = (view) => {\n  const line = view.state.doc.lineAt(view.state.selection.main.head);\n  if (line.text.trim() !== '' || view.state.facet(treeFacet).find(line.from)) {\n    return false;\n  }\n\n  view.dispatch({\n    changes: { from: line.from, to: line.to, insert: TASK_MARKER },\n    selection: EditorSelection.cursor(line.from + TASK_MARKER.length),\n  });\n  view.focus();\n  return true;\n};\n\n/**\n * Hints at what an empty row wants: an empty item shows a placeholder in its content, and a blank line\n * shows an add button where the item's grip would be. Both follow the caret and need focus.\n */\nexport const ghost = (options: GhostOptions = {}): Extension => {\n  const { item, add } = { ...DEFAULTS, ...options };\n\n  return [\n    // Empty item: placeholder at the content start.\n    ViewPlugin.fromClass(\n      class {\n        decorations: DecorationSet = Decoration.none;\n\n        constructor(view: EditorView) {\n          this.decorations = this.compute(view);\n        }\n\n        update(update: ViewUpdate) {\n          if (update.docChanged || update.selectionSet || update.focusChanged) {\n            this.decorations = this.compute(update.view);\n          }\n        }\n\n        private compute(view: EditorView): DecorationSet {\n          if (!view.hasFocus) {\n            return Decoration.none;\n          }\n          const line = view.state.doc.lineAt(view.state.selection.main.head);\n          if (!EMPTY_ITEM_REGEX.test(line.text) || !view.state.facet(treeFacet).find(line.from)) {\n            return Decoration.none;\n          }\n          return Decoration.set([Decoration.widget({ widget: new PlaceholderWidget(item), side: 1 }).range(line.to)]);\n        }\n      },\n      { decorations: (plugin) => plugin.decorations },\n    ),\n\n    // Blank line: an add button floating in the grip gutter, positioned like the menu trigger.\n    ViewPlugin.fromClass(\n      class {\n        readonly view: EditorView;\n        readonly button: HTMLElement;\n        rafId: number | null = null;\n        cleanup?: CleanupFn;\n\n        constructor(view: EditorView) {\n          this.view = view;\n          const container = view.scrollDOM;\n          if (getComputedStyle(container).position === 'static') {\n            container.style.position = 'relative';\n          }\n\n          // A control-sized box around the button, as the grip is built, so the two gutter controls share\n          // one hit area and centre.\n          const button = Domino.of('button')\n            .classNames('grid size-6 place-items-center rounded-xs hover:bg-hover-surface')\n            .attributes({ 'type': 'button', 'aria-label': add, 'title': add })\n            .append(Domino.of('div').classNames('cm-outliner-add-icon').append(Domino.svg('ph--plus--regular')));\n          this.button = Domino.of('div').classNames('cm-outliner-add').append(button).root;\n          // `mousedown` rather than `click`: the click would first blur the editor and hide the button.\n          button.root.addEventListener('mousedown', (event) => {\n            event.preventDefault();\n            insertTaskAtLine(this.view);\n          });\n          this.button.style.display = 'none';\n          container.appendChild(this.button);\n\n          const handler = () => this.schedule();\n          this.cleanup = addEventListener(document, 'scroll', handler, { capture: true, passive: true });\n          this.schedule();\n        }\n\n        destroy() {\n          this.cleanup?.();\n          this.button.remove();\n          if (this.rafId != null) {\n            cancelAnimationFrame(this.rafId);\n          }\n        }\n\n        update(update: ViewUpdate) {\n          if (\n            update.docChanged ||\n            update.focusChanged ||\n            update.geometryChanged ||\n            update.selectionSet ||\n            update.viewportChanged\n          ) {\n            this.schedule();\n          }\n        }\n\n        schedule() {\n          if (this.rafId != null) {\n            cancelAnimationFrame(this.rafId);\n          }\n          this.rafId = requestAnimationFrame(() => {\n            this.rafId = null;\n            this.position();\n          });\n        }\n\n        position() {\n          const { state } = this.view;\n          const line = state.doc.lineAt(state.selection.main.head);\n          const blank = this.view.hasFocus && line.text.trim() === '' && !state.facet(treeFacet).find(line.from);\n          const coords = blank ? this.view.coordsAtPos(line.from) : null;\n          if (!coords) {\n            this.button.style.display = 'none';\n            return;\n          }\n\n          const { x } = this.view.contentDOM.getBoundingClientRect();\n          this.button.style.top = `${(coords.top + coords.bottom) / 2}px`;\n          this.button.style.left = `${x - GUTTER_WIDTH / 2}px`;\n          this.button.style.display = 'grid';\n        }\n      },\n    ),\n\n    EditorView.theme({\n      // Pinned like the grip: `left`/`top` name the centre point and the transform centres the box.\n      '.cm-outliner-add': {\n        position: 'fixed',\n        zIndex: '5',\n        transform: 'translate(-50%, -50%)',\n        display: 'grid',\n        placeItems: 'center',\n        width: 'var(--dx-control)',\n        height: 'var(--dx-control)',\n      },\n      '.cm-outliner-add-icon': {\n        display: 'grid',\n        placeContent: 'center',\n        // `size-3`: the glyph is 1em.\n        fontSize: '0.75rem',\n        color: 'var(--color-fg-muted, currentColor)',\n      },\n    }),\n  ];\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type ChangeSpec, EditorSelection, EditorState } from '@codemirror/state';\nimport { type EditorView, ViewPlugin } from '@codemirror/view';\n\nimport { log } from '@dxos/log';\n\nimport { exitItemAnnotation } from './commands.ts';\nimport { treeFacet } from './tree.ts';\n\nconst LIST_ITEM_REGEX = /^\\s*- (\\[ \\]|\\[x\\])? /;\n\n/**\n * Initialize empty document.\n */\nconst initialize = (autoInsert = false) => {\n  return ViewPlugin.fromClass(\n    class {\n      #timer: ReturnType<typeof setTimeout> | null = null;\n\n      constructor(view: EditorView) {\n        if (!autoInsert) {\n          return;\n        }\n\n        const first = view.state.doc.lineAt(0);\n        const text = view.state.sliceDoc(first.from, first.to);\n        const match = text.match(LIST_ITEM_REGEX);\n        if (!match) {\n          // Deferred so the dispatch runs after the view finishes constructing.\n          this.#timer = setTimeout(() => {\n            this.#timer = null;\n            const insert = '- [ ] ';\n            view.dispatch({\n              changes: [{ from: 0, to: 0, insert }],\n              selection: EditorSelection.cursor(insert.length),\n            });\n          });\n        }\n      }\n\n      destroy() {\n        if (this.#timer != null) {\n          clearTimeout(this.#timer);\n          this.#timer = null;\n        }\n      }\n    },\n  );\n};\n\n/**\n * Handle cursor movement, selection, and editing.\n */\nexport const editor = (autoInsert = false) => [\n  initialize(autoInsert),\n\n  EditorState.transactionFilter.of((tr) => {\n    const tree = tr.state.facet(treeFacet);\n\n    //\n    // Check cursor is in a valid position.\n    //\n    if (!tr.docChanged) {\n      const current = tr.state.selection.main.from;\n      if (current != null) {\n        // Prose between or around lists has no item; the caret is free there.\n        const currentItem = tree.find(current);\n        if (!currentItem) {\n          return tr;\n        }\n\n        // Check if outside of editable range.\n        if (current < currentItem.contentRange.from || current > currentItem.contentRange.to) {\n          const prev = tr.startState.selection.main.from;\n          const prevItem = prev != null ? tree.find(prev) : undefined;\n          if (!prevItem) {\n            return [{ selection: EditorSelection.cursor(currentItem.contentRange.from) }];\n          } else {\n            if (currentItem.index < prevItem.index) {\n              // Moving line up.\n              return [{ selection: EditorSelection.cursor(currentItem.contentRange.to) }];\n            } else if (currentItem.index > prevItem.index) {\n              // Moving line down.\n              return [{ selection: EditorSelection.cursor(currentItem.contentRange.from) }];\n            } else {\n              // Moving left.\n              if (current < prev) {\n                if (currentItem.index === 0) {\n                  // At start of the list.\n                  return [];\n                } else {\n                  // Go to previous line.\n                  return [{ selection: EditorSelection.cursor(currentItem.lineRange.from - 1) }];\n                }\n              } else {\n                // Go to end of line.\n                return [{ selection: EditorSelection.cursor(currentItem.contentRange.to) }];\n              }\n            }\n          }\n        }\n      }\n\n      return tr;\n    }\n\n    // Exiting an empty item deletes its marker on purpose; the marker guard below would read the same\n    // change shape as Backspace and join the line away.\n    if (tr.annotation(exitItemAnnotation)) {\n      return tr;\n    }\n\n    //\n    // Validate changes that don't break the tree.\n    //\n    let cancel = false;\n    const changes: ChangeSpec[] = [];\n    tr.changes.iterChanges((fromA, toA, fromB, toB, insert) => {\n      const line = tr.startState.doc.lineAt(fromA);\n      const match = line.text.match(LIST_ITEM_REGEX);\n      if (match) {\n        // Check cursor was in a valid position.\n        const startTree = tr.startState.facet(treeFacet);\n        const startItem = startTree.find(tr.startState.selection.main.from);\n\n        // Check if entire line was deleted (which is ok).\n        const deleteLine = fromA === startItem?.lineRange.from && toA === startItem?.lineRange.to + 1;\n        if (deleteLine) {\n          return;\n        }\n\n        // Detect and cancel replacement of task marker with continuation indent.\n        // Task markers are atomic so will be deleted when backspace is pressed.\n        // The markdown extension inserts 2 or 6 spaces when deleting a list or task marker in order to create a continuation.\n        // - [ ] <- backspace here deletes the task marker.\n        // - [ ] <- backspace here inserts 6 spaces (creates continuation).\n        //   - [ ] <- backspace here deletes the task marker.\n        const start = line.from + (match?.[0]?.length ?? 0);\n        const replace = start === toA && toA - fromA === insert.length;\n        if (replace) {\n          changes.push({ from: line.from - 1, to: toA });\n          return;\n        }\n\n        // Detect deletion of marker.\n        if (fromB === toB) {\n          if (toA === line.to) {\n            const line = tr.state.doc.lineAt(fromA);\n            if (line.text.match(/^\\s*$/)) {\n              if (line.from === 0) {\n                // Don't delete first line.\n                cancel = true;\n                return;\n              } else {\n                // Delete indent and marker.\n                changes.push({ from: line.from - 1, to: toA });\n                return;\n              }\n            }\n          }\n          return;\n        }\n\n        // Check the caret still lands in an item; a marker deletion (above) is handled before this since\n        // it leaves the caret on a line that belongs to no item until the join removes it.\n        const currentItem = tree.find(tr.state.selection.main.from);\n        if (!currentItem?.contentRange) {\n          cancel = true;\n          return;\n        }\n\n        // Prevent newline if line is empty.\n        const item = tree.find(fromA);\n        if (item?.contentRange.from === item?.contentRange.to && fromA === toA) {\n          cancel = true;\n          return;\n        }\n\n        log('change', {\n          item,\n          line: { from: line.from, to: line.to },\n          a: [fromA, toA],\n          b: [fromB, toB],\n          insert: { text: insert.toString(), length: insert.length },\n        });\n      }\n    });\n\n    if (changes.length > 0) {\n      log('modified,', { changes });\n      return [{ changes }];\n    } else if (cancel) {\n      log('cancel');\n      return [];\n    }\n\n    return tr;\n  }),\n];\n","//\n// Copyright 2024 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nimport { type CleanupFn, addEventListener } from '@dxos/async';\nimport { Domino } from '@dxos/ui';\n\nimport { GUTTER_WIDTH } from '../blocks/index.ts';\nimport { treeFacet } from './tree.ts';\n\n// The right-hand strip (`GUTTER_WIDTH`, shared with the grip's left strip) centers the trigger within it.\n// No size constant: the trigger is a square `dx-button` whose box follows the size scale, so it is centred on a point\n// by CSS (`translate(-50%, -50%)`) — see `.cm-outliner-menu` below.\n\nexport type MenuOptions = {\n  icon?: string;\n};\n\n/** Floating action button positioned beside the active outliner line. */\n// TODO(burdon): Replace with popover.\nexport const menu = (options: MenuOptions = {}): Extension => [\n  ViewPlugin.fromClass(\n    class {\n      view: EditorView;\n      tag: HTMLElement;\n      rafId?: number | null;\n      cleanup?: CleanupFn;\n\n      constructor(view: EditorView) {\n        this.view = view;\n\n        // Position context.\n        const container = view.scrollDOM;\n        if (getComputedStyle(container).position === 'static') {\n          container.style.position = 'relative';\n        }\n\n        // Outer `dx-anchor` (fires `dx-anchor-activate`, driving the popover) styled as an `dx-button`;\n        // inner element holds the phosphor glyph. Mirrors the drag grip's construction.\n        this.tag = Domino.of('dx-anchor')\n          .classNames('dx-control dx-button dx-button-square cm-popover-trigger cm-outliner-menu')\n          .attributes({ 'data-variant': 'ghost', 'data-size': 'sm' })\n          .append(\n            Domino.of('div')\n              .classNames('cm-popover-trigger-icon')\n              .append(Domino.svg(options.icon ?? 'ph--dots-three-vertical--regular')),\n          ).root;\n\n        container.appendChild(this.tag);\n\n        // Capture-phase on the document, not `container`: the trigger is `position: fixed`, so its\n        // coordinates go stale when ANY ancestor scrolls — and when the editor grows to fit its\n        // content the scroller is the surrounding plank, which never fires on `scrollDOM`.\n        const handler = () => this.scheduleUpdate();\n        this.cleanup = addEventListener(document, 'scroll', handler, { capture: true, passive: true });\n        this.scheduleUpdate();\n      }\n\n      destroy() {\n        this.cleanup?.();\n        this.tag.remove();\n        if (this.rafId != null) {\n          cancelAnimationFrame(this.rafId);\n        }\n      }\n\n      update(update: ViewUpdate) {\n        this.tag.dataset.focused = update.view.hasFocus ? 'true' : 'false';\n        if (!update.view.hasFocus) {\n          return;\n        }\n\n        // TODO(burdon): Timer to fade in/out.\n        /*if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(openEffect)))) {\n          this.tag.style.display = 'none';\n          this.tag.classList.add('opacity-10');\n        } else if (update.transactions.some((tr) => tr.effects.some((effect) => effect.is(closeEffect)))) {\n          this.tag.style.display = '';\n        } else */ if (\n          update.docChanged ||\n          update.focusChanged ||\n          update.geometryChanged ||\n          update.selectionSet ||\n          update.viewportChanged\n        ) {\n          this.scheduleUpdate();\n        }\n      }\n\n      updateButtonPosition() {\n        const { x, width } = this.view.contentDOM.getBoundingClientRect();\n\n        const pos = this.view.state.selection.main.head;\n        // The actions act on an item; a prose line has none to offer.\n        if (!this.view.state.facet(treeFacet).find(pos)) {\n          this.tag.style.display = 'none';\n          return;\n        }\n\n        const line = this.view.lineBlockAt(pos);\n        const coords = this.view.coordsAtPos(line.from);\n        if (!coords) {\n          // No resolvable line (e.g. the caret's item was just deleted): hide rather than leave the\n          // trigger stranded at its previous position.\n          this.tag.style.display = 'none';\n          return;\n        }\n\n        // The centre of the line's first row, and of the 3rem gutter immediately right of the content\n        // (mirrors the grip). Both name a point; the CSS transform centres the box on it.\n        const offsetTop = (coords.top + coords.bottom) / 2;\n        const offsetLeft = x + width + GUTTER_WIDTH / 2;\n\n        this.tag.style.top = `${offsetTop}px`;\n        this.tag.style.left = `${offsetLeft}px`;\n        this.tag.style.display = '';\n      }\n\n      scheduleUpdate() {\n        if (this.rafId != null) {\n          cancelAnimationFrame(this.rafId);\n        }\n\n        this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));\n      }\n    },\n  ),\n\n  styles,\n];\n\nconst styles = EditorView.theme({\n  // `dx-button` owns the box (size, hover); this only pins it and gates visibility on editor focus.\n  '.cm-popover-trigger': {\n    position: 'fixed',\n    opacity: '0',\n    cursor: 'pointer',\n  },\n  // Scoped to this menu's own trigger rather than the shared `cm-popover-trigger`: that class is also\n  // on the inline `dx-anchor` decorations `popover.ts` puts in the document, and centring those on a\n  // point would shift them out from under their own text.\n  '.cm-outliner-menu': {\n    // `left`/`top` name the centre point; the browser centres the real box on it, whatever its size.\n    transform: 'translate(-50%, -50%)',\n  },\n  '.cm-popover-trigger-icon': {\n    display: 'grid',\n    placeContent: 'center',\n    fontSize: '16px',\n    color: 'var(--color-fg-muted, currentColor)',\n  },\n  '&:focus-within .cm-popover-trigger': {\n    opacity: '1',\n  },\n  // Hide the trigger while a block drag is in flight (the scroller carries `cm-blockDragging`).\n  '.cm-scroller.cm-blockDragging .cm-popover-trigger': {\n    opacity: '0 !important',\n    pointerEvents: 'none',\n  },\n});\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type EditorState, type Extension, Prec, type Range } from '@codemirror/state';\nimport { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\n\nimport { mx } from '@dxos/ui-theme';\n\nimport { decorateMarkdown } from '../../language/index.ts';\nimport { CONTENT_WIDTH, blockSelectionField } from '../blocks/index.ts';\nimport { commands } from './commands.ts';\nimport { outlinerDnd } from './dnd.ts';\nimport { editor } from './editor.ts';\nimport { type GhostOptions, ghost } from './ghost.ts';\nimport { menu } from './menu.ts';\nimport { getRange, outlinerTree, treeFacet } from './tree.ts';\n\n// ISSUES: (move to DESIGN.md)\n// TODO(burdon): Remove requirement for continuous lines to be indented (so that user's can't accidentally delete them and break the layout).\n// TODO(burdon): Prevent unterminated fenced code from breaking subsequent items (\"firewall\" markdown parsing within each item?)\n// TODO(burdon): What if a different editor \"breaks\" the layout?\n// TODO(burdon): Check Automerge recognizes text that is moved/indented (e.g., concurrent editing item while being moved).\n// TODO(burdon): When selecting across items, select entire items (don't show selection that spans the gaps).\n// TODO(burdon): Handle backspace at start of line (or empty line).\n// TODO(burdon): Convert to task object and insert link (menu button).\n\nexport type OutlinerProps = {\n  /** Copy for the empty-row hints; see {@link GhostOptions}. */\n  ghost?: GhostOptions;\n  /**\n   * Presentation only: no editing affordances. Drops the drag grips, the floating menu, and the\n   * gutters reserved for them — a read-only surface (a card preview) has nothing to grab and no room\n   * to spare. Pair with `readOnly` on the basic extensions, which is what actually blocks edits.\n   */\n  readonly?: boolean;\n};\n\n/**\n * Outliner extension.\n * - Stores outline as a standard markdown document with task and list markers.\n * - Supports continuation lines and rich formatting (with Shift+Enter).\n * - Constrains editor to outline structure.\n * - Supports smart cut-and-paste.\n */\nexport const outliner = ({ readonly, ghost: ghostOptions }: OutlinerProps = {}): Extension => [\n  // Commands.\n  Prec.highest(commands()),\n\n  // State.\n  outlinerTree(),\n\n  // Filter and possibly modify changes.\n  editor(),\n\n  // Block selection, drag-to-reorder, highlight, and clipboard (built on the `blocks` extensions).\n  // Kept in its original position: CodeMirror resolves decorations and themes by extension order, so\n  // moving this below the markdown decorations changes what the document renders as.\n  ...(readonly ? [] : [outlinerDnd()]),\n\n  // Current-item indicator (the selection highlight is drawn by `outlinerDnd`).\n  decorations(),\n\n  // Default markdown decorations.\n  decorateMarkdown({ listPaddingLeft: 8 }),\n\n  // Floating menu (with reserved margins left/right for the grip and menu). A read-only surface has\n  // neither, so it also reclaims the margins.\n  ...(readonly\n    ? []\n    : [\n        ghost(ghostOptions),\n        menu(),\n        EditorView.contentAttributes.of({\n          class: CONTENT_WIDTH,\n        }),\n      ]),\n];\n\n/**\n * Structural row layout plus a subtle border on the current (caret) item. The selection highlight is\n * drawn by `outlinerDnd` (the shared `blocks` layer), so this no longer renders selection.\n */\nconst decorations = () => [\n  ViewPlugin.fromClass(\n    class {\n      decorations: DecorationSet = Decoration.none;\n      constructor(view: EditorView) {\n        this.updateDecorations(view.state, view);\n      }\n\n      update(update: ViewUpdate) {\n        const selectionChanged =\n          update.startState.field(blockSelectionField, false) !== update.state.field(blockSelectionField, false);\n        if (\n          update.focusChanged ||\n          update.docChanged ||\n          update.viewportChanged ||\n          update.selectionSet ||\n          selectionChanged\n        ) {\n          this.updateDecorations(update.state, update.view);\n        }\n      }\n\n      private updateDecorations(state: EditorState, { viewport: { from, to }, hasFocus }: EditorView) {\n        const tree = state.facet(treeFacet);\n        const current = tree.find(state.selection.main.from);\n        const doc = state.doc;\n\n        // The block selection stores each selected item's line-start anchor; a line is selected when it\n        // falls within any selected item's subtree. Rendered as a line background (not the `blocks`\n        // RectangleMarker layer) so it stays aligned to the actual rows.\n        const selectedRanges = (state.field(blockSelectionField, false) ?? [])\n          .map((anchor) => {\n            const item = tree.find(anchor);\n            return item ? getRange(tree, item) : null;\n          })\n          .filter((range): range is [number, number] => range != null);\n        const isSelected = (pos: number) =>\n          selectedRanges.some(([rangeFrom, rangeTo]) => pos >= rangeFrom && pos <= rangeTo);\n\n        const decorations: Range<Decoration>[] = [];\n        for (let lineNum = doc.lineAt(from).number; lineNum <= doc.lineAt(to).number; lineNum++) {\n          const line = doc.line(lineNum);\n          const item = tree.find(line.from);\n          if (item) {\n            const lineFrom = doc.lineAt(item.contentRange.from);\n            const lineTo = doc.lineAt(item.contentRange.to);\n            decorations.push(\n              Decoration.line({\n                class: mx(\n                  'cm-list-item',\n                  lineFrom.number === line.number && 'cm-list-item-start',\n                  lineTo.number === line.number && 'cm-list-item-end',\n                  isSelected(line.from) && 'cm-list-item-selected',\n                  hasFocus && item === current && 'cm-list-item-current',\n                ),\n              }).range(line.from, line.from),\n            );\n          } else {\n            // Prose sits flush with the content edge, so a blank line reads as a gap between lists,\n            // not as a continuation of the item above.\n            decorations.push(Decoration.line({ class: 'cm-outline-prose' }).range(line.from, line.from));\n          }\n        }\n\n        this.decorations = Decoration.set(decorations);\n      }\n    },\n    {\n      decorations: (v) => v.decorations,\n    },\n  ),\n\n  // Theme.\n  EditorView.theme({\n    '.cm-list-item': {\n      // borderLeftWidth: '1px',\n      // borderRightWidth: '1px',\n      paddingLeft: '32px',\n      borderColor: 'transparent',\n    },\n    '.cm-list-item.cm-codeblock-start': {\n      borderRadius: '0',\n    },\n\n    // No vertical margins: CodeMirror's gutter/height-map measures each line's border-box but not its\n    // margins, so any inter-row margin makes the drag grips drift further from center down the list (and\n    // shows as gaps between selected rows). Keep row spacing in the padding, which CM does measure.\n    '.cm-list-item-start': {\n      // borderTopWidth: '1px',\n      // borderTopLeftRadius: '4px',\n      // borderTopRightRadius: '4px',\n      paddingTop: '4px',\n    },\n    '.cm-list-item-end': {\n      // borderBottomWidth: '1px',\n      // borderBottomLeftRadius: '4px',\n      // borderBottomRightRadius: '4px',\n      paddingBottom: '4px',\n    },\n\n    // Accent background behind the selected subtree; flat so adjacent selected rows read as one region.\n    '.cm-list-item-selected': {\n      boxSizing: 'border-box',\n      backgroundColor: 'var(--color-cm-highlight-surface)',\n      borderRadius: '0',\n    },\n    // Subtle border on the item under the caret; distinct from the accent selection highlight.\n    '.cm-list-item-current': {\n      borderColor: 'var(--color-focus-ring-subtle)',\n    },\n\n    // The drag preview clones the selected rows, whose content is shifted horizontally to preview the\n    // drop indent — so the highlight must come from the (full-width) container, not the cloned lines,\n    // to span the editor width regardless of the indent level.\n    '.cm-blockDragPreview': {\n      backgroundImage: 'linear-gradient(var(--color-cm-highlight-surface), var(--color-cm-highlight-surface))',\n    },\n    '.cm-blockDragPreview .cm-list-item-selected': {\n      backgroundColor: 'transparent',\n    },\n  }),\n];\n","//\n// Copyright 2026 DXOS.org\n//\n\n/** One side of a {@link DiffRow}; absent where that column has no counterpart line. */\nexport type DiffCell = {\n  /** 1-based line number in that version of the file. */\n  number: number;\n  text: string;\n};\n\n/**\n * A row of the side-by-side layout. `changed` pairs a removal with the addition that replaced it,\n * so the two columns stay aligned; a row missing a side renders that column as a filler.\n */\nexport type DiffRow = {\n  kind: 'context' | 'added' | 'removed' | 'changed';\n  before?: DiffCell;\n  after?: DiffCell;\n};\n\n/** A `@@` hunk: a contiguous window of the file, and the size of the gap skipped before it. */\nexport type DiffChunk = {\n  /** Trailing context of the `@@` header (the enclosing function, as git emits it). */\n  section?: string;\n  /** Lines of the file skipped immediately before this chunk, which the header reports. */\n  gap?: number;\n  rows: DiffRow[];\n};\n\nexport type ParsedDiff = {\n  /** Path as written on the fence or taken from the `+++` header. */\n  file?: string;\n  /** Language name for syntax highlighting, from the fence or inferred from the extension. */\n  language?: string;\n  /** Line range shown, as `66-99`; derived from the chunks when the fence does not say. */\n  range?: string;\n  added: number;\n  removed: number;\n  chunks: DiffChunk[];\n};\n\n/** Extensions worth naming; anything else falls back to the raw extension. */\nconst LANGUAGES: Record<string, string> = {\n  ts: 'typescript',\n  tsx: 'tsx',\n  js: 'javascript',\n  jsx: 'jsx',\n  mjs: 'javascript',\n  cjs: 'javascript',\n  json: 'json',\n  css: 'css',\n  html: 'html',\n  md: 'markdown',\n  mdl: 'markdown',\n  yml: 'yaml',\n  yaml: 'yaml',\n  sh: 'shell',\n  bash: 'shell',\n  rs: 'rust',\n  go: 'go',\n  py: 'python',\n  sql: 'sql',\n  toml: 'toml',\n};\n\n/** Display names, since `typescript` on a file header reads as a slug. */\nconst LANGUAGE_LABELS: Record<string, string> = {\n  typescript: 'TypeScript',\n  tsx: 'TypeScript',\n  javascript: 'JavaScript',\n  jsx: 'JavaScript',\n  json: 'JSON',\n  css: 'CSS',\n  html: 'HTML',\n  markdown: 'Markdown',\n  yaml: 'YAML',\n  shell: 'Shell',\n  rust: 'Rust',\n  go: 'Go',\n  python: 'Python',\n  sql: 'SQL',\n  toml: 'TOML',\n};\n\nexport const languageLabel = (language?: string): string | undefined =>\n  language ? (LANGUAGE_LABELS[language] ?? language) : undefined;\n\nconst languageFromFile = (file: string): string | undefined => {\n  const extension = file.split('.').pop()?.toLowerCase();\n  return extension ? (LANGUAGES[extension] ?? extension) : undefined;\n};\n\n/**\n * Attributes on a fence's info line: `diff file=src/x.ts lines=66-99 lang=typescript`. A bare token\n * is taken as the path, so `diff src/x.ts` works without the key.\n */\nexport type FenceInfo = {\n  /** The word after the backticks; only `diff` is this extension's. */\n  language: string;\n  attributes: Record<string, string>;\n  /** Tokens carrying no `=`, in order. */\n  positional: string[];\n};\n\nexport const parseFenceInfo = (info: string): FenceInfo => {\n  const attributes: Record<string, string> = {};\n  const positional: string[] = [];\n  // Quoted values may contain spaces, so tokenise rather than splitting on whitespace.\n  const tokens = info.trim().match(/(?:[^\\s\"']+|\"[^\"]*\"|'[^']*')+/g) ?? [];\n  const [language = '', ...rest] = tokens;\n  for (const token of rest) {\n    const separator = token.indexOf('=');\n    if (separator > 0) {\n      const key = token.slice(0, separator);\n      const value = token.slice(separator + 1).replace(/^[\"']|[\"']$/g, '');\n      attributes[key] = value;\n    } else {\n      positional.push(token.replace(/^[\"']|[\"']$/g, ''));\n    }\n  }\n\n  return { language: language.toLowerCase(), attributes, positional };\n};\n\n/** `@@ -12,7 +12,9 @@ section` — counts are optional and default to one line. */\nconst HUNK_HEADER = /^@@\\s+-(\\d+)(?:,(\\d+))?\\s+\\+(\\d+)(?:,(\\d+))?\\s+@@(.*)$/;\n\n/** Metadata git emits before a file's first hunk. */\nconst PREAMBLE = /^(index |new file mode |deleted file mode |old mode |new mode |similarity index |rename |copy )/;\n\n/**\n * Parses a unified diff body into rows already paired for the side-by-side layout. Runs of removals\n * and additions are zipped positionally — the convention every review tool uses — so a replaced line\n * sits opposite the line that replaced it and a pure insertion leaves the left column empty.\n *\n * Headers git emits around the body (`diff --git`, `index`, `---`, `+++`) are consumed for the file\n * name rather than rendered, so a block pasted straight out of `git diff` reads the same as one\n * written by hand.\n */\nexport const parseDiff = (body: string, info?: FenceInfo): ParsedDiff => {\n  const file = info?.attributes.file ?? info?.positional[0];\n  const chunks: DiffChunk[] = [];\n  let added = 0;\n  let removed = 0;\n  let headerFile: string | undefined;\n\n  let chunk: DiffChunk | undefined;\n  let beforeLine = 1;\n  let afterLine = 1;\n  /** Line after the previous chunk's last, so the skipped gap can be measured. */\n  let beforeEnd: number | undefined;\n\n  // Buffered runs, flushed as paired rows when the run ends.\n  let removals: DiffCell[] = [];\n  let additions: DiffCell[] = [];\n  const flush = () => {\n    if (!chunk) {\n      return;\n    }\n    for (let index = 0; index < Math.max(removals.length, additions.length); index++) {\n      const before = removals[index];\n      const after = additions[index];\n      chunk.rows.push({ kind: before && after ? 'changed' : before ? 'removed' : 'added', before, after });\n    }\n    removals = [];\n    additions = [];\n  };\n\n  const openChunk = (next: DiffChunk) => {\n    flush();\n    chunks.push(next);\n    chunk = next;\n  };\n\n  for (const raw of body.split('\\n')) {\n    // A diff saved or copied on Windows keeps its carriage returns; they are transport, not content,\n    // and left in place they defeat every anchored match below.\n    const line = raw.endsWith('\\r') ? raw.slice(0, -1) : raw;\n\n    const header = HUNK_HEADER.exec(line);\n    if (header) {\n      const [, beforeStart, , afterStart, , section] = header;\n      beforeLine = Number(beforeStart);\n      afterLine = Number(afterStart);\n      openChunk({\n        section: section.trim() || undefined,\n        // Above the first chunk the skipped gap is everything before it, which the header states.\n        gap: Math.max(0, beforeLine - (beforeEnd ?? 1)),\n        rows: [],\n      });\n      continue;\n    }\n\n    // A new file's header ends whatever chunk was open; nothing inside a hunk starts unprefixed.\n    // A fence holding more than one file is a misuse, but it must not then label every chunk with\n    // the first file's name or measure the second file's gap from the first file's line numbers.\n    if (line.startsWith('diff --git ')) {\n      flush();\n      chunk = undefined;\n      headerFile = undefined;\n      beforeEnd = undefined;\n      continue;\n    }\n\n    // `---`, `+++` and friends are file headers ONLY before the first hunk. Inside one, `--- x` is a\n    // removed line whose text begins `-- `, which every deleted SQL, Lua or Haskell comment does.\n    if (!chunk) {\n      if (line.startsWith('+++ ')) {\n        headerFile ??= line.slice(4).replace(/^b\\//, '').trim();\n        continue;\n      }\n      if (line.startsWith('--- ') || PREAMBLE.test(line) || line.length === 0) {\n        continue;\n      }\n      // A body with no `@@` header is still a diff; open an implicit chunk on the first content line.\n      openChunk({ rows: [] });\n    }\n\n    // `\\ No newline at end of file` annotates the preceding line and is a line of neither version.\n    if (line.startsWith('\\\\')) {\n      continue;\n    }\n\n    const current = chunk;\n    if (current === undefined) {\n      continue;\n    }\n    if (line.startsWith('+')) {\n      additions.push({ number: afterLine++, text: line.slice(1) });\n      added++;\n    } else if (line.startsWith('-')) {\n      removals.push({ number: beforeLine++, text: line.slice(1) });\n      removed++;\n    } else {\n      flush();\n      const text = line.startsWith(' ') ? line.slice(1) : line;\n      current.rows.push({\n        kind: 'context',\n        before: { number: beforeLine++, text },\n        after: { number: afterLine++, text },\n      });\n    }\n    beforeEnd = beforeLine;\n  }\n  flush();\n\n  // A trailing blank line is an artefact of the fence, not part of the diff.\n  const last = chunks.at(-1);\n  const lastRow = last?.rows.at(-1);\n  if (last && lastRow?.kind === 'context' && lastRow.before?.text === '' && lastRow.after?.text === '') {\n    last.rows.pop();\n  }\n\n  const resolvedFile = file ?? headerFile;\n  return {\n    file: resolvedFile,\n    language: info?.attributes.lang ?? (resolvedFile ? languageFromFile(resolvedFile) : undefined),\n    range: info?.attributes.lines ?? shownRange(chunks),\n    added,\n    removed,\n    chunks: chunks.filter((chunk) => chunk.rows.length > 0),\n  };\n};\n\n/** The `after` line span the block covers, for the header's `Lines 66-99`. */\nconst shownRange = (chunks: DiffChunk[]): string | undefined => {\n  // `!== undefined`, not a truthiness test: a `@@ -0,0 +1,N @@` new-file hunk numbers a side 0.\n  const numbers = chunks\n    .flatMap((chunk) => chunk.rows.map((row) => row.after?.number))\n    .filter((line): line is number => line !== undefined);\n  if (numbers.length === 0) {\n    return undefined;\n  }\n  const first = Math.min(...numbers);\n  const last = Math.max(...numbers);\n  return first === last ? `${first}` : `${first}-${last}`;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { HighlightStyle, LanguageDescription, type LanguageSupport } from '@codemirror/language';\nimport { languages } from '@codemirror/language-data';\nimport { type Highlighter, highlightCode } from '@lezer/highlight';\nimport { vscodeDarkStyle, vscodeLightStyle } from '@uiw/codemirror-theme-vscode';\n\n/**\n * The editor's own code colours (see `createBasicExtensions`), so a chunk reads exactly as the same\n * code does in an editor. Both modules are mounted, so the caller picks one by the editor's\n * `darkTheme`: a style's `themeType` is only honoured through `syntaxHighlighting`, not by its rules.\n */\nexport const diffHighlightStyles: Record<'dark' | 'light', HighlightStyle> = {\n  dark: HighlightStyle.define(vscodeDarkStyle),\n  light: HighlightStyle.define(vscodeLightStyle),\n};\n\n/** Resolved supports, so a document with many blocks in one language loads its parser once. */\nconst loaded = new Map<string, Promise<LanguageSupport | undefined>>();\n\nconst loadLanguage = (name: string): Promise<LanguageSupport | undefined> => {\n  let pending = loaded.get(name);\n  if (!pending) {\n    const description = LanguageDescription.matchLanguageName(languages, name, true);\n    // Cached as a rejection-free promise: one unknown language must not break the next lookup.\n    pending = description ? Promise.resolve(description.load()).catch(() => undefined) : Promise.resolve(undefined);\n    loaded.set(name, pending);\n  }\n\n  return pending;\n};\n\n/**\n * Syntax-highlights `code` and returns one fragment per line, ready to drop into the cells of a\n * rendered diff. The whole side is parsed at once rather than line by line, so multi-line constructs\n * (a block comment, a template literal) are coloured as the constructs they are; Lezer's error\n * recovery covers the fact that a chunk is not a whole file.\n *\n * A chunk is a window into a file, and one that opens inside a declaration parses as top-level\n * statements: a type's members come out as bare variable names, uncoloured. `context` — the enclosing\n * line git names in the hunk header — is parsed ahead of the code and dropped from the result, so the\n * chunk is read inside the construct it belongs to.\n *\n * Resolves `undefined` when the language is unknown, leaving the caller's plain text in place.\n */\nexport const highlightLines = async (\n  code: string,\n  language: string,\n  context?: string,\n  highlighter: Highlighter | readonly Highlighter[] = diffHighlightStyles.light,\n): Promise<DocumentFragment[] | undefined> => {\n  const support = await loadLanguage(language);\n  if (!support) {\n    return undefined;\n  }\n\n  const prefix = context ? `${context}\\n` : '';\n  const source = prefix + code;\n  const tree = support.language.parser.parse(source);\n  const lines: DocumentFragment[] = [document.createDocumentFragment()];\n  highlightCode(\n    source,\n    tree,\n    highlighter,\n    (text, classes) => {\n      const line = lines[lines.length - 1];\n      if (!classes) {\n        line.appendChild(document.createTextNode(text));\n        return;\n      }\n      const span = document.createElement('span');\n      span.className = classes;\n      span.textContent = text;\n      line.appendChild(span);\n    },\n    () => lines.push(document.createDocumentFragment()),\n  );\n\n  return prefix ? lines.slice(1) : lines;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Extension } from '@codemirror/state';\nimport { EditorView } from '@codemirror/view';\n\n/**\n * Locals are derived from `--surface-bg` inside the block rather than declared as global tokens: a\n * custom property that references another resolves against the element that DECLARES it, so a\n * surface-relative token defined at `:root` would be frozen to the root surface.\n */\nconst surfaces = {\n  '--cm-diff-surface': 'oklch(from var(--surface-bg) calc(l + var(--dx-lift) * 0.02) c h)',\n  '--cm-diff-chrome': 'oklch(from var(--surface-bg) calc(l + var(--dx-lift) * 0.04) c h)',\n  '--cm-diff-hatch': 'oklch(from var(--surface-bg) calc(l + var(--dx-lift) * 0.05) c h)',\n  '--cm-diff-gutter-fg': 'var(--color-fg-subtle)',\n  // Derived from the gutter colours so the hue has one source, but flatter and weaker: a whole\n  // column of changed code is a large area, and the merge view's per-line tint reads as a wash here.\n  '--cm-diff-add': 'oklch(from var(--color-cm-diff-add-gutter) l calc(c * 0.7) h / 0.16)',\n  '--cm-diff-remove': 'oklch(from var(--color-cm-diff-remove-gutter) l calc(c * 0.7) h / 0.14)',\n};\n\nexport const diffBlockTheme = EditorView.theme({\n  '.cm-diff-block': {\n    ...surfaces,\n    margin: '0',\n    border: '1px solid var(--color-separator)',\n    borderRadius: '0.5rem',\n    background: 'var(--cm-diff-surface)',\n    overflow: 'hidden',\n    fontSize: '0.8125rem',\n    // A block widget's intrinsic width is the document's: an unconstrained chunk drags the prose\n    // beside it past the pane. `100%` cannot break that cycle (it resolves against the width the\n    // block itself is setting), so the cap is the query container — the host puts\n    // `dx-container-type-inline-size` on a content element of definite width, as `documentSlots`\n    // does. With no such container `cqi` falls back to the viewport, which is still a cap.\n    maxWidth: 'min(100%, 100cqi)',\n  },\n\n  //\n  // Header\n  //\n\n  '.cm-diff-header': {\n    display: 'flex',\n    alignItems: 'center',\n    gap: '0.5rem',\n    height: '2.75rem',\n    minWidth: '0',\n    padding: '0 0.875rem 0 0.5rem',\n    background: 'var(--cm-diff-chrome)',\n    fontFamily: 'var(--font-body)',\n    fontSize: '0.8125rem',\n    lineHeight: '1',\n    whiteSpace: 'nowrap',\n  },\n  '.cm-diff-toggle': {\n    display: 'flex',\n    alignItems: 'center',\n    justifyContent: 'center',\n    width: '1.5rem',\n    height: '1.5rem',\n    flex: '0 0 auto',\n    padding: '0',\n    border: 'none',\n    borderRadius: '0.25rem',\n    background: 'transparent',\n    color: 'var(--color-fg-muted)',\n    cursor: 'pointer',\n  },\n  '.cm-diff-toggle:hover': { background: 'var(--color-hover-surface)' },\n  '.cm-diff-toggle svg': { width: '0.875rem', height: '0.875rem', transition: 'transform 150ms ease' },\n  '.cm-diff-collapsed .cm-diff-toggle svg': { transform: 'rotate(-90deg)' },\n  '.cm-diff-collapsed .cm-diff-body': { display: 'none' },\n\n  '.cm-diff-path': { display: 'flex', minWidth: '0' },\n  // The directory gives way first: the file name is what identifies the block.\n  '.cm-diff-dir': {\n    flex: '0 1 auto',\n    minWidth: '0',\n    overflow: 'hidden',\n    textOverflow: 'ellipsis',\n    whiteSpace: 'nowrap',\n    color: 'var(--color-fg-subtle)',\n  },\n  '.cm-diff-name': { flex: '0 0 auto', color: 'var(--color-fg)', fontWeight: '500' },\n\n  '.cm-diff-stat': { flex: '0 0 auto', fontVariantNumeric: 'tabular-nums' },\n  '.cm-diff-stat-added': { color: 'var(--color-cm-diff-add-gutter)' },\n  '.cm-diff-stat-removed': { color: 'var(--color-cm-diff-remove-gutter)' },\n  '.cm-diff-lang': { flex: '0 0 auto', color: 'var(--color-fg-subtle)' },\n  // Pushed to the trailing edge, so the range reads as chrome rather than as part of the path.\n  '.cm-diff-range': { marginInlineStart: 'auto', flex: '0 0 auto', color: 'var(--color-fg-subtle)' },\n  // At the narrow layout the path and the counts are all the header has room to say.\n  '.cm-diff-block[data-layout=\"inline\"] .cm-diff-lang': { display: 'none' },\n  '.cm-diff-block[data-layout=\"inline\"] .cm-diff-range': { display: 'none' },\n\n  //\n  // Hunks\n  //\n\n  '.cm-diff-expander': {\n    display: 'flex',\n    alignItems: 'center',\n    gap: '0.5rem',\n    height: '2.25rem',\n    minWidth: '0',\n    padding: '0 0.875rem',\n    borderBlock: '1px solid var(--color-separator-subtle)',\n    background: 'var(--cm-diff-surface)',\n    color: 'var(--color-fg-muted)',\n    fontFamily: 'var(--font-body)',\n    fontSize: '0.8125rem',\n  },\n  '.cm-diff-expander:first-child': { borderBlockStart: 'none' },\n  '.cm-diff-expander-icon': { width: '0.875rem', height: '0.875rem', flex: '0 0 auto' },\n  // Without this the un-wrapping section label sets the block's minimum width, and a block widget's\n  // minimum is the whole document's: the prose beside it would be pushed past the pane too.\n  '.cm-diff-section': {\n    minWidth: '0',\n    color: 'var(--color-fg-subtle)',\n    fontFamily: 'var(--font-mono)',\n    fontVariantLigatures: 'none',\n    overflow: 'hidden',\n    textOverflow: 'ellipsis',\n    whiteSpace: 'nowrap',\n  },\n\n  '.cm-diff-grid': {\n    display: 'grid',\n    fontFamily: 'var(--font-mono)',\n    lineHeight: '1.8',\n    // Ligatures misreport a diff: JetBrains Mono draws `===` as `≡` and `=>` as `⇒`, so the reader\n    // cannot tell which operator the change actually introduced.\n    fontVariantLigatures: 'none',\n  },\n  '.cm-diff-body[data-layout=\"split\"] .cm-diff-grid': {\n    gridTemplateColumns: 'auto minmax(0, 1fr) auto minmax(0, 1fr)',\n  },\n  '.cm-diff-body[data-layout=\"inline\"] .cm-diff-grid': {\n    gridTemplateColumns: 'auto auto auto minmax(0, 1fr)',\n  },\n\n  '.cm-diff-num': {\n    padding: '0 0.5rem 0 0.75rem',\n    display: 'flex',\n    justifyContent: 'flex-end',\n    gap: '0.375rem',\n    color: 'var(--cm-diff-gutter-fg)',\n    fontVariantNumeric: 'tabular-nums',\n    userSelect: 'none',\n    // The sign stays on the first visual line of a wrapped row.\n    alignItems: 'flex-start',\n  },\n  '.cm-diff-sign': { width: '0.5rem' },\n  '.cm-diff-mark': {\n    paddingInlineEnd: '0.5rem',\n    color: 'var(--cm-diff-gutter-fg)',\n    userSelect: 'none',\n  },\n  '.cm-diff-line': {\n    paddingInlineEnd: '1rem',\n    whiteSpace: 'pre-wrap',\n    overflowWrap: 'anywhere',\n    tabSize: '2',\n    color: 'var(--color-fg)',\n  },\n  '.cm-diff-commentable': { position: 'relative' },\n  // Hangs into the gutter to the left of the code so it never shifts the text it annotates.\n  '.cm-diff-comment': {\n    position: 'absolute',\n    insetInlineStart: '-1.25rem',\n    insetBlockStart: '0.125rem',\n    width: '1.125rem',\n    height: '1.125rem',\n    display: 'flex',\n    alignItems: 'center',\n    justifyContent: 'center',\n    borderRadius: '0.25rem',\n    border: 'none',\n    background: 'var(--color-accent-bg)',\n    color: 'var(--color-accent-fg)',\n    fontSize: '0.875rem',\n    lineHeight: '1',\n    cursor: 'pointer',\n    opacity: '0',\n    transition: 'opacity 100ms ease',\n  },\n  '.cm-diff-commentable:hover .cm-diff-comment, .cm-diff-comment:focus-visible': { opacity: '1' },\n  // The split gutter is a rule between the two versions, not a border on every cell.\n  '.cm-diff-body[data-layout=\"split\"] .cm-diff-split': {\n    borderInlineStart: '1px solid var(--color-separator)',\n    marginInlineStart: '0.5rem',\n  },\n\n  '.cm-diff-added': { background: 'var(--cm-diff-add)' },\n  '.cm-diff-removed': { background: 'var(--cm-diff-remove)' },\n  '.cm-diff-num.cm-diff-added, .cm-diff-mark.cm-diff-added': { color: 'var(--color-cm-diff-add-gutter)' },\n  '.cm-diff-num.cm-diff-removed, .cm-diff-mark.cm-diff-removed': { color: 'var(--color-cm-diff-remove-gutter)' },\n\n  '.cm-diff-filler': {\n    backgroundImage:\n      'repeating-linear-gradient(45deg, transparent 0 5px, var(--cm-diff-hatch) 5px 6px, transparent 6px 11px)',\n  },\n});\n\n/**\n * Reading-mode tuning for a walkthrough document: headings and inline code take the reading\n * foreground rather than the editor's syntax accent, since here they are structure the reader\n * navigates by, not markup being authored. Optional, and separate from {@link diffBlockTheme} — a\n * host that wants the chunks inside a normal editing surface adds only that one.\n */\nexport const walkthroughTheme = (): Extension =>\n  EditorView.theme({\n    '&': {\n      '--color-cm-heading': 'var(--color-fg)',\n      '--color-cm-heading-number': 'var(--color-fg-subtle)',\n      '--color-cm-code-inline': 'var(--color-fg)',\n    },\n  });\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type EditorState, type Extension } from '@codemirror/state';\nimport { Decoration, EditorView, WidgetType } from '@codemirror/view';\nimport { type SyntaxNodeRef } from '@lezer/common';\n\nimport { type WidgetMatcher, widgetMatchersFacet, widgetsCore } from '../widgets/index.ts';\nimport { type DiffRow, type ParsedDiff, languageLabel, parseDiff, parseFenceInfo } from './diff-parser.ts';\nimport { diffHighlightStyles, highlightLines } from './highlight.ts';\nimport { diffBlockTheme } from './theme.ts';\n\n/** The fence language this extension claims. */\nconst DIFF_LANGUAGE = 'diff';\n\n/**\n * Below `NARROW_WIDTH` the two columns no longer fit side by side; the block returns to them only\n * above `WIDE_WIDTH`. The gap is hysteresis: re-rendering changes the block's height, which can\n * add or remove the scroller's scrollbar and move the measured width back across a single\n * threshold, leaving the observer flip-flopping.\n */\nconst NARROW_WIDTH = 720;\nconst WIDE_WIDTH = 760;\n\n/**\n * Heights assumed before the block mounts, so CodeMirror's viewport estimate is not wild. Taken\n * from the theme; a wrapped line still makes this an under-estimate, which is why the block\n * declares it rather than pinning it.\n */\nconst ROW_HEIGHT = 24;\nconst EXPANDER_HEIGHT = 38;\nconst CHROME_HEIGHT = 46;\n\nexport type DiffLayout = 'split' | 'inline' | 'auto';\n\nexport type DiffBlocksOptions = {\n  /**\n   * `split` renders before and after as two columns, `inline` as one unified column, and `auto`\n   * (the default) picks by measured width — two columns of code are unreadable in a narrow pane.\n   */\n  layout?: DiffLayout;\n  /** Syntax-highlight the code; the language is loaded lazily. Defaults to true. */\n  highlight?: boolean;\n  /**\n   * Offers a comment button on each hovered line of a diff that names its file; called with the line\n   * the reader picked and the button that was pressed, so a composer can be floated at that line.\n   * Absent means no button.\n   */\n  onLineComment?: (target: DiffLineTarget, anchor: HTMLElement) => void;\n};\n\n/** A single line of a rendered diff, as a review comment addresses it. */\nexport type DiffLineTarget = {\n  file: string;\n  /** `before` is the removed/old column, `after` the added/new one (context lines count as `after`). */\n  side: 'before' | 'after';\n  /** 1-based line number in that version of the file. */\n  line: number;\n  text: string;\n};\n\n/**\n * Renders ```diff fenced blocks as diff chunks inside an otherwise ordinary markdown document, so a\n * walkthrough can interleave prose, headings and diagrams with the changes it narrates.\n *\n * This is deliberately not `@codemirror/merge`: both of its views take the WHOLE document as one\n * side of one diff, which cannot express \"these ten lines are a diff and the rest is prose\".\n *\n * The fence's info line carries the metadata a review header shows:\n * ```diff file=src/index.ts lines=66-99 lang=typescript```\n *\n * Blocks are atomic, as every widget from this registry is: the caret steps over a rendered chunk\n * rather than into it, and the fence is edited by replacing the block rather than in place.\n */\nexport const diffBlocks = (options: DiffBlocksOptions = {}): Extension => [\n  widgetsCore,\n  widgetMatchersFacet.of(createDiffMatcher(options)),\n  diffBlockTheme,\n  // The highlighted spans are static DOM, so the styles' rules are mounted directly rather than\n  // through `syntaxHighlighting`, which would also restyle the host document.\n  Object.values(diffHighlightStyles).flatMap((style) =>\n    style.module ? [EditorView.styleModule.of(style.module)] : [],\n  ),\n];\n\n/**\n * The fence's content, or undefined when it is not a diff. Sliced from the document rather than read\n * off the `CodeText` child because a fence whose language has a nested parser mounts that parser's\n * tree there, and the raw text is what needs parsing either way.\n */\nexport const parseDiffFence = (state: EditorState, node: SyntaxNodeRef): ParsedDiff | undefined => {\n  const text = state.sliceDoc(node.from, node.to);\n  const lines = text.split('\\n');\n  const opening = lines[0];\n  const fence = /^\\s*(`{3,}|~{3,})(.*)$/.exec(opening);\n  if (!fence) {\n    return undefined;\n  }\n\n  const info = parseFenceInfo(fence[2]);\n  if (info.language !== DIFF_LANGUAGE) {\n    return undefined;\n  }\n\n  // The closing fence is absent while the block is still being typed (or streamed in).\n  const last = lines[lines.length - 1];\n  const body = /^\\s*(`{3,}|~{3,})\\s*$/.test(last) ? lines.slice(1, -1) : lines.slice(1);\n  return parseDiff(body.join('\\n'), info);\n};\n\nconst createDiffMatcher = (options: DiffBlocksOptions): WidgetMatcher => ({\n  nodes: ['FencedCode'],\n  match: (node, { state }) => {\n    const parsed = parseDiffFence(state, node);\n    if (!parsed || parsed.chunks.length === 0) {\n      return undefined;\n    }\n\n    const { from, to } = node;\n    return {\n      from,\n      to,\n      decoration: Decoration.replace({\n        block: true,\n        widget: new DiffBlockWidget(parsed, state.sliceDoc(from, to), options),\n      }),\n    };\n  },\n});\n\n//\n// Widget\n//\n\n/** Where a rendered code cell gets its highlighted content from once the language resolves. */\ntype PendingCell = { element: HTMLElement; side: 'before' | 'after'; index: number };\n\n/** What a widget renders as before the observer has measured it; `auto` starts split. */\nconst initialLayout = (layout?: DiffLayout): 'split' | 'inline' => (layout === 'inline' ? 'inline' : 'split');\n\n/** Highlighting is on unless it was turned off, so absent and `true` are the same configuration. */\nconst highlighted = (options: DiffBlocksOptions): boolean => options.highlight !== false;\n\nclass DiffBlockWidget extends WidgetType {\n  /** Cleared on destroy so a language that resolves after the widget is gone writes nothing. */\n  #alive = true;\n  #observer?: ResizeObserver;\n  #layout: 'split' | 'inline';\n  #root?: HTMLElement;\n  #view?: EditorView;\n  readonly #diff: ParsedDiff;\n  /** The fence source, so a rebuilt widget over changed text is not reused. */\n  readonly #source: string;\n  readonly #options: DiffBlocksOptions;\n\n  constructor(diff: ParsedDiff, source: string, options: DiffBlocksOptions) {\n    super();\n    this.#diff = diff;\n    this.#source = source;\n    this.#options = options;\n    this.#layout = initialLayout(options.layout);\n  }\n\n  override eq(other: this): boolean {\n    // Only the DEFAULTS are normalized: an absent layout means `auto` and an absent `highlight`\n    // means on, so a widget configured either way renders identically and must not be torn down —\n    // that would lose the collapse the reader toggled and the width the observer measured. `auto`\n    // and `split` stay distinct despite both starting split, since only `auto` observes its width.\n    return (\n      this.#source === other.#source &&\n      (this.#options.layout ?? 'auto') === (other.#options.layout ?? 'auto') &&\n      highlighted(this.#options) === highlighted(other.#options) &&\n      this.#options.onLineComment === other.#options.onLineComment\n    );\n  }\n\n  override get estimatedHeight(): number {\n    const lines = this.#diff.chunks.reduce(\n      (count, chunk) =>\n        count +\n        // The unified layout renders a replaced pair as two lines, not one.\n        chunk.rows.reduce((rows, row) => rows + (this.#layout === 'inline' && row.kind === 'changed' ? 2 : 1), 0),\n      0,\n    );\n    const expanders = this.#diff.chunks.filter((chunk) => chunk.gap).length;\n    return CHROME_HEIGHT + expanders * EXPANDER_HEIGHT + lines * ROW_HEIGHT;\n  }\n\n  /** The widget owns its interactions (collapse, expanders); the editor should not also act on them. */\n  override ignoreEvent(): boolean {\n    return true;\n  }\n\n  override destroy(): void {\n    this.#alive = false;\n    this.#observer?.disconnect();\n    this.#observer = undefined;\n    this.#view = undefined;\n  }\n\n  /**\n   * CodeMirror caches a block widget's height, so collapsing one or reflowing its rows leaves every\n   * position below it mapped against a height that no longer exists.\n   */\n  #remeasure(): void {\n    this.#view?.requestMeasure();\n  }\n\n  override toDOM(view: EditorView): HTMLElement {\n    // A widget may be re-rendered after a destroy; revive it so a resolving language still lands.\n    this.#alive = true;\n    this.#view = view;\n    this.#observer?.disconnect();\n    const root = document.createElement('div');\n    root.className = 'cm-diff-block';\n    this.#root = root;\n    root.appendChild(this.#renderHeader());\n\n    const body = root.appendChild(document.createElement('div'));\n    body.className = 'cm-diff-body';\n    this.#renderBody(body);\n\n    if (this.#options.layout === 'auto' || this.#options.layout === undefined) {\n      // Measured rather than a container query, because the two layouts are different DOM: a unified\n      // row must not repeat a context line once per column.\n      this.#observer = new ResizeObserver(([entry]) => {\n        const { width } = entry.contentRect;\n        const next = width < NARROW_WIDTH ? 'inline' : width > WIDE_WIDTH ? 'split' : this.#layout;\n        if (next !== this.#layout) {\n          this.#layout = next;\n          this.#renderBody(body);\n          this.#remeasure();\n        }\n      });\n      this.#observer.observe(root);\n    }\n\n    return root;\n  }\n\n  #renderHeader(): HTMLElement {\n    const { file, language, range, added, removed } = this.#diff;\n    const header = document.createElement('div');\n    header.className = 'cm-diff-header';\n\n    const toggle = header.appendChild(document.createElement('button'));\n    toggle.className = 'cm-diff-toggle';\n    toggle.type = 'button';\n    toggle.setAttribute('aria-expanded', 'true');\n    toggle.setAttribute('aria-label', 'Collapse');\n    toggle.appendChild(caret());\n    toggle.addEventListener('click', () => {\n      const collapsed = header.parentElement?.classList.toggle('cm-diff-collapsed') ?? false;\n      toggle.setAttribute('aria-expanded', String(!collapsed));\n      toggle.setAttribute('aria-label', collapsed ? 'Expand' : 'Collapse');\n      this.#remeasure();\n    });\n\n    if (file) {\n      const path = header.appendChild(document.createElement('span'));\n      path.className = 'cm-diff-path';\n      const separator = file.lastIndexOf('/');\n      if (separator > 0) {\n        const directory = path.appendChild(document.createElement('span'));\n        directory.className = 'cm-diff-dir';\n        directory.textContent = file.slice(0, separator + 1);\n      }\n      const name = path.appendChild(document.createElement('span'));\n      name.className = 'cm-diff-name';\n      name.textContent = separator > 0 ? file.slice(separator + 1) : file;\n    }\n\n    if (added > 0) {\n      const stat = header.appendChild(document.createElement('span'));\n      stat.className = 'cm-diff-stat cm-diff-stat-added';\n      stat.textContent = `+${added}`;\n    }\n    if (removed > 0) {\n      const stat = header.appendChild(document.createElement('span'));\n      stat.className = 'cm-diff-stat cm-diff-stat-removed';\n      stat.textContent = `-${removed}`;\n    }\n\n    const label = languageLabel(language);\n    if (label) {\n      const element = header.appendChild(document.createElement('span'));\n      element.className = 'cm-diff-lang';\n      element.textContent = label;\n    }\n\n    if (range) {\n      const element = header.appendChild(document.createElement('span'));\n      element.className = 'cm-diff-range';\n      element.textContent = `Lines ${range.replace('-', '–')}`;\n    }\n\n    return header;\n  }\n\n  #renderBody(body: HTMLElement): void {\n    body.textContent = '';\n    body.dataset.layout = this.#layout;\n    // Also on the root, so the header can drop what does not fit at the narrow layout's widths.\n    if (this.#root) {\n      this.#root.dataset.layout = this.#layout;\n    }\n\n    for (const chunk of this.#diff.chunks) {\n      if (chunk.gap) {\n        body.appendChild(expander(chunk.gap, chunk.section));\n      }\n      const grid = body.appendChild(document.createElement('div'));\n      grid.className = 'cm-diff-grid';\n      const pending: PendingCell[] = [];\n      const before: string[] = [];\n      const after: string[] = [];\n      for (const row of chunk.rows) {\n        if (this.#layout === 'split') {\n          this.#renderSplitRow(grid, row, pending, before, after);\n        } else {\n          this.#renderInlineRow(grid, row, pending, before, after);\n        }\n      }\n\n      // Per chunk rather than per side of the whole block: each chunk is its own window into the\n      // file, read inside the declaration its hunk header names.\n      if (highlighted(this.#options) && this.#diff.language) {\n        void this.#highlight(this.#diff.language, chunk.section, before, after, pending);\n      }\n    }\n  }\n\n  #renderSplitRow(grid: HTMLElement, row: DiffRow, pending: PendingCell[], before: string[], after: string[]): void {\n    const removal = row.kind === 'removed' || row.kind === 'changed';\n    const addition = row.kind === 'added' || row.kind === 'changed';\n\n    if (row.before) {\n      grid.appendChild(number(row.before.number, removal ? '-' : undefined, removal ? 'removed' : undefined));\n      const cell = grid.appendChild(code(row.before.text, removal ? 'removed' : undefined));\n      this.#attachComment(cell, 'before', row.before.number, row.before.text);\n      pending.push({ element: cell, side: 'before', index: before.length });\n      before.push(row.before.text);\n    } else {\n      grid.appendChild(filler('cm-diff-num'));\n      grid.appendChild(filler('cm-diff-line'));\n    }\n\n    if (row.after) {\n      grid.appendChild(\n        number(row.after.number, addition ? '+' : undefined, addition ? 'added' : undefined, 'cm-diff-split'),\n      );\n      const cell = grid.appendChild(code(row.after.text, addition ? 'added' : undefined));\n      this.#attachComment(cell, 'after', row.after.number, row.after.text);\n      pending.push({ element: cell, side: 'after', index: after.length });\n      after.push(row.after.text);\n    } else {\n      grid.appendChild(filler('cm-diff-num', 'cm-diff-split'));\n      grid.appendChild(filler('cm-diff-line'));\n    }\n  }\n\n  #renderInlineRow(grid: HTMLElement, row: DiffRow, pending: PendingCell[], before: string[], after: string[]): void {\n    // A `changed` row is one pairing for the split layout and two lines for the unified one.\n    const lines: DiffRow[] =\n      row.kind === 'changed'\n        ? [\n            { kind: 'removed', before: row.before },\n            { kind: 'added', after: row.after },\n          ]\n        : [row];\n\n    for (const line of lines) {\n      const cell = line.before ?? line.after;\n      if (!cell) {\n        continue;\n      }\n      const status = line.kind === 'added' ? 'added' : line.kind === 'removed' ? 'removed' : undefined;\n      grid.appendChild(number(line.before?.number, undefined, status));\n      grid.appendChild(number(line.after?.number, undefined, status));\n      grid.appendChild(mark(line.kind === 'added' ? '+' : line.kind === 'removed' ? '-' : '', status));\n      const element = grid.appendChild(code(cell.text, status));\n      // Context lines are highlighted from the `after` text, which holds every line the result has.\n      const side = line.kind === 'removed' ? 'before' : 'after';\n      const lineNumber = side === 'before' ? line.before?.number : line.after?.number;\n      if (lineNumber !== undefined) {\n        this.#attachComment(element, side, lineNumber, cell.text);\n      }\n      const source = side === 'before' ? before : after;\n      pending.push({ element, side, index: source.length });\n      source.push(cell.text);\n    }\n  }\n\n  /** Adds the hover comment button to a code cell, when a callback was given and the diff names its file. */\n  #attachComment(cell: HTMLElement, side: DiffLineTarget['side'], line: number, text: string): void {\n    const { onLineComment } = this.#options;\n    const { file } = this.#diff;\n    if (!onLineComment || !file) {\n      return;\n    }\n    cell.classList.add('cm-diff-commentable');\n    const button = document.createElement('button');\n    button.type = 'button';\n    button.className = 'cm-diff-comment';\n    button.setAttribute('aria-label', `Comment on line ${line}`);\n    button.textContent = '+';\n    button.addEventListener('mousedown', (event) => event.preventDefault());\n    button.addEventListener('click', (event) => {\n      event.stopPropagation();\n      onLineComment({ file, side, line, text }, button);\n    });\n    // Prepended so highlighting, which replaces the cell's text content, is applied after and must keep it.\n    cell.prepend(button);\n  }\n\n  async #highlight(\n    language: string,\n    section: string | undefined,\n    before: string[],\n    after: string[],\n    pending: PendingCell[],\n  ): Promise<void> {\n    const style = diffHighlightStyles[this.#view?.state.facet(EditorView.darkTheme) ? 'dark' : 'light'];\n    const [beforeLines, afterLines] = await Promise.all([\n      before.length > 0 ? highlightLines(before.join('\\n'), language, section, style) : undefined,\n      after.length > 0 ? highlightLines(after.join('\\n'), language, section, style) : undefined,\n    ]);\n    if (!this.#alive || (!beforeLines && !afterLines)) {\n      return;\n    }\n\n    for (const { element, side, index } of pending) {\n      const fragment = (side === 'before' ? beforeLines : afterLines)?.[index];\n      if (fragment) {\n        // The comment button lives in the cell; keep it across the text swap.\n        const button = element.querySelector(':scope > .cm-diff-comment');\n        element.textContent = '';\n        if (button) {\n          element.appendChild(button);\n        }\n        element.appendChild(fragment);\n      }\n    }\n  }\n}\n\n//\n// Cells\n//\n\nconst number = (value: number | undefined, marker?: string, status?: string, ...classes: string[]): HTMLElement => {\n  const element = document.createElement('div');\n  element.className = ['cm-diff-num', status && `cm-diff-${status}`, ...classes].filter(Boolean).join(' ');\n  if (value !== undefined) {\n    const digits = element.appendChild(document.createElement('span'));\n    digits.textContent = String(value);\n  }\n  if (marker) {\n    const sign = element.appendChild(document.createElement('span'));\n    sign.className = 'cm-diff-sign';\n    sign.textContent = marker;\n  }\n\n  return element;\n};\n\nconst mark = (marker: string, status?: string): HTMLElement => {\n  const element = document.createElement('div');\n  element.className = ['cm-diff-mark', status && `cm-diff-${status}`].filter(Boolean).join(' ');\n  element.textContent = marker;\n  return element;\n};\n\nconst code = (text: string, status?: string): HTMLElement => {\n  const element = document.createElement('div');\n  element.className = ['cm-diff-line', status && `cm-diff-${status}`].filter(Boolean).join(' ');\n  // A blank line still needs to occupy its row.\n  element.textContent = text.length > 0 ? text : ' ';\n  return element;\n};\n\n/** The empty half of a one-sided row, hatched so it reads as absent rather than as a blank line. */\nconst filler = (kind: string, ...classes: string[]): HTMLElement => {\n  const element = document.createElement('div');\n  element.className = [kind, 'cm-diff-filler', ...classes].join(' ');\n  return element;\n};\n\nconst expander = (lines: number, section?: string): HTMLElement => {\n  const element = document.createElement('div');\n  element.className = 'cm-diff-expander';\n  const icon = element.appendChild(arrow());\n  icon.classList.add('cm-diff-expander-icon');\n  const label = element.appendChild(document.createElement('span'));\n  label.textContent = `${lines} ${lines === 1 ? 'line' : 'lines'}`;\n  if (section) {\n    const context = element.appendChild(document.createElement('span'));\n    context.className = 'cm-diff-section';\n    context.textContent = section;\n  }\n\n  return element;\n};\n\n//\n// Icons\n//\n\nconst svg = (path: string): SVGElement => {\n  const element = document.createElementNS('http://www.w3.org/2000/svg', 'svg');\n  element.setAttribute('viewBox', '0 0 16 16');\n  element.setAttribute('fill', 'none');\n  element.setAttribute('stroke', 'currentColor');\n  element.setAttribute('stroke-width', '1.5');\n  element.setAttribute('stroke-linecap', 'round');\n  element.setAttribute('stroke-linejoin', 'round');\n  const shape = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n  shape.setAttribute('d', path);\n  element.appendChild(shape);\n  return element;\n};\n\nconst caret = (): SVGElement => svg('M4 6l4 4 4-4');\n\nconst arrow = (): SVGElement => svg('M8 13V3M4 7l4-4 4 4');\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport { type EditorState, type Extension } from '@codemirror/state';\nimport { EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';\nimport { type Tree } from '@lezer/common';\n\nimport { parseDiffFence } from './diff-block.ts';\n\n/** Headings the outline is built from; deeper ones are noise in a rail this size. */\nconst HEADINGS: Record<string, number> = {\n  ATXHeading1: 1,\n  ATXHeading2: 2,\n  ATXHeading3: 3,\n  SetextHeading1: 1,\n  SetextHeading2: 2,\n};\n\n/**\n * A heading's text without its markers.\n *\n * Only an ATX heading has them: CommonMark closes one with a run of `#` that is either preceded by\n * a space or is all that remains, and that run is syntax. An ATX heading ending in `C#` keeps its\n * hash, since no space separates it from the word. The gate on the node name is what makes the\n * setext entries in `HEADINGS` safe — a setext heading's line is literal — even though the markdown\n * configuration here currently parses one as a paragraph instead.\n */\nconst headingTitle = (node: string, text: string): string => {\n  const line = text.split('\\n')[0];\n  if (!node.startsWith('ATXHeading')) {\n    return line.trim();\n  }\n\n  return line\n    .replace(/^#+\\s*/, '')\n    .replace(/(?:\\s+|^)#+\\s*$/, '')\n    .trim();\n};\n\n/** One file under a section, as its own navigable row on the rail's second level. */\nexport type WalkthroughFile = {\n  /** Document position of the file's first diff block, which its row scrolls to. */\n  from: number;\n  /** Base name, the only part that fits the rail's width. */\n  name: string;\n  added: number;\n  removed: number;\n};\n\nexport type WalkthroughEntry = {\n  /** Document position the entry scrolls to. */\n  from: number;\n  level: number;\n  title: string;\n  /** The files whose diffs appear under this heading, in the order they first appear. */\n  files: WalkthroughFile[];\n  added: number;\n  removed: number;\n};\n\n/**\n * The document's sections and the diff blocks beneath each, in document order. Exported so a host\n * can render its own navigation from the same reading of the document the rail uses.\n */\nexport const walkthroughOutline = (state: EditorState): WalkthroughEntry[] => {\n  const entries: WalkthroughEntry[] = [];\n  syntaxTree(state).iterate({\n    enter: (node) => {\n      const level = HEADINGS[node.name];\n      if (level) {\n        const title = headingTitle(node.name, state.sliceDoc(node.from, node.to));\n        entries.push({ from: node.from, level, title, files: [], added: 0, removed: 0 });\n        return false;\n      }\n\n      if (node.name === 'FencedCode') {\n        const diff = parseDiffFence(state, node);\n        if (diff) {\n          // A diff before any heading still belongs somewhere; give it an untitled lead section.\n          const entry = entries.at(-1) ?? { from: 0, level: 1, title: '', files: [], added: 0, removed: 0 };\n          if (entries.length === 0) {\n            entries.push(entry);\n          }\n          const name = diff.file?.split('/').pop();\n          if (name) {\n            // A file split across several chunks stays ONE row, carrying their combined counts,\n            // and keeps the position of the first chunk so its row scrolls to the top of the file.\n            const file = entry.files.find((candidate) => candidate.name === name);\n            if (file) {\n              file.added += diff.added;\n              file.removed += diff.removed;\n            } else {\n              entry.files.push({ from: node.from, name, added: diff.added, removed: diff.removed });\n            }\n          }\n          entry.added += diff.added;\n          entry.removed += diff.removed;\n        }\n        return false;\n      }\n\n      return undefined;\n    },\n  });\n\n  return entries;\n};\n\nexport type WalkthroughSidebarOptions = {\n  /** `stats` shows change counts only, the rail Graphite collapses to; `full` adds titles and files. */\n  variant?: 'full' | 'stats';\n  /** Fraction of the viewport height at which an entry becomes the current one. */\n  threshold?: number;\n};\n\n/**\n * A navigation rail for a walkthrough document, on two levels: a row per section, and beneath it a\n * row per file it touches, each carrying its own change counts and each scrolling to what it names.\n * The row under the reader's eye is marked as current.\n *\n * Deliberately separate from {@link diffBlocks} — it only reads the document, so it can be added,\n * dropped or replaced by a host-rendered panel without touching how the chunks render.\n */\nexport const walkthroughSidebar = (options: WalkthroughSidebarOptions = {}): Extension => [\n  ViewPlugin.fromClass(\n    class {\n      readonly #view: EditorView;\n      readonly #rail: HTMLElement;\n      readonly #onScroll = () => this.#scheduleMark();\n\n      #entries: WalkthroughEntry[] = [];\n      /** Every row on the rail, section and file alike, in document order — the navigable sequence. */\n      #rows: { from: number; element: HTMLElement }[] = [];\n      /** The tree the outline was read from, so a background parse advancing it rebuilds the rail. */\n      #tree?: Tree;\n      #frame?: number;\n\n      constructor(view: EditorView) {\n        this.#view = view;\n        this.#rail = document.createElement('div');\n        // `dx-scrollbar-thin` gives the rail the app's scrollbar; a bare overflow box draws the browser's.\n        this.#rail.className = 'cm-walkthrough-sidebar dx-scrollbar-thin';\n        this.#rail.dataset.variant = options.variant ?? 'full';\n        view.dom.appendChild(this.#rail);\n        view.scrollDOM.addEventListener('scroll', this.#onScroll, { passive: true });\n        this.#render();\n      }\n\n      update(update: ViewUpdate): void {\n        // Not `docChanged` alone: a long document is parsed in the background, so the sections past\n        // the first parse window appear on a later transaction that changes nothing — and a\n        // read-only walkthrough never produces a document change at all.\n        if (update.docChanged || syntaxTree(update.state) !== this.#tree) {\n          this.#render();\n        } else if (update.viewportChanged || update.geometryChanged) {\n          this.#scheduleMark();\n        }\n      }\n\n      destroy(): void {\n        this.#view.scrollDOM.removeEventListener('scroll', this.#onScroll);\n        if (this.#frame !== undefined) {\n          cancelAnimationFrame(this.#frame);\n        }\n        this.#rail.remove();\n      }\n\n      #render(): void {\n        this.#tree = syntaxTree(this.#view.state);\n        const entries = walkthroughOutline(this.#view.state);\n        // The tree advances far more often than the outline changes; rebuilding the DOM every time\n        // would drop the rail's scroll position and restart its transitions.\n        if (sameOutline(entries, this.#entries) && this.#rows.length > 0) {\n          return;\n        }\n\n        this.#entries = entries;\n        this.#rail.textContent = '';\n        // An empty rail still paints a border and still insets the prose by its width.\n        this.#rail.toggleAttribute('data-empty', entries.length === 0);\n        this.#rows = [];\n        const collapsed = (options.variant ?? 'full') === 'stats';\n        for (const entry of entries) {\n          // A section whose one file row repeats its totals shows them once, on the file; the\n          // collapsed rail has no file rows, so there it keeps them.\n          const ownCounts = collapsed || entry.files.length !== 1;\n          this.#rows.push({\n            from: entry.from,\n            element: this.#addRow({\n              className: 'cm-walkthrough-entry',\n              level: entry.level,\n              from: entry.from,\n              text: entry.title,\n              added: ownCounts ? entry.added : 0,\n              removed: ownCounts ? entry.removed : 0,\n              label: entryLabel(entry, ownCounts),\n            }),\n          });\n\n          for (const file of entry.files) {\n            this.#rows.push({\n              from: file.from,\n              element: this.#addRow({\n                className: 'cm-walkthrough-file',\n                level: entry.level,\n                from: file.from,\n                text: file.name,\n                added: file.added,\n                removed: file.removed,\n                label: fileLabel(file),\n              }),\n            });\n          }\n        }\n\n        this.#scheduleMark();\n      }\n\n      /** One navigable row: a name, its change counts, and the position it scrolls to. */\n      #addRow(spec: {\n        className: string;\n        level: number;\n        from: number;\n        text: string;\n        added: number;\n        removed: number;\n        label: string;\n      }): HTMLElement {\n        const row = this.#rail.appendChild(document.createElement('button'));\n        row.type = 'button';\n        row.className = spec.className;\n        row.dataset.level = String(spec.level);\n        row.addEventListener('click', () => {\n          this.#view.dispatch({ effects: EditorView.scrollIntoView(spec.from, { y: 'start', yMargin: 24 }) });\n        });\n\n        const name = row.appendChild(document.createElement('span'));\n        name.className = 'cm-walkthrough-name';\n        name.textContent = spec.text;\n\n        const stats = row.appendChild(document.createElement('span'));\n        stats.className = 'cm-walkthrough-stats';\n        if (spec.added > 0) {\n          const added = stats.appendChild(document.createElement('span'));\n          added.className = 'cm-walkthrough-added';\n          added.textContent = `+${spec.added}`;\n        }\n        if (spec.removed > 0) {\n          const removed = stats.appendChild(document.createElement('span'));\n          removed.className = 'cm-walkthrough-removed';\n          removed.textContent = `-${spec.removed}`;\n        }\n\n        // The `stats` variant hides the name, and a row with no counts then has no text at all;\n        // name it from the outline either way.\n        row.setAttribute('aria-label', spec.label);\n\n        return row;\n      }\n\n      /**\n       * Coalesced to one frame: a scroll event fires far faster than the rail can usefully change,\n       * and marking reads the height map once per section.\n       */\n      #scheduleMark(): void {\n        if (this.#frame !== undefined) {\n          return;\n        }\n        this.#frame = requestAnimationFrame(() => {\n          this.#frame = undefined;\n          this.#markCurrent();\n        });\n      }\n\n      /** The last row — section or file — whose target has passed the reading line. */\n      #markCurrent(): void {\n        const { scrollTop, clientHeight } = this.#view.scrollDOM;\n        const line = scrollTop + clientHeight * (options.threshold ?? 0.25);\n        let current = -1;\n        for (let index = 0; index < this.#rows.length; index++) {\n          // The height map, not `coordsAtPos`: a target scrolled out of the RENDERED range has no\n          // coordinates, and a long section would then clear the mark rather than keep it.\n          const block = this.#view.lineBlockAt(this.#rows[index].from);\n          if (block.top <= line) {\n            current = index;\n          }\n        }\n\n        this.#rows.forEach(({ element }, index) => element.toggleAttribute('data-current', index === current));\n      }\n    },\n  ),\n  walkthroughSidebarTheme,\n];\n\n/** Accessible name for a rail button, from the outline rather than from whatever the variant shows. */\nconst entryLabel = (entry: WalkthroughEntry, ownCounts: boolean): string => {\n  const parts = [entry.title || 'Untitled section'];\n  if (entry.files.length > 0) {\n    parts.push(entry.files.map((file) => file.name).join(', '));\n  }\n  const announced = ownCounts ? counts(entry) : '';\n  if (announced.length > 0) {\n    parts.push(announced);\n  }\n  return parts.join(' — ');\n};\n\n/** Built the way a row is, so a section with no removals is not announced as `-0`. */\nconst counts = ({ added, removed }: { added: number; removed: number }): string =>\n  [added > 0 ? `+${added}` : undefined, removed > 0 ? `-${removed}` : undefined]\n    .filter((count) => count !== undefined)\n    .join(' ');\n\n/** Accessible name for a file row, whose counts the `stats` variant shows without the name. */\nconst fileLabel = (file: WalkthroughFile): string => [file.name, counts(file)].filter(Boolean).join(' — ');\n\n/** Whether the rail would render the same rows, so an unchanged outline does not rebuild it. */\nconst sameOutline = (left: WalkthroughEntry[], right: WalkthroughEntry[]): boolean =>\n  left.length === right.length &&\n  left.every((entry, index) => {\n    const other = right[index];\n    return (\n      entry.from === other.from &&\n      entry.title === other.title &&\n      entry.level === other.level &&\n      entry.added === other.added &&\n      entry.removed === other.removed &&\n      entry.files.length === other.files.length &&\n      entry.files.every((file, fileIndex) => {\n        const otherFile = other.files[fileIndex];\n        return (\n          file.from === otherFile.from &&\n          file.name === otherFile.name &&\n          file.added === otherFile.added &&\n          file.removed === otherFile.removed\n        );\n      })\n    );\n  });\n\nconst walkthroughSidebarTheme = EditorView.theme({\n  // The rail is absolutely positioned against the editor, which must therefore be its containing block.\n  '&': { '--cm-walkthrough-width': '17rem', 'position': 'relative' },\n  '&:has(.cm-walkthrough-sidebar[data-variant=\"stats\"])': { '--cm-walkthrough-width': '6rem' },\n  '.cm-walkthrough-sidebar': {\n    position: 'absolute',\n    insetBlock: '0',\n    insetInlineEnd: '0',\n    width: 'var(--cm-walkthrough-width)',\n    display: 'flex',\n    flexDirection: 'column',\n    gap: '0.0625rem',\n    padding: '1rem 0.5rem',\n    overflowY: 'auto',\n    borderInlineStart: '1px solid var(--color-separator-subtle)',\n    fontFamily: 'var(--font-body)',\n  },\n  // The rail overlays the editor, so the text is inset by exactly its width.\n  '.cm-scroller': { paddingInlineEnd: 'var(--cm-walkthrough-width)' },\n\n  '.cm-walkthrough-entry, .cm-walkthrough-file': {\n    display: 'grid',\n    gridTemplateColumns: 'minmax(0, 1fr) auto',\n    alignItems: 'baseline',\n    gap: '0 0.5rem',\n    padding: '0.25rem 0.5rem',\n    border: 'none',\n    borderRadius: '0.375rem',\n    background: 'transparent',\n    textAlign: 'start',\n    cursor: 'pointer',\n  },\n  '.cm-walkthrough-entry:hover, .cm-walkthrough-file:hover': { background: 'var(--color-hover-surface)' },\n  '.cm-walkthrough-entry[data-current], .cm-walkthrough-file[data-current]': {\n    background: 'var(--color-current-surface)',\n  },\n  '.cm-walkthrough-entry': { marginBlockStart: '0.75rem' },\n  '.cm-walkthrough-entry:first-child': { marginBlockStart: '0' },\n  '.cm-walkthrough-entry[data-level=\"3\"] .cm-walkthrough-name': { paddingInlineStart: '0.75rem' },\n\n  '.cm-walkthrough-entry .cm-walkthrough-name': {\n    color: 'var(--color-fg)',\n    fontSize: '0.8125rem',\n  },\n  // The second level: one file per row, indented under the section that touches it.\n  '.cm-walkthrough-file': { paddingInlineStart: '1.25rem' },\n  '.cm-walkthrough-file[data-level=\"3\"]': { paddingInlineStart: '2rem' },\n  '.cm-walkthrough-file .cm-walkthrough-name': {\n    color: 'var(--color-fg-subtle)',\n    fontSize: '0.75rem',\n  },\n  '.cm-walkthrough-name': {\n    overflow: 'hidden',\n    textOverflow: 'ellipsis',\n    whiteSpace: 'nowrap',\n  },\n  '.cm-walkthrough-stats': {\n    display: 'flex',\n    gap: '0.25rem',\n    fontSize: '0.75rem',\n    fontVariantNumeric: 'tabular-nums',\n  },\n  // Nothing to navigate: the rail neither paints nor insets the prose.\n  '&:has(.cm-walkthrough-sidebar[data-empty])': { '--cm-walkthrough-width': '0px' },\n  '.cm-walkthrough-sidebar[data-empty]': { display: 'none' },\n\n  '.cm-walkthrough-added': { color: 'var(--color-cm-diff-add-gutter)' },\n  '.cm-walkthrough-removed': { color: 'var(--color-cm-diff-remove-gutter)' },\n\n  // Collapsed: the section counts alone, as a rail beside the prose — a per-file breakdown needs\n  // the names beside it to mean anything.\n  '.cm-walkthrough-sidebar[data-variant=\"stats\"] .cm-walkthrough-name': { display: 'none' },\n  '.cm-walkthrough-sidebar[data-variant=\"stats\"] .cm-walkthrough-file': { display: 'none' },\n  '.cm-walkthrough-sidebar[data-variant=\"stats\"] .cm-walkthrough-entry': {\n    gridTemplateColumns: 'minmax(0, 1fr)',\n    justifyItems: 'end',\n    minHeight: '1.75rem',\n  },\n});\n"],"mappings":"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"}