@dxos/ui-editor 0.14.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/lib/chunk-diff.mjs +14 -3
  2. package/dist/lib/chunk-diff.mjs.map +1 -1
  3. package/dist/lib/headless.mjs +1 -2
  4. package/dist/lib/index.mjs +146 -23
  5. package/dist/lib/index.mjs.map +1 -1
  6. package/dist/types/src/extensions/language/index.d.ts +1 -0
  7. package/dist/types/src/extensions/language/index.d.ts.map +1 -1
  8. package/dist/types/src/extensions/language/markdown/styles.d.ts.map +1 -1
  9. package/dist/types/src/extensions/language/turtle/index.d.ts +2 -0
  10. package/dist/types/src/extensions/language/turtle/index.d.ts.map +1 -0
  11. package/dist/types/src/extensions/language/turtle/turtle.d.ts +45 -0
  12. package/dist/types/src/extensions/language/turtle/turtle.d.ts.map +1 -0
  13. package/dist/types/src/extensions/language/turtle/turtle.test.d.ts +2 -0
  14. package/dist/types/src/extensions/language/turtle/turtle.test.d.ts.map +1 -0
  15. package/dist/types/src/extensions/state/selection.d.ts.map +1 -1
  16. package/dist/types/src/extensions/widgets/widgets.d.ts.map +1 -1
  17. package/dist/types/tsconfig.tsbuildinfo +1 -1
  18. package/package.json +24 -24
  19. package/src/extensions/language/index.ts +1 -0
  20. package/src/extensions/language/markdown/styles.ts +2 -0
  21. package/src/extensions/language/turtle/index.ts +5 -0
  22. package/src/extensions/language/turtle/turtle.test.ts +106 -0
  23. package/src/extensions/language/turtle/turtle.ts +183 -0
  24. package/src/extensions/state/selection.test.ts +70 -2
  25. package/src/extensions/state/selection.ts +19 -4
  26. package/src/extensions/widgets/object-links.test.ts +17 -1
  27. package/src/extensions/widgets/widgets.ts +18 -17
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"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/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, syntaxTreeAvailable } 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 `parseCompletionPlugin` 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 once the whole document has parsed. `StateField.create` (and incremental rebuilds) only\n * see the syntax tree parsed so far, so a block past CodeMirror's initial background-parse window is\n * not decorated until some later transaction — previously it appeared only after the first edit. This\n * dispatches a one-shot rebuild when the full tree becomes available (re-armed on each edit).\n */\nconst parseCompletionPlugin = ViewPlugin.fromClass(\n class {\n #rebuiltForLength = -1;\n\n update(update: ViewUpdate) {\n if (update.docChanged) {\n this.#rebuiltForLength = -1;\n }\n const length = update.state.doc.length;\n if (this.#rebuiltForLength !== length && syntaxTreeAvailable(update.state, length)) {\n this.#rebuiltForLength = length;\n // Defer: cannot dispatch during an update.\n queueMicrotask(() => 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 parseCompletionPlugin,\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 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 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 // `\` 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAQA,IAAa,oBAAoB,SAC/B,GACE,yEACA,SAAS,0BACL,+DACA,2BACN;AAEF,IAAa,gBAAiD,EAC5D,SAAS;;;;;;;AAOP,WAAW,GAKT,iCAEA,yFACA,uBACF,EACF,EACF;AAEA,IAAa,eAAgD,EAC3D,SAAS,EACP,WAAW,QACb,EACF;AAEA,IAAa,cAA+C,EAC1D,SAAS,EACP,WAAW,QACb,EACF;;;AClCA,IAAa,cAAc,YAAY,OAAiB;AACxD,IAAa,iBAAiB,YAAY,OAAe;AACzD,IAAa,iBAAiB,YAAY,OAAa;;;;AAKvD,IAAa,kBAA6B;CACxC,OAAO,CACL,gBACA,KAAK,QACH,SAAO,GAAG,CACR;EACE,KAAK;EACL,MAAM,SAAS,iBAAiB,MAAM,IAAI;CAC5C,GACA;EACE,KAAK;EACL,MAAM,SAAS,iBAAiB,MAAM,MAAM;CAC9C,CACF,CAAC,CACH,CACF;AACF;AAIA,IAAM,oBAAoB,MAAkB,cAAsC;CAChF,MAAM,EAAE,cAAc,KAAK,MAAM,MAAM,cAAc;CACrD,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;CACtC,MAAM,SACJ,cAAc,OACV,CAAC,GAAG,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,aAAa,SAAS,MAAM,GAAG,IAC9D,UAAU,MAAM,aAAa,SAAS,MAAM,GAAG;CACrD,IAAI,CAAC,QACH,OAAO;CAGT,KAAK,SAAS;EAAE,WAAW,kBAAgB,OAAO,OAAO,GAAG;EAAG,gBAAgB;CAAK,CAAC;CACrF,OAAO;AACT;AAMA,IAAM,iBAAiB,WAAW,OAA2B;CAC3D,eAAmC,EACjC,WAAW,CAAC,EACd;CAEA,SAAS,OAA2B,OAA2B;EAE7D,IAAI,YAAY,MAAM,UAAU,KAAK,cAAc;GACjD,GAAG;GACH,KAAK,GAAG,QAAQ,OAAO,SAAS,GAAG;EACrC,EAAE;EAGF,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,WAAW,GACvB,YAAY,CAAC,GAAG,WAAW,OAAO,KAAK;OAClC,IAAI,OAAO,GAAG,cAAc,GACjC,YAAY,UAAU,QAAQ,MAAM,EAAE,OAAO,OAAO,KAAK;OACpD,IAAI,OAAO,GAAG,cAAc,GACjC,YAAY,CAAC;EAKjB,UAAU,MAAM,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,IAAI,CAAC;EAChD,OAAO,EAAE,UAAU;CACrB;AACF,CAAC;;;;;;;ACvED,IAAa,YAAY,UAAuB,CAAC,MAAiB;CAChE,OAAO;EACL;EACA;EACA;EACA,aAAW,iBAAiB,EAC1B,OAAO,OAAO,SAAS;GAErB,IAAI,CAAC,WAAW,KAAK,GACnB,OAAO;GAGT,MAAM,eAAe;GACrB,MAAM,QAAQ,MAAM,cAAc;GAClC,MAAM,MAAM,KAAK,YAAY,KAAK;GAClC,IAAI,OAAO,UAAU,QAAQ,MAAM;IACjC,KAAK,SAAS,EAAE,WAAW,EAAE,QAAQ,IAAI,EAAE,CAAC;IAC5C,QAAQ,SAAS,MAAM,EAAE,MAAM,CAAC;GAClC;GACA,OAAO;EACT,EACF,CAAC;CACH;AACF;;;;;;;AAQA,IAAM,cAAc,UAClB,CAAC,CAAC,MAAM,gBAAgB,MAAM,KAAK,MAAM,aAAa,KAAK,CAAC,CAAC,SAAS,OAAO;AAE/E,IAAM,mBAAmB,YAAY,OAAsB;AAE3D,IAAM,gBAAgB,WAAW,OAAsB;CACrD,cAAc;CACd,OAAO,KAAK,IAAI;EACd,IAAI,OAAO,QAAQ,GAAG,YACpB,MAAM,GAAG,QAAQ,OAAO,GAAG;EAE7B,OAAO,GAAG,QAAQ,QAAQ,OAAO,WAAY,OAAO,GAAG,gBAAgB,IAAI,OAAO,QAAQ,OAAQ,GAAG;CACvG;AACF,CAAC;AAMD,IAAM,iBAAiB,WAAW,UAChC,MAAM;CAIiB;CAHrB,SAA6B;CAC7B;CAEA,YAAY,MAA2B;EAAlB,KAAA,OAAA;EACnB,KAAK,aAAa;GAAE,MAAM,KAAK,QAAQ,KAAK,IAAI;GAAG,OAAO,KAAK,WAAW,KAAK,IAAI;EAAE;CACvF;CAEA,OAAO,QAAoB;EACzB,MAAM,MAAM,OAAO,MAAM,MAAM,aAAa;EAC5C,IAAI,OAAO,MAAM;GACf,KAAK,QAAQ,OAAO;GACpB,KAAK,SAAS;EAChB,OAAO;GACL,IAAI,CAAC,KAAK,QAAQ;IAChB,KAAK,SAAS,KAAK,KAAK,UAAU,YAAY,SAAS,cAAc,KAAK,CAAC;IAC3E,KAAK,OAAO,YAAY;GAC1B;GACA,IAAI,OAAO,WAAW,MAAM,aAAa,MAAM,OAAO,OAAO,cAAc,OAAO,iBAChF,KAAK,KAAK,eAAe,KAAK,UAAU;EAE5C;CACF;CAEA,UAAgC;EAC9B,MAAM,MAAM,KAAK,KAAK,MAAM,MAAM,aAAa;EAC/C,MAAM,OAAO,OAAO,QAAQ,KAAK,KAAK,YAAY,GAAG;EACrD,IAAI,CAAC,MACH,OAAO;EAET,MAAM,QAAQ,KAAK,KAAK,UAAU,sBAAsB;EACxD,OAAO;GACL,MAAM,KAAK,OAAO,MAAM,OAAO,KAAK,KAAK,UAAU,aAAa,KAAK,KAAK;GAC1E,KAAK,KAAK,MAAM,MAAM,MAAM,KAAK,KAAK,UAAU,YAAY,KAAK,KAAK;GACtE,QAAQ,KAAK,SAAS,KAAK;EAC7B;CACF;CAEA,WAAW,SAA+B;EACxC,IAAI,CAAC,KAAK,QACR;EAEF,MAAM,EAAE,QAAQ,WAAW,KAAK;EAChC,IAAI,SAAS;GACX,KAAK,OAAO,MAAM,OAAO,QAAQ,OAAO,SAAS;GACjD,KAAK,OAAO,MAAM,MAAM,QAAQ,MAAM,SAAS;GAC/C,KAAK,OAAO,MAAM,SAAS,QAAQ,SAAS,SAAS;EACvD,OACE,KAAK,OAAO,MAAM,OAAO;CAE7B;CAEA,UAAU;EACR,KAAK,QAAQ,OAAO;CACtB;CAEA,WAAW,KAAoB;EAC7B,IAAI,KAAK,KAAK,MAAM,MAAM,aAAa,MAAM,KAC3C,KAAK,KAAK,SAAS,EAAE,SAAS,iBAAiB,GAAG,GAAG,EAAE,CAAC;CAE5D;AACF,GACA,EACE,gBAAgB;CACd,SAAS,OAAO;EACd,IAAI,WAAW,KAAK,GAAG;GAErB,MAAM,eAAe;GACrB,KAAK,WAAW,KAAK,KAAK,YAAY;IAAE,GAAG,MAAM;IAAS,GAAG,MAAM;GAAQ,CAAC,CAAC;EAC/E;CACF;CACA,UAAU,OAAO;EACf,MAAM,UAAU,MAAM,yBAAyB,OAAO,MAAM,gBAAgB;EAC5E,IAAI,MAAM,WAAW,KAAK,KAAK,cAAc,CAAC,KAAK,KAAK,WAAW,SAAS,OAAO,GACjF,KAAK,WAAW,IAAI;CAExB;CACA,UAAU;EACR,KAAK,WAAW,IAAI;CACtB;CACA,OAAO;EACL,KAAK,WAAW,IAAI;CACtB;AACF,EACF,CACF;AAEA,IAAM,YAAS,aAAW,MAAM;CAC9B,kBAAkB;EAChB,YAAY;EACZ,OAAO;EACP,SAAS;CACX;CACA,wBAAwB,EACtB,SAAS,QACX;AACF,CAAC;;;;;;;;;;;ACrID,IAAa,cAAc,EACzB,UACA,UACA,eAAe,MACf,gBAAgB,SACK,CAAC,MAAiB;CACvC,OAAO;EAIL,gBACI,KAAK,QACH,SAAO,GAAG,CACR;GACE,KAAK;GACL,gBAAgB;GAChB,MAAM,SAAS;IACb,WAAW,KAAK,MAAM,IAAI,SAAS,CAAC;IACpC,OAAO;GACT;EACF,GACA;GACE,KAAK;GACL,gBAAgB;GAChB,MAAM,SAAS;IACb,KAAK,SAAS,KAAK,MAAM,iBAAiB,IAAI,CAAC;IAC/C,OAAO;GACT;EACF,CACF,CAAC,CACH,IACA,CAAC;EACL,KAAK,QACH,SAAO,GAAG,CACR;GACE,KAAK;GACL,gBAAgB;GAChB,WAAW;IACT,WAAW;IAEX,OAAO;GACT;EACF,CACF,CAAC,CACH;EACA,aAAW,iBAAiB,EAC1B,OAAO,QAAQ,SAAS;GACtB,IAAI,cACF,WAAW,KAAK,MAAM,IAAI,SAAS,CAAC;QAEpC,WAAW;GAEb,OAAO;EACT,EACF,CAAC;CACH;AACF;;;AC9EA,IAAa,wBAAwB,SAAsD;CACzF,MAAM,SAA8B,CAAC;CACrC,MAAM,OAAO,KAAK,KAAK;CACvB,OAAO,KAAK,OAAO;EACjB,OAAO,KAAK;GACV,MAAM,KAAK;GACX,IAAI,KAAK;GACT,OAAO,KAAK;EACd,CAAC;EACD,KAAK,KAAK;CACZ;CAEA,OAAO;AACT;;;ACKA,IAAM,kBAAkB;AACxB,IAAM,qBAAqB;AAC3B,IAAM,qBAAqB;;;;;;;;;AAU3B,IAAa,qBACX,SACA,MACA,EAAE,UAAU,iBAAiB,YAAY,oBAAoB,YAAY,uBAA0C,CAAC,MACpG;CAChB,IAAI,YAAY,MACd,OAAO,CAAC;CAGV,MAAM,aAA0B,CAAC;EAAE,MAAM;EAAG,IAAI,QAAQ;EAAQ,QAAQ;CAAK,CAAC;CAC9E,IAAI,QAAQ,WAAW,KAAK,KAAK,WAAW,KAAK,QAAQ,SAAS,aAAa,KAAK,SAAS,WAC3F,OAAO;CAGT,MAAM,UAAU,KAAK,SAAS,MAAM;EAAE;EAAS;CAAU,CAAC;CAC1D,IAAI,QAAQ,WAAW,GACrB,OAAO,CAAC;CAIV,IAAI,QAAQ,WAAW,KAAK,QAAQ,EAAE,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC,QAAQ,QAAQ,QAC/E,OAAO;CAGT,OAAO,QAAQ,KAAK,YAAY;EAC9B,MAAM,OAAO;EACb,IAAI,OAAO;EACX,QAAQ,KAAK,MAAM,OAAO,OAAO,OAAO,GAAG;CAC7C,EAAE;AACJ;;;ACrDA,IAAa,eAAe,MAAY,SAAwB;CAC9D,MAAM,IAAI,OAAO,KAAK,OAAO,KAAK;CAClC,OAAO;EAAE,MAAM;EAAG,OAAO;EAAG,KAAK,KAAK;EAAK,QAAQ,KAAK;CAAO;AACjE;AAEA,IAAI;AAEJ,IAAa,aAAa,MAAY,MAAc,KAAK,SAAgB;CACvE,MAAM,QAAQ,iBAAiB,eAAe,SAAS,YAAY;CACnE,MAAM,OAAO,MAAM,EAAE;CACrB,MAAM,SAAS,MAAM,IAAI;CACzB,OAAO;AACT;AAEA,IAAa,kBAAkB,QAA2B;CACxD,IAAI,IAAI,aAAa,GACnB,OAAO,UAAU,KAAa,GAAG,IAAI,UAAW,MAAM,CAAC,CAAC,eAAe;MAClE,IAAI,IAAI,aAAa,GAC1B,OAAQ,IAAoB,eAAe;MAE3C,OAAO,CAAC;AAEZ;;;;AC5BA,IAAa,sBAAsB,UAA0B,MAAM,QAAQ,uBAAuB,MAAM;AAExG,IAAa,kBAAkB,MAAkB,MAAc,WAAmB;CAChF,KAAK,SAAS;EACZ,SAAS;GAAE;GAAM,IAAI;GAAM;EAAO;EAClC,WAAW;GAAE,QAAQ,OAAO,OAAO;GAAQ,MAAM,OAAO,OAAO;EAAO;CACxE,CAAC;AACH;;;;;AAMA,IAAa,qBAAqB,MAAkB,MAAc,WAAmB;CACnF,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,IAAI;CACvC,IAAI,KAAK,SAAS,MAChB,eAAe,MAAM,MAAM,MAAM;MAC5B;EACL,SAAS,OAAO;EAChB,KAAK,SAAS;GACZ,SAAS;IAAE,MAAM,KAAK;IAAI,IAAI,KAAK;IAAI;GAAO;GAC9C,WAAW;IAAE,QAAQ,KAAK,KAAK,OAAO;IAAQ,MAAM,KAAK,KAAK,OAAO;GAAO;EAC9E,CAAC;CACH;AACF;;;;;;ACZA,IAAa,YAAY,EAAE,SAAS,eAA2C;CAC7E,OAAO,CACL,WACE,aAAW,kBAAkB,IAAI,OAAO,aAAa;EACnD,QAAQ;GAAE,IAAI,MAAM,MAAM,UAAU;GAAG;EAAS,CAAC;EACjD,OAAO;CACT,CAAC,GAEH,YACE,aAAW,eAAe,IAAI,EAAE,OAAO,iBAAiB;EACtD,IAAI,YACF,SAAS;GAAE,IAAI,MAAM,MAAM,UAAU;GAAG,MAAM,MAAM,IAAI,SAAS;EAAE,CAAC;CAExE,CAAC,CACL,CAAC,CAAC,OAAO,aAAa;AACxB;;;;;;ACxBA,SAAgB,YAAY,kBAAkB,GAAc;CAC1D,OAAO,iBAAiB,eAAe;AACzC;AAEA,IAAM,oBAAoB,oBACxB,aAAW,MAAM,EACf,YAAY;CACV,YAAY,GAAG,gBAAgB;CAC/B,eAAe,GAAG,gBAAgB;AACpC,EACF,CAAC;;;;;;ACLH,IAAa,UAAU,EAAE,cAAc,OAAO,aAA4B,CAAC,MAAiB;CAC1F,OAAO,CACL,KAAK,QACH,SAAO,GAAG,CACR;EACE,KAAK;EACL,gBAAgB;EAChB,MAAM,SAAS;GACb,MAAM,OAAO,KAAK,MAAM,IAAI,SAAS,CAAC,CAAC,KAAK;GAC5C,IAAI,aAAa,eAAe,KAAK,SAAS,IAC9B;QAAA,SAAS,IACnB,GAEF,KAAK,SAAS,EACZ,SAAS;KACP,MAAM;KACN,IAAI,KAAK,MAAM,IAAI;KACnB,QAAQ;IACV,EACF,CAAC;GAAA;GAIL,OAAO;EACT;CACF,GACA;EACE,KAAK;EACL,gBAAgB;EAChB,MAAM,SAAS;GAEb,KAAK,SAAS,KAAK,MAAM,iBAAiB,IAAI,CAAC;GAC/C,OAAO;EACT;CACF,CACF,CAAC,CACH,CACF;AACF;;;;;AAUA,IAAa,oBAAoB,EAAE,eAAmD;CACpF,MAAM,OAAO,SAAqB;EAChC,SAAS,KAAK,MAAM,IAAI,SAAS,CAAC;EAClC,OAAO;CACT;CAIA,OAAO,KAAK,QACV,SAAO,GAAG,CACR;EAAE,KAAK;EAAc,gBAAgB;EAAM;CAAI,GAC/C;EAAE,KAAK;EAAc,gBAAgB;EAAM;CAAI,CACjD,CAAC,CACH;AACF;;;;;;;;AC/DA,IAAa,mBAAmB,cAA6C;CAC3E,WAAW,KAAK,UAAU;EACxB,IAAI;GACF,OAAO,SAAS,UAAU,KAAK,KAAK;EACtC,SAAS,KAAK;GACZ,IAAI,MAAM,KAAE,KAAA,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;GACb,OAAO;EACT;CACF;CAEA,aAAa,WAAW;EACtB,IAAI;GACF,OAAO,WAAW,UAAU,MAAM;EACpC,SAAS,KAAK;GACZ,IAAI,MAAM,KAAE,KAAA,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;GACb,OAAO;EACT;CACF;AACF;;;;ACtBA,IAAa,cAAc,YAAY,UAAU,GAAG,cAAc;AAQlE,IAAa,WAAW,OAAoB,UAAqC,MAAM,MAAM,KAAK,CAAC,CAAC;AACpG,IAAa,gBAAgB,OAAoB,UAAoC,MAAM,MAAM,KAAK,CAAC,CAAC;AAMxG,IAAa,oBAAoB,YAAY,OAAoB,CAAC,CAAC;AAEnE,IAAa,eAAe,aAA8C,kBAAkB,GAAG,EAAE,SAAS,CAAC;AAE3G,IAAa,sBAAsB,WAAW,OAAgB;AAE9D,IAAa,eAAe,OAA6B;CACvD,OAAO,CAAC,CAAC,GAAG,WAAW,mBAAmB;AAC5C;;;;;;;AChBA,IAAa,mBACX,OACA,QACA,cACA,UACsB;CACtB,MAAM,EAAE,WAAW,SAAS,MAAM,MAAM,KAAK;CAI7C,IAAI,aAAa;CACjB,KAAK,MAAM,MAAM,cACf,GAAG,QAAQ,kBAAkB;EAC3B,aAAa;CACf,CAAC;CAGH,IAAI,CAAC,YACH;CAiBF,OAdiB,OAAO,SAAS,YAAY,QAAwB;EACnE,MAAM,uBAAsE,CAAC;EAC7E,KAAK,MAAM,MAAM,cACf,GAAG,QAAQ,aAAa,OAAO,KAAK,QAAQ,MAAM,WAAW;GAC3D,qBAAqB,KAAK;IAAE,MAAM;IAAO,KAAK,MAAM;IAAO;GAAO,CAAC;EACrE,CAAC;EAIH,qBAAqB,QAAQ,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,aAAa;GAChE,KAAE,OAAO,KAAK,KAAK,MAAM,GAAG,MAAM,KAAK,OAAO,SAAS,CAAC;EAC1D,CAAC;CACH,CAEO,KAAY,KAAA;AACrB;;;;;;;AC9BA,IAAa,oBAAoB,MAAkB,WAA4B,QAAgB,YAAqB;CAClH,KAAK,MAAM,SAAS,SAAS;EAC3B,MAAM,aAAa,YAAY,OAAO,QAAQ,KAAK,KAAK;EACxD,IAAI,cAAc,MAAM;GACtB,MAAM,YAAY,UAAU,GAAG,YAAY,KAAK,MAAM,IAAI,QAAQ,IAAI;GACtE,YAAY,UAAU,IAAI,WAAW,CAAC;GACtC,KAAK,SAAS;IACZ,SAAS;IACT,aAAa,oBAAoB,GAAG,KAAK;GAC3C,CAAC;EACH;CACF;CAEA,KAAK,SAAS;EACZ;EACA,aAAa,oBAAoB,GAAG,KAAK;CAC3C,CAAC;AACH;AAEA,IAAM,eAAe,OAAc,QAAgB,UAA0C;CAC3F,IAAI,MAAM,WAAW,UACnB,OAAO,aAAa,QAAQ,KAAK;MAC5B,IAAI,MAAM,WAAW,UAC1B,OAAO,aAAa,QAAQ,KAAK;MAC5B,IAAI,MAAM,WAAW,OAC1B,OAAO,UAAU,QAAQ,KAAK;MACzB,IAAI,MAAM,WAAW,OAC1B,OAAO,UAAU,QAAQ,OAAO,KAAK;MAErC,OAAO;AAEX;AAEA,IAAM,gBAAgB,QAAgB,UAA0C;CAC9E,MAAM,QAAQ,SAAS,QAAQ,MAAM,IAAI;CACzC,IAAI,SAAS,MACX,OAAO,CAAC;CAIV,OAAO,CAAC;EAAE,MAAM;EAAO,IAAI;EAAO,QADrB,MAAM,OAAO,KAAK,UAAW,QAAQ,MAAM,SAAS,IAAI,EAAG,CAAC,CAAC,KAAK,EACrC;CAAK,CAAC;AAClD;AAEA,IAAM,gBAAgB,QAAgB,UAA8C;CAClF,MAAM,QAAQ,SAAS,QAAQ,MAAM,IAAI;CACzC,IAAI,SAAS,MACX,OAAO,CAAC;CAGV,OAAO,CAAC;EAAE,MAAM;EAAO,QAAQ,MAAM;CAAM,CAAC;AAC9C;AAEA,IAAM,aAAa,QAAgB,UAAuC;CACxE,MAAM,QAAQ,SAAS,QAAQ,MAAM,IAAI;CACzC,IAAI,SAAS,MACX,OAAO,CAAC;CAIV,OAAO,CAAC;EAAE,MAAM;EAAO,IAAI,SADZ,MAAM,UAAU;CACW,CAAC;AAC7C;AAEA,IAAM,aAAa,QAAgB,OAAiB,UAA0C;CAE5F,IADc,SAAS,QAAQ,CAAC,GAAG,MAAM,MAAM,CAAC,CAC5C,KAAS,MACX,OAAO,CAAC;CAGV,MAAM,SAAS,MAAM,IAAI;CACzB,IAAI,OAAO,MAAM,UAAU,UACzB,OAAO,CAAC;CAGV,OAAO,CAAC;EAAE,MAAM;EAAG,IAAI;EAAQ,QAAQ,MAAM;CAAM,CAAC;AACtD;AAIA,IAAM,YAAY,UAAkB,kBAAyC;CAC3E,IAAI,cAAc,WAAW,SAAS,SAAS,GAC7C,OAAO;CAGT,KAAK,IAAI,IAAI,GAAG,IAAI,SAAS,QAAQ,KACnC,IAAI,SAAS,OAAO,cAAc,IAChC,OAAO;CAIX,MAAM,QAAQ,cAAc,cAAc,SAAS;CACnD,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,OAAO;AACT;;;;;;;AClGA,IAAa,SAAb,MAAoB;CAIC;CACA;CAJnB,WAAmB;CAEnB,YACE,SACA,QACA;EAFiB,KAAA,UAAA;EACA,KAAA,SAAA;CAChB;CAEH,UAAU,MAAkB,QAAuB;EACjD,IAAI,KAAK,UACP;EAGF,KAAK,WAAW;EAChB,IAAI,QACF,KAAK,eAAe,IAAI;OAExB,KAAK,kBAAkB,IAAI;EAE7B,KAAK,WAAW;CAClB;CAEA,eAAuB,MAAwB;EAC7C,IAAI,kBAAe,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;EAAA,CAAC;EAGpB,MAAM,eAAe,KAAK,MAAM,MAAM,KAAK,MAAM,CAAC,CAAC,yBAAyB,QAAQ,OAAO,CAAC,YAAY,EAAE,CAAC;EAC3G,MAAM,WAAW,gBAAgB,KAAK,QAAQ,KAAK,SAAS,cAAc,KAAK,KAAK;EAEpF,IAAI,UACF,KAAK,SAAS;GACZ,SAAS,YAAY,QAAQ;GAC7B,aAAa,oBAAoB,GAAG,KAAK;EAC3C,CAAC;CAEL;CAEA,kBAA0B,MAAwB;EAChD,IAAI,qBAAkB,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;EAAA,CAAC;EAGvB,MAAM,WAAW,aAAa,KAAK,OAAO,KAAK,MAAM;EACrD,MAAM,WAAW,KAAE,SAAS,KAAK,QAAQ,IAAI,CAAE;EAC/C,MAAM,OAAO,KAAE,OAAO,UAAU,QAAQ,IAAI,CAAC,IAAI,KAAE,KAAK,KAAK,QAAQ,IAAI,GAAI,UAAU,QAAQ;EAE/F,MAAM,YAAY,KAAK,MAAM;EAE7B,iBAAiB,MAAM,WADV,QAAQ,KAAK,OAAO,KAAK,MACJ,GAAM,IAAI;EAK5C,KAAK,SAAS;GACZ,SAAS,YAAY,QAAQ;GAC7B,aAAa,oBAAoB,GAAG,KAAK;EAC3C,CAAC;CACH;AACF;;;;;;;ACxDA,IAAa,aAAa,aAAsC;CAC9D,MAAM,YAAY,WAAW,OAAc;EACzC,cAAc;GACZ,OAAO;IACL,MAAM,SAAS,KAAK,MAAM;IAC1B,WAAW,KAAE,SAAS,SAAS,OAAO,IAAI,CAAE;IAC5C,0BAA0B,CAAC;GAC7B;EACF;EAEA,SAAS,OAAO,OAAO;GACrB,MAAM,SAAgB;IACpB,MAAM,SAAS,KAAK,MAAM;IAC1B,WAAW,MAAM;IACjB,0BAA0B,MAAM,yBAAyB,MAAM;GACjE;GAEA,IAAI,oBAAoB;GACxB,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,iBAAiB,GAAG;IAChC,OAAO,YAAY,OAAO,MAAM;IAChC,oBAAoB;GACtB;GAGF,IAAI,mBACF,OAAO,2BAA2B,CAAC;QAEnC,IAAI,CAAC,YAAY,EAAE,GACjB,OAAO,yBAAyB,KAAK,EAAE;GAI3C,OAAO;EACT;CACF,CAAC;CAED,MAAM,SAAS,IAAI,OAAO,SAAS,QAAQ,SAAS;CAEpD,OAAO;EACL,OAAO,UAAU,GAAG,gBAAgB,QAAQ,CAAC;EAG7C;EAGA,WAAW,UACT,MAAM;GACyB;GAA7B,YAAY,OAAoC;IAAnB,KAAA,QAAA;IAC3B,SAAS,OAAO,YAAY,UAAU,KAAK,aAAa;IAMxD,qBAAqB;KACnB,MAAM,QAAQ,IAAI,SAAiB,QAAQ;KAE3C,IAAI,UADY,KAAK,MAAM,MAAM,IAAI,SACvB,GACZ,KAAK,MAAM,SAAS;MAClB,SAAS;OAAE,MAAM;OAAG,IAAI,KAAK,MAAM,MAAM,IAAI;OAAQ,QAAQ;MAAM;MACnE,aAAa,CAAC,aAAa,oBAAoB,GAAG,IAAI,CAAC;KACzD,CAAC;IAEL,CAAC;GACH;GAEA,UAAU;IACR,SAAS,OAAO,eAAe,UAAU,KAAK,aAAa;GAC7D;GAEA,sBAA+B;IAC7B,OAAO,UAAU,KAAK,OAAO,KAAK;GACpC;EACF,CACF;EAGA,aAAW,eAAe,IAAI,EAAE,MAAM,SAAS,mBAAmB;GAChE,IAAI,CAAC,QAAQ,OAGP;QAAA,CADkB,aAAa,MAAM,OAAO,GAAG,WAAW,YAAY,SAAS,MAAM,YAAY,KAChG,GACH,OAAO,UAAU,MAAM,IAAI;GAAA;EAGjC,CAAC;CACH;AACF;;;;;;;ACzDA,IAAa,aAAa,WAAW,kBAA6B;CAChE,OAAO;EACL,kBAAkB,GAAG,QAAQ;EAC7B,WAAW,UAAU,2BAA2B,EAC9C,cAAc,UAAU,MAAM,YAChC,CAAC;EACD;EACA;CACF;AACF;AAEA,IAAM,gBAAmC;CACvC,mBAAmB,IAAI,MAAM;CAE7B,YAAY,CAAC;CACb,aAAa,CAAC;CAEd,uBAAuB,CAAC;CACxB,cAAc,CAAC;AACjB;;;;AAKA,IAAa,oBAAoB,iBAAoC,aAAa;AAGlF,IAAM,mCAAmC,WAAW,OAAO;;;;AAK3D,IAAa,4BAAb,MAA8D;CAC5D,OAAwB,IAAI,QAAO,KAAA,GAAA;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;CAAA,CAAC;CACpC;CACA;CACA;CAEA;CACA;CAEA,cAAoC,SAAS,GAAG,CAAC,CAAC;CAElD,YAAY,MAAkB;EAC5B,KAAK,QAAQ;EACb,KAAK,mBAAmB,KAAK,MAAM,MAAM,OAAO,SAAS;EACzD,KAAK,YAAY,KAAK,MAAM,MAAM,iBAAiB;EACnD,KAAK,UAAU,KAAK;EACpB,KAAK,UAAU,kBAAkB,GAAG,KAAK,YAAY;GACnD,KAAK,SAAS,EAAE,aAAa,CAAC,iCAAiC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;EAC1E,CAAC;CACH;CAEA,UAAgB;EACd,WAAW,KAAK,KAAK;EACrB,KAAU,KAAK,QAAQ;EACvB,KAAK,UAAU,MAAM;CACvB;CAEA,OAAO,QAA0B;EAC/B,KAAK,sBAAsB,OAAO,IAAI;EACtC,KAAK,wBAAwB,OAAO,IAAI;CAC1C;CAEA,sBAA8B,MAAwB;EAEpD,MAAM,EAAE,SAAS,KAAA,GAAW,OAAO,KAAA,MADlB,KAAK,YAAY,KAAK,IAAI,cAAc,SAAS,IACN,KAAK,MAAM,UAAU,OAAO,CAAC;EACzF,IAAI,KAAK,gBAAgB,UAAU,KAAK,cAAc,MACpD;EAGF,KAAK,cAAc;EACnB,KAAK,YAAY;EAEjB,KAAK,UAAU,OACb,WAAW,KAAA,KAAa,SAAS,KAAA,IAC7B;GACE,QAAQ,KAAK,iBAAiB,SAAS,MAAM;GAC7C,MAAM,KAAK,iBAAiB,SAAS,MAAM,EAAE;EAC/C,IACA,KAAA,CACN;CACF;CAEA,wBAAgC,MAAwB;EACtD,MAAM,cAAmC,CAOzC;EAEA,MAAM,UAAU,KAAK,MAAM,MAAM,cAAc,KAAK;EACpD,IAAI,kBAAkB;EACtB,MAAM,kBAAkB,KAAK,UAAU,gBAAgB;EACvD,KAAK,MAAM,SAAS,iBAAiB;GACnC,MAAM,SAAS,MAAM,UAAU,SAAS,KAAK,iBAAiB,WAAW,MAAM,SAAS,MAAM,IAAI;GAClG,MAAM,OAAO,MAAM,UAAU,OAAO,KAAK,iBAAiB,WAAW,MAAM,SAAS,IAAI,IAAI;GAC5F,IAAI,UAAU,QAAQ,QAAQ,MAC5B;GAGF,MAAM,QAAQ,KAAK,IAAI,KAAK,IAAI,QAAQ,IAAI,GAAG,KAAK,MAAM,IAAI,MAAM;GACpE,MAAM,MAAM,KAAK,IAAI,KAAK,IAAI,QAAQ,IAAI,GAAG,KAAK,MAAM,IAAI,MAAM;GAElE,MAAM,YAAY,KAAK,MAAM,IAAI,OAAO,KAAK;GAC7C,MAAM,UAAU,KAAK,MAAM,IAAI,OAAO,GAAG;GAEzC,MAAM,YAAY,MAAM,KAAK;GAC7B,MAAM,aAAa,MAAM,KAAK;GAE9B,IAAI,UAAU,WAAW,QAAQ,QAE/B,YAAY,KAAK;IACf,MAAM;IACN,IAAI;IACJ,OAAO,WAAW,KAAK;KACrB,YAAY,EAAE,OAAO,qBAAqB,aAAa;KACvD,OAAO;IACT,CAAC;GACH,CAAC;QACI;IAEL,YAAY,KAAK;KACf,MAAM;KACN,IAAI,UAAU,OAAO,UAAU;KAC/B,OAAO,WAAW,KAAK;MACrB,YAAY,EAAE,OAAO,qBAAqB,aAAa;MACvD,OAAO;KACT,CAAC;IACH,CAAC;IAGD,YAAY,KAAK;KACf,MAAM,QAAQ;KACd,IAAI;KACJ,OAAO,WAAW,KAAK;MACrB,YAAY,EAAE,OAAO,qBAAqB,aAAa;MACvD,OAAO;KACT,CAAC;IACH,CAAC;IAED,KAAK,IAAI,IAAI,UAAU,SAAS,GAAG,IAAI,QAAQ,QAAQ,KAAK;KAC1D,MAAM,UAAU,KAAK,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC;KACvC,YAAY,KAAK;MACf,MAAM;MACN,IAAI;MACJ,OAAO,WAAW,KAAK,EACrB,YAAY;OAAE,OAAO,qBAAqB;OAAc,OAAO;MAA0B,EAC3F,CAAC;KACH,CAAC;IACH;GACF;GAEA,MAAM,OAAO,MAAM,KAAK,eAAe;GACvC,MAAM,YAAY,SAAS,QAAQ,QAAQ,QAAQ,WAAW,MAAM;GACpE,oBAAoB;GACpB,YAAY,KAAK;IACf,MAAM;IACN,IAAI;IACJ,OAAO,WAAW,OAAO;KACvB,MAAM,OAAO,SAAS,IAAI,KAAK;KAC/B,OAAO;KACP,QAAQ,IAAI,kBAAkB,MAAM,QAAQ,MAAM,WAAW,SAAS;IACxE,CAAC;GACH,CAAC;EACH;EAGA,IAAI,WAAW,CAAC,iBACd,aAAa,IAAI;EAGnB,KAAK,cAAc,WAAW,IAAI,aAAa,IAAI;CACrD;AACF;AAKA,IAAM,kBAAkB,YAAY,OAA4B;;AAGhE,IAAM,uBAAuB;AAG7B,IAAM,4BAAY,IAAI,QAA4B;AAClD,IAAM,6BAAa,IAAI,QAAmD;;AAG1E,IAAM,gBAAgB,SAAqB;CACzC,IAAI,WAAW,IAAI,IAAI,GACrB;CAEF,MAAM,YAAY,wBAAwB,KAAK,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK;CAC/E,WAAW,IACT,MACA,iBACQ;EACJ,WAAW,OAAO,IAAI;EACtB,IAAI,KAAK,IAAI,aACX,KAAK,SAAS,EAAE,SAAS,gBAAgB,GAAG,IAAI,EAAE,CAAC;CAEvD,GACA,KAAK,IAAI,GAAG,SAAS,CACvB,CACF;AACF;AAEA,IAAM,cAAc,SAAqB;CACvC,aAAa,WAAW,IAAI,IAAI,CAAC;CACjC,WAAW,OAAO,IAAI;AACxB;;;;;AAMA,IAAM,eAAe,WAAW,OAA4B;CAC1D,cAAc;CACd,SAAS,OAAO,OAAO;EACrB,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,eAAe,GAC3B,OAAO,OAAO;EAGlB,OAAO,SAAS,GAAG,aAAa;GAAE,GAAG;GAAO,KAAK,GAAG,QAAQ,OAAO,MAAM,GAAG;EAAE,IAAI;CACpF;CACA,UAAU,UACR,YAAY,KAAK,QAAQ,UACvB,QACI;EACE,KAAK,MAAM;EACX,OAAO;EACP,cAAc;GACZ,MAAM,MAAM,SAAS,cAAc,KAAK;GACxC,IAAI,YAAY;GAChB,IAAI,MAAM,kBAAkB,MAAM;GAClC,IAAI,cAAc,MAAM;GACxB,OAAO,EAAE,IAAI;EACf;CACF,IACA,IACN;AACJ,CAAC;AAED,IAAM,oBAAN,cAAgC,WAAW;CAEtB;CACA;CACA;CAEA;CALnB,YACE,SACA,OACA,QAEA,WAA4B,OAC5B;EACA,MAAM;EANW,KAAA,UAAA;EACA,KAAA,QAAA;EACA,KAAA,SAAA;EAEA,KAAA,WAAA;CAGnB;CAEA,MAAe,MAA+B;EAC5C,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,YAAY;EACjB,KAAK,QAAQ,OAAO,KAAK;EACzB,KAAK,QAAQ,OAAO,KAAK;EACzB,KAAK,QAAQ,QAAQ,KAAK;EAC1B,KAAK,gBAAgB,gBAAgB,KAAK,QAAQ;EAClD,KAAK,MAAM,kBAAkB,KAAK;EAClC,KAAK,MAAM,cAAc,KAAK;EAE9B,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAGhB,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,YAAY;EACjB,KAAK,cAAc,KAAK;EAExB,KAAK,YAAY,SAAS,eAAe,GAAQ,CAAC;EAClD,KAAK,YAAY,GAAG;EACpB,KAAK,YAAY,SAAS,eAAe,GAAQ,CAAC;EAClD,KAAK,YAAY,IAAI;EACrB,KAAK,iBAAiB,oBAAoB;GACxC,WAAW,IAAI;GACf,UAAU,IAAI,MAAM,KAAK,IAAI,CAAC;GAC9B,MAAM,MAAM,KAAK,SAAS,IAAI;GAC9B,KAAK,SAAS,EACZ,SAAS,gBAAgB,GAAG;IAAE;IAAK,QAAQ,KAAK;IAAS,MAAM,KAAK;IAAO,OAAO,KAAK;GAAO,CAAC,EACjG,CAAC;EACH,CAAC;EAED,KAAK,iBAAiB,oBAAoB,aAAa,IAAI,CAAC;EAC5D,OAAO;CACT;CAEA,UAAmB,KAA2B;EAE5C,IAAI,IAAI,QAAQ,SAAS,KAAK,WAAW,IAAI,QAAQ,SAAS,KAAK,SAAS,IAAI,QAAQ,UAAU,KAAK,QACrG,OAAO;EAET,IAAI,gBAAgB,gBAAgB,KAAK,QAAQ;EACjD,OAAO;CACT;CAEA,GAAY,QAAuB;EACjC,OACE,OAAO,YAAY,KAAK,WACxB,OAAO,WAAW,KAAK,UACvB,OAAO,UAAU,KAAK,SACtB,OAAO,aAAa,KAAK;CAE7B;CAEA,IAAa,kBAAkB;EAC7B,OAAO;CACT;CAEA,cAAgC;EAC9B,OAAO;CACT;AACF;AAEA,IAAM,YAAS,aAAW,MAAM;CAC9B,wBAAwB,CAAC;CACzB,4BAA4B;EAC1B,SAAS;EACT,QAAQ;CACV;CACA,6BAA6B;EAC3B,UAAU;EACV,YAAY;EACZ,aAAa;EACb,YAAY;EACZ,aAAa;EACb,WAAW;EACX,SAAS;EACT,QAAQ;CACV;CACA,gCAAgC;EAC9B,cAAc;EACd,UAAU;EACV,OAAO;EACP,QAAQ;EACR,KAAK;EACL,MAAM;EACN,iBAAiB;EACjB,YAAY;EACZ,WAAW;CACb;CACA,0EAA0E;EACxE,WAAW;EACX,iBAAiB;CACnB;CAEA,4BAA4B;EAC1B,UAAU;EACV,OAAO;EACP,QAAQ;EACR,UAAU;EACV,UAAU;EACV,YAAY;CACd;CAEA,uCAAuC;EACrC,UAAU;EACV,YAAY;EACZ,YAAY;EACZ,YAAY;EACZ,OAAO;EACP,SAAS;EACT,QAAQ;EACR,cAAc;EACd,YAAY;EACZ,eAAe;CACjB;CAEA,6CAA6C,EAC3C,wBAAwB,EAC1B;CACA,6CAA6C,EAC3C,qBAAqB,EACvB;AACF,CAAC;;;;;;;;;;AC7YD,IAAM,UAAU,YACd,QAAQ,SAAS,UACb,EAAE,MAAM,QAAQ,IAChB;CACE,MAAM;CACN,OAAO;EACL,QAAQ,QAAQ,MAAM;EACtB,MAAM,EAAE,GAAG,QAAQ,MAAM,KAAK;EAC9B,GAAI,QAAQ,MAAM,WAAW,EAAE,UAAU,EAAE,GAAG,QAAQ,MAAM,SAAS,EAAE,IAAI,CAAC;CAC9E;AACF;AAEN,IAAM,YAAY,YAAiE;CACjF,IAAI,SAAS,SAAS,SACpB,OAAO,EAAE,MAAM,QAAQ;CAEzB,IAAI,SAAS,SAAS,QACpB;CAGF,MAAM,QAAQ,WAAW,QAAQ,KAAK;CACtC,MAAM,OAAO,WAAW,OAAO,IAAI;CACnC,MAAM,WAAW,WAAW,OAAO,QAAQ;CAC3C,IAAI,OAAO,OAAO,WAAW,YAAY,CAAC,MACxC;CAGF,OAAO;EACL,MAAM;EACN,OAAO;GACL,QAAQ,MAAM;GACd,MAAM;IACJ,aAAa,OAAO,KAAK,eAAe,EAAE;IAC1C,WAAW,OAAO,KAAK,aAAa,EAAE;IACtC,YAAY,OAAO,KAAK,cAAc,EAAE;GAC1C;GACA,UAAU,YAAY;IACpB,QAAQ,OAAO,SAAS,WAAW,WAAW,SAAS,SAAS,KAAA;IAChE,MAAM,OAAO,SAAS,SAAS,WAAW,SAAS,OAAO,KAAA;GAC5D;EACF;CACF;AACF;AAEA,IAAM,cAAc,UAClB,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAA;AAEjF,IAAM,oBAAoB;;;;AAY1B,IAAa,yBAAb,MAAiE;CAC/D,gCAAiC,IAAI,IAA4B;CAEjE;CACA;CACA;CACA;CAEA;CACA;CACA;CAEA,oBAAoC,IAAI,MAAY;CAEpD,YAAY,EAAE,WAAW,SAAS,QAAQ,QAAgC;EACxE,KAAK,aAAa;EAClB,KAAK,WAAW;EAChB,KAAK,UAAU;EACf,KAAK,QAAQ;CACf;CAEA,OAAa;EACX,KAAK,OAAO,IAAI,QAAO,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;EAAA,CAAC;EACxB,KAAK,YAAY,IAAI,aAAa,KAAK,MAAM,YAAY;GACvD,IAAI,KAAK,aAAa;IACpB,MAAM,KAAK,WAAW,YAAY,KAAK,UAAU,OAAO;KAAE,MAAM;KAAQ,OAAO,KAAK;IAAY,CAAC,CAAC;IAIlG,MAAM,MAAM,iBAAiB;GAC/B;EACF,CAAC;EAED,KAAK,KAAK,UACR,KAAK,WAAW,OAAO,KAAK,WAAW,YAA2B;GAChE,MAAM,UAAU,SAAS,WAAW,QAAQ,OAAO,CAAC;GACpD,QAAQ,SAAS,MAAjB;IACE,KAAK;KACH,KAAK,oBAAoB;KACzB;IAEF,KAAK,QACH,KAAK,mBAAmB,OAAO;GAGnC;EACF,CAAC,CACH;EAEA,KAAU,WAAW,YAAY,KAAK,UAAU,OAAO,EAAE,MAAM,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,QAAQ;GACxF,IAAI,MAAM,6BAA6B,EAAE,IAAI,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA;GAAA,CAAC;EAChD,CAAC;CACH;CAEA,QAAc;EACZ,KAAU,MAAM,QAAQ;EACxB,KAAK,OAAO,KAAA;EACZ,KAAK,YAAY,KAAA;CACnB;CAEA,kBAAoC;EAClC,OAAO,MAAM,KAAK,KAAK,cAAc,OAAO,CAAC;CAC/C;CAEA,OAAO,UAA+C;EACpD,KAAK,cAAc;GACjB,QAAQ,KAAK;GACb;GACA,MAAM,KAAK;EACb;EAGA,KAAK,WAAW,SAAS;CAC3B;CAEA,sBAAoC;EAClC,KAAK,WAAW,SAAS;CAC3B;CAEA,mBAA2B,SAAgC;EACzD,UAAU,QAAQ,SAAS,QAAK,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,CAAA,2BAAA,EAAA;EAAA,CAAC;EAEjC,KAAK,cAAc,IAAI,QAAQ,MAAM,QAAQ,QAAQ,KAAK;EAC1D,KAAK,kBAAkB,KAAK;CAC9B;AACF;;;;;;;;;ACtJA,IAAa,gBAAgB,EAAE,cAAmC,CAAC,MAAiB;CAClF,MAAM,UAAU,WAAW,UACzB,MAAM;EACJ;EACA,qBAAoC;EAEpC,YAAY,MAAkB;GAC5B,KAAK,eAAe,KAAK,mBAAmB,IAAI;EAClD;EAEA,OAAO,QAAoB;GACzB,IAAI,OAAO,cAAc,OAAO,cAC9B,KAAK,eAAe,KAAK,mBAAmB,OAAO,IAAI;EAE3D;EAEA,mBAA2B,MAAiC;GAC1D,MAAM,OAAO,KAAK,MAAM,IAAI,SAAS;GACrC,MAAM,cAAc,YAAY,IAAI,KAAK,CAAC;GAC1C,IAAI,CAAC,YAAY,QAAQ;IACvB,KAAK,qBAAqB;IAC1B,OAAO,WAAW;GACpB;GAGA,KAAK,qBAAqB,YAAY;GACtC,MAAM,SAAS,KAAK,mBAAmB,MAAM,KAAK,MAAM;GACxD,IAAI,CAAC,QACH,OAAO,WAAW;GAIpB,OAAO,WAAW,IAAI,CACpB,WAAW,OAAO;IAChB,QAAQ,IAAI,uBAAuB,MAAM;IACzC,MAAM;GACR,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,CAChC,CAAC;EACH;EAEA,mBAAmB,MAA2B;GAC5C,IAAI,CAAC,KAAK,oBACR,OAAO;GAGT,MAAM,OAAO,KAAK,MAAM,IAAI,SAAS;GACrC,MAAM,SAAS,KAAK,mBAAmB,MAAM,KAAK,MAAM;GACxD,IAAI,CAAC,QACH,OAAO;GAGT,KAAK,SAAS;IACZ,SAAS;KACP,MAAM,KAAK,MAAM,IAAI;KACrB,QAAQ;IACV;IACA,WAAW,EACT,QAAQ,KAAK,MAAM,IAAI,SAAS,OAAO,OACzC;GACF,CAAC;GAED,OAAO;EACT;CACF,GACA,EACE,cAAc,MAAM,EAAE,aACxB,CACF;CAEA,OAAO;EACL;EACA;EAEA,KAAK,QACH,SAAO,GAAG,CACR;GACE,KAAK;GACL,gBAAgB;GAChB,MAAM,SAAS;IAEb,OADe,KAAK,OAAO,OACpB,CAAA,EAAQ,mBAAmB,IAAI,KAAK;GAC7C;EACF,GACA;GACE,KAAK;GACL,gBAAgB;GAChB,MAAM,SAAS;IAEb,IAAI,KAAK,MAAM,UAAU,KAAK,SAAS,KAAK,MAAM,IAAI,QACpD,OAAO;IAIT,OADe,KAAK,OAAO,OACpB,CAAA,EAAQ,mBAAmB,IAAI,KAAK;GAC7C;EACF,CACF,CAAC,CACH;CACF;AACF;AAEA,IAAM,yBAAN,cAAqC,WAAW;CAC1B;CAApB,YAAY,QAAwB;EAClC,MAAM;EADY,KAAA,SAAA;CAEpB;CAEA,GAAY,OAAa;EACvB,OAAO,KAAK,WAAW,MAAM;CAC/B;CAEA,QAAiB;EACf,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,cAAc,KAAK;EACxB,KAAK,YAAY;EACjB,OAAO;CACT;AACF;AAEA,IAAM,WAAS,aAAW,MAAM,EAC9B,yBAAyB,EACvB,SAAS,GACX,EACF,CAAC;;;;;;AC1ID,IAAa,oBACV,aAAuB,UAA6B,CAAC,OACrD,EAAE,WAA8B;CAC/B,IAAI,KAAK,WAAW,KAAK,QAAQ,SAC/B,OAAO,QAAQ;CAGjB,MAAM,QAAQ,KAAK,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO;CAC9C,IAAI,MAAM,QAAQ;EAChB,MAAM,MAAM,MAAM,GAAG,EAAE;EACvB,IAAI,IAAI,WAAW,QAAQ,aAAa,IACtC,KAAK,MAAM,cAAc,aAAa;GACpC,MAAM,QAAQ,gBAAgB,YAAY,GAAG;GAC7C,IAAI,OACF,OAAO;EAEX;CAEJ;AACF;AAEF,IAAa,mBAAmB,YAAoB,KAAa,YAAY,MAA0B;CACrG,IACE,IAAI,UAAU,aACd,WAAW,SAAS,IAAI,UACxB,WAAW,WAAW,GAAG,GAIzB,OAAO,WAAW,MAAM,IAAI,MAAM;AAEtC;;;;;;;AC3BA,IAAa,WAAW,EAAE,OAAO,eAA0C;CACzE,OAAO,eAAe;EAEpB,kBAAkB;EAGlB,aAAa,CAAC;EAEd,OAAO;EACP,UAAU,EACP,YAAwD;GACvD,IAAI,KAAK,sBAAsB,EAAE,QAAQ,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;GAE1C,MAAM,QAAQ,QAAQ,YAAY,SAAS;GAC3C,IAAI,CAAC,SAAU,MAAM,SAAS,MAAM,MAAM,CAAC,QAAQ,UACjD,OAAO;GAGT,OAAO;IACL,MAAM,MAAM;IACZ,SAAS,SAAS,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,KAAK,WAAW,EAAE,OAAO,IAAI,QAAQ,EAAE;GAC9F;EACF,CACF;CACF,CAAC;AACH;;;;;;;;AChCA,IAAa,UAAU,YAAY,OAAgB;;AAGnD,IAAa,YAAY,WAAW,OAAgB;CAClD,cAAc;CACd,SAAS,OAAO,OAAO;EACrB,IAAI,OAAO;EACX,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,OAAO,GACnB,OAAO,OAAO;EAIlB,OAAO;CACT;AACF,CAAC;;AAGD,IAAa,UAAU,UAAgC,MAAM,MAAM,WAAW,KAAK,KAAK;;AAGxF,IAAa,aAAwB;;;;;;;;ACRrC,IAAa,eAAe,EAAE,SAAS,QAAQ,KAAO,YAAY,YAA2C;CAC3G,MAAM,SAAS,WAAW,UACxB,MAAM;EACJ;EACA,eAAe,WAAW;EAE1B,OAAO,QAAoB;GAGzB,MAAM,cAAc,OAAO,OAAO,UAAU,MAAM,OAAO,OAAO,KAAK,KAAK;GAS1E,IAAI,CAAC,OAAO,cAAc,CAAC,OAAO,gBAAgB,CAAC,OAAO,gBAAgB,CAAC,aACzE;GAGF,IAAI,KAAK,UAAU;IACjB,OAAO,aAAa,KAAK,QAAQ;IACjC,KAAK,WAAW,KAAA;GAClB;GACA,KAAK,eAAe,WAAW;GAK/B,IAAI,aAAa,CAAC,OAAO,KAAK,UAC5B;GAIF,IAAI,OAAO,OAAO,KAAK,KAAK,GAC1B;GAGF,MAAM,aAAa,OAAO,KAAK,MAAM,IAAI,OAAO,OAAO,KAAK,MAAM,UAAU,KAAK,IAAI;GACrF,IAAI,WAAW,KAAK,KAAK,MAAM,IAC7B;GAGF,MAAM,YAAY,WAAW;GAC7B,MAAM,OAAO,OAAO;GACpB,KAAK,WAAW,iBAAiB;IAC/B,KAAK,eAAe,WAAW,IAAI,CACjC,WAAW,OAAO;KAChB,QAAQ,IAAI,kBAAkB,OAAO;KACrC,MAAM;IACR,CAAC,CAAC,CAAC,MAAM,SAAS,CACpB,CAAC;IACD,KAAK,OAAO,CAAC,CAAC;GAChB,GAAG,KAAK;EACV;EAEA,UAAU;GACR,IAAI,KAAK,UACP,aAAa,KAAK,QAAQ;EAE9B;CACF,GACA,EACE,UAAU,WAAW;EACnB,OAAO,CAAC,aAAW,YAAY,IAAI,SAAS,KAAK,OAAO,MAAM,CAAC,EAAE,gBAAgB,WAAW,IAAI,CAAC;CACnG,EACF,CACF;CAEA,OAAO,OAAO,YAAY,WACtB,CAAC,QAAQ,aAAW,kBAAkB,GAAG,EAAE,oBAAoB,QAAQ,CAAC,CAAC,IACzE;AACN;AAEA,IAAa,oBAAb,cAAuC,WAAW;CAC3B;CAArB,YAAY,SAA2B;EACrC,MAAM;EADa,KAAA,UAAA;CAErB;CAEA,MAAM,MAAkB;EACtB,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,YAAY;EACjB,KAAK,MAAM,gBAAgB;EAG3B,KAAK,MAAM,aAAa;EACxB,KAAK,aAAa,eAAe,MAAM;EACvC,KAAK,YACH,OAAO,KAAK,YAAY,WACpB,SAAS,eAAe,KAAK,OAAO,IACpC,OAAO,KAAK,YAAY,aACtB,KAAK,QAAQ,IAAI,IACjB,KAAK,QAAQ,UAAU,IAAI,CACnC;EAEA,OAAO;CACT;CAEA,SAAkB,KAAkB;EAClC,MAAM,QAAQ,IAAI,aAAa,eAAe,IAAI,UAAU,IAAI,CAAC;EACjE,IAAI,CAAC,MAAM,QACT,OAAO;EAGT,MAAM,QAAQ,iBAAiB,IAAI,UAAyB;EAC5D,MAAM,OAAO,YAAY,MAAM,IAAI,MAAM,cAAc,KAAK;EAC5D,MAAM,aAAa,SAAS,MAAM,UAAU;EAC5C,IAAI,KAAK,SAAS,KAAK,MAAM,aAAa,KACxC,OAAO;GACL,MAAM,KAAK;GACX,OAAO,KAAK;GACZ,KAAK,KAAK;GACV,QAAQ,KAAK,MAAM;EACrB;EAGF,OAAO;CACT;CAEA,cAAuB;EACrB,OAAO;CACT;AACF;;;;;;AC1HA,IAAa,aAAa,EAAE,eAAiC,CAAC,MAAiB;CAG7E,MAAM,kBAAkB,WAAW,UACjC,MAAM;EACJ,cAA6B,WAAW;EACxC;EACA,OAAO,QAAoB;GAEzB,IAAI,CAAC,OAAO,cAAc,CAAC,OAAO,cAChC;GAGF,MAAM,UAAU,IAAI,gBAA4B;GAChD,MAAM,YAAY,OAAO,KAAK,MAAM,UAAU;GAC9C,MAAM,OAAO,OAAO,KAAK,MAAM,IAAI,OAAO,UAAU,IAAI;GAIxD,KAAK,OAAO,KAAA;GACZ,IAAI,UAAU,SAAS,UAAU,MAAM,UAAU,SAAS,KAAK,IAAI;IACjE,MAAM,MAAM,OAAO,MAAM,SAAS,KAAK,MAAM,UAAU,IAAI;IAC3D,KAAK,OAAO,aAAa,EAAE,MAAM,IAAI,CAAC;IACtC,IAAI,KAAK,MACP,QAAQ,IAAI,UAAU,MAAM,UAAU,IAAI,WAAW,OAAO,EAAE,QAAQ,IAAI,kBAAkB,KAAK,IAAI,EAAE,CAAC,CAAC;GAE7G;GAEA,KAAK,cAAc,QAAQ,OAAO;EACpC;CACF,GACA,EACE,cAAc,UAAU,MAAM,YAChC,CACF;CAEA,MAAM,YAAqB,SAAqB;EAC9C,MAAM,OAAO,KAAK,OAAO,eAAe,CAAC,EAAE;EAC3C,IAAI,CAAC,MACH,OAAO;EAGT,MAAM,YAAY,KAAK,MAAM,UAAU;EACvC,KAAK,SAAS;GACZ,SAAS,CAAC;IAAE,MAAM,UAAU;IAAM,IAAI,UAAU;IAAI,QAAQ;GAAK,CAAC;GAClE,WAAW,kBAAgB,OAAO,UAAU,OAAO,KAAK,MAAM;EAChE,CAAC;EAED,OAAO;CACT;CAEA,OAAO,CACL,iBAGA,KAAK,QACH,SAAO,GAAG,CACR;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP,GACA;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP,CACF,CAAC,CACH,CACF;AACF;;;ACpFA,IAAM,WAA8F;CAClG,GAAG;EACD,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,GAAG;EACD,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,GAAG;EACD,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,GAAG;EACD,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,GAAG;EACD,WAAW;EACX,UAAU;EACV,YAAY;CACd;CACA,GAAG;EACD,WAAW;EACX,UAAU;EACV,YAAY;CACd;AACF;AAGA,IAAa,WAAW;AACxB,IAAa,WAAW;AAExB,IAAa,gBAAgB;CAC3B,MAAM;CACN,UAAU;CACV,MAAM;CACN,UAAU,WAAyB;EACjC,WAAW,GAAG,SAAS,MAAM,CAAC,WAAW,6CAA6C;EACtF,OAAO;EACP,YAAY,SAAS,MAAM,CAAC;EAC5B,UAAU,SAAS,MAAM,CAAC;EAC1B,YAAY;CACd;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCA,IAAa,YAAY,aAAW,UAAU;;;;CAI5C,KAAK,CAAC;CACN,gBAAgB,EACd,SAAS,OACX;;;;CAKA,gBAAgB,EAId,gBAAgB,OAClB;CAGA,mCAAmC;EACjC,OAAO;EACP,QAAQ;CACV;CACA,0CAA0C,EACxC,YAAY,cACd;CACA,yCAAyC,EACvC,YAAY,cACd;CAGA,yCAAyC;EACvC,iBAAiB;EACjB,cAAc;EACd,QAAQ;EACR,gBAAgB;EAChB,YAAY;CACd;CAGA,iDAAiD,EAC/C,iBAAiB,+BACnB;;;;;CAMA,eAAe;EACb,SAAS;EACT,OAAO;CACT;;;;;CAMA,eAAe;EACb,YAAY;EACZ,aAAa;CACf;CACA,cAAc,CAAC;CACf,6BAA6B;EAC3B,cAAc;EACd,OAAO;CACT;CACA,+CAA+C,EAC7C,UAAU,OACZ;;;;CAIA,qBAAqB;EACnB,YAAY;EACZ,UAAU;CACZ;;;;CAKA,YAAY;EACV,YAAY;EACZ,eAAe;CACjB;;;;;CAKA,cAAc,EACZ,YAAY,UACd;CACA,kBAAkB,EAChB,YAAY,8BACd;;;;CAKA,8BAA8B,EAC5B,YAAY,mCACd;CACA,mBAAmB,EACjB,OAAO,2BACT;;;;;;CAOA,2BAA2B,EACzB,YAAY,4BACd;CACA,4EAA4E,EAC1E,YAAY,oCACd;;;;;CAMA,mBAAmB;EACjB,QAAQ;EACR,SAAS;EACT,cAAc;EACd,YAAY;EACZ,OAAO;CACT;CACA,4BAA4B,EAC1B,gBAAgB,YAClB;;;;CAKA,YAAY;EACV,oBAAoB;EACpB,yBAAyB;EACzB,qBAAqB;EACrB,qBAAqB;EACrB,cAAc;EACd,QAAQ;CACV;CACA,mBAAmB,EACjB,OAAO,2BACT;CACA,yBAAyB,EACvB,OAAO,iCACT;;;;CAKA,eAAe,EACb,YAAY,6BACd;CACA,qBAAqB,CAAC;CACtB,qBAAqB;EACnB,YAAY;EACZ,QAAQ;EACR,cAAc;EACd,UAAU;CACZ;;;;;CAMA,uCAAuC;EACrC,WAAW;EACX,YAAY;EACZ,QAAQ;EACR,cAAc;CAChB;CACA,4CAA4C,EAC1C,WAAW,OACb;CACA,iDAAiD,EAC/C,SAAS,MACX;CACA,gEAAgE;EAC9D,YAAY;EACZ,OAAO;CACT;CACA,iEAAiE;EAC/D,aAAa;EACb,OAAO;CACT;;;;CAKA,sBAAsB;EACpB,OAAO;EACP,QAAQ;EACR,SAAS;EACT,aAAa;CACf;CACA,sBAAsB,EACpB,SAAS,OACX;CACA,uBAAuB;EACrB,OAAO;EACP,SAAS;CACX;CACA,6BAA6B;EAC3B,OAAO;EACP,gBAAgB;CAClB;;;;;;;;;;;;;;;;CAkBA,cAAc,CAAC;CACf,aAAa,EACX,iBAAiB,oBACnB;CACA,sDAAsD;EACpD,OAAO;EACP,UAAU;EACV,KAAK;EACL,QAAQ;EACR,SAAS;EACT,SAAS;CACX;CACA,qCAAqC,EACnC,iBAAiB,6BACnB;CACA,iDAAiD,EAC/C,SAAS,2CACX;CACA,mBAAmB;EACjB,SAAS;EACT,YAAY;EACZ,QAAQ;CACV;CACA,gCAAgC,CAAC;CACjC,kCAAkC;EAChC,OAAO;EACP,QAAQ;EACR,aAAa;EACb,SAAS;EACT,OAAO;CACT;CACA,oBAAoB;EAClB,WAAW,EAET,iBAAiB,0CACnB;EACA,YAAY,EACV,iBAAiB,+BACnB;CACF;CACA,uBAAuB;EACrB,SAAS;EACT,WAAW;CACb;AACF,CAAC;AAED,IAAa,eAA0B,aAAW,MAAM,EACtD,eAAe;CAEb,YAAY;CACZ,cAAc;AAChB,EACF,CAAC;AAMD,IAAa,mBAAmB,EAAE,cAA2B,CAAC,MAC5D,aAAW,MAAM;CAEf,gBAAgB,EACd,YAAY,YAAY,WAAW,SACrC;CAGA,uCAAuC;EACrC,YAAY;EACZ,UAAU;CACZ;AACF,CAAC;;;ACnYH,IAAM,cAAc,YAAY,OAAgB;;;;;AAMhD,IAAa,aAAa,WAAW,OAAgB;CACnD,cAAc;CACd,SAAS,OAAO,OAAO;EACrB,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,WAAW,GACvB,OAAO,OAAO;EAIlB,OAAO;CACT;AACF,CAAC;;;;AAKD,IAAa,QAAQ,CACnB,YACA,aAAW,iBAAiB;CAC1B,QAAQ,QAAQ,SAAS;EACvB,4BAA4B,KAAK,SAAS,EAAE,SAAS,YAAY,GAAG,IAAI,EAAE,CAAC,CAAC;CAC9E;CACA,OAAO,QAAQ,SAAS;EACtB,4BAA4B,KAAK,SAAS,EAAE,SAAS,YAAY,GAAG,KAAK,EAAE,CAAC,CAAC;CAC/E;AACF,CAAC,CACH;;;;;ACGA,IAAa,oBAAoB,YAAY,OAAsB;;;;;;;AAQnE,IAAa,sBAAsB,YAAY,OAA+C;;;;;AAM9F,IAAa,gBAAgB,MAAkB,YAA2B;CACxE,KAAK,SAAS,EACZ,SAAS,kBAAkB,GAAG,OAAO,EACvC,CAAC;AACH;;;;;;;;;;;AAiBA,IAAa,WAAW,EAAE,aAAa,MAAsB,CAAC,MAAM;CAElE,MAAM,gBAAgB,WAAW,UAC/B,MAAM,cAAc;EAEW;EAD7B;EACA,YAAY,MAAmC;GAAlB,KAAA,OAAA;GAC3B,KAAK,UAAU,cAAc,KAAK,IAAI;EACxC;EAGA,UAAU;GACR,KAAK,QAAQ,OAAO;EACtB;EAEA,SAAS;GACP,KAAK,QAAQ,OAAO;EACtB;EAEA,MAAM,EAAE,QAAQ,YAAoD,EAAE,QAAQ,MAAM,GAAG;GACrF,IAAI,QACF,KAAK,QAAQ,OAAO,OAAO;QAE3B,KAAK,QAAQ,OAAO;EAExB;EAEA,OAAO,EAAE,MAAM,SAAS,GAAG,UAAU,WAAW,aAA4B;GAC1E,MAAM,EAAE,WAAW,cAAc,iBAAiB,KAAK,KAAK;GAC5D,MAAM,eAAe,KAAK,KAAK,UAAU,sBAAsB;GAC/D,MAAM,MAAM,KAAK,KAAK,MAAM;GAE5B,IAAI,kBAAkB,eAAe,eAAe;GACpD,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,GAAG;IACtC,MAAM,YAAY,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC;IACrC,MAAM,SAAS,KAAK,KAAK,YAAY,SAAS;IAC9C,IAAI,QAAQ;KAEV,MAAM,mBAAmB;KACzB,MAAM,eAAe,eAAe;KAEpC,IAAI,aAAa,OAGf,kBAAkB,mBAAmB,OAAO,SAAS,aAAa,SAAS;UAG3E,kBAAkB,mBAAmB,OAAO,MAAM,aAAa,MAAM;KAIvE,kBAAkB,KAAK,IAAI,GAAG,KAAK,IAAI,iBAAiB,YAAY,CAAC;IACvE;GACF;GAGA,4BAA4B;IAC1B,KAAK,KAAK,UAAU,SAAS,EAAE,KAAK,gBAAgB,CAAC;GACvD,CAAC;EACH;CACF,CACF;CAEA,OAAO;EACL;EAGA,aAAW,eAAe,IAAI,WAAW;GACvC,OAAO,aAAa,SAAS,gBAAgB;IAC3C,IAAI;KACF,MAAM,SAAS,OAAO,KAAK,OAAO,aAAa;KAC/C,IAAI,QACG;WAAA,MAAM,UAAU,YAAY,SAC/B,IAAI,OAAO,GAAG,mBAAmB,GAC/B,OAAO,MAAM,OAAO,KAAK;WACpB,IAAI,OAAO,GAAG,iBAAiB,GACpC,OAAO,OAAO,OAAO,KAAK;KAAA;IAIlC,SAAS,KAAK;KACZ,IAAI,MAAM,KAAE,KAAA,GAAA;MAAA,YAAA;MAAA,GAAA;MAAA,GAAA;MAAA,GAAA,KAAA;KAAA,CAAC;IACf;GACF,CAAC;EACH,CAAC;EAGD,aAAW,MAAM;GACf,gBAAgB;IACd,WAAW;IAKX,gBAAgB;GAClB;GACA,qDAAqD,EACnD,SAAS,OACX;GACA,yCAAyC;IACvC,YAAY;IACZ,YAAY;GACd;GACA,iDAAiD,EAC/C,YAAY,+BACd;GAMA,sBAAsB;IACpB,SAAS;IACT,SAAS;IACT,QAAQ,GAAG,WAAW;GACxB;GACA,qBAAqB;IACnB,UAAU;IACV,QAAQ;IACR,OAAO;GACT;EACF,CAAC;CACH;AACF;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,cAAc,MAAkB,QAAQ,GAAG,gBAAgB,GAAG,eAAe,IAAI;CAC/F,MAAM,KAAK,KAAK;CAEhB,IAAI,aAAa;CACjB,IAAI,WAAW;CACf,IAAI,QAAuB;CAC3B,IAAI,WAAW;CACf,IAAI,UAAU;CAEd,SAAS,MAAM,KAAa;EAE1B,MAAM,KAAK,aAAa,IAAI,IAAI,KAAK,KAAK,IAAI,KAAM,MAAM,YAAY,GAAI;EAC1E,WAAW;EAEX,MAAM,YAAY,GAAG,eAAe,GAAG;EACvC,MAAM,QAAQ,YAAY;EAI1B,IAAI,SAAS;GACX,GAAG,YAAY;GACf,aAAa;GACb,WAAW;GACX,IAAI,KAAK,IAAI,KAAK,IAAI,eAAe;IACnC,QAAQ;IACR,WAAW;IACX;GACF;GACA,QAAQ,sBAAsB,KAAK;GACnC;EACF;EAEA,IAAI,KAAK,IAAI,KAAK,IAAI,iBAAiB,KAAK,IAAI,QAAQ,IAAI,cAAc;GACxE,GAAG,YAAY;GACf,aAAa;GACb,WAAW;GACX,QAAQ;GACR,WAAW;GACX;EACF;EAGA,MAAM,QAAQ,QAAQ,QAAQ,QAAQ,IAAI,QAAQ;EAClD,YAAY,QAAQ;EACpB,cAAc,WAAW;EACzB,GAAG,YAAY;EACf,QAAQ,sBAAsB,KAAK;CACrC;CAEA,OAAO;EACL,SAAS,aAAa,UAAU;GAC9B,UAAU;GACV,IAAI,UAAU,MAAM;IAClB,aAAa,GAAG;IAChB,WAAW;IACX,QAAQ,sBAAsB,KAAK;GACrC;EACF;EACA,cAAc;GACZ,IAAI,UAAU,MAAM;IAClB,qBAAqB,KAAK;IAC1B,WAAW;IACX,WAAW;IACX,QAAQ;GACV;EACF;CACF;AACF;;;;AC/QA,IAAa,mBAAmB,YAAY,OAAgB;;;;AAiB5D,IAAa,cAAc,EAAE,iBAAiB,SAA0B,CAAC,MAAM;CAC7E,IAAI;CACJ,IAAI,WAAW;CACf,IAAI,cAAc;CAClB,IAAI,UAAU;CACd,IAAI,cAAc;CAIlB,IAAI,WAAW;CAEf,MAAM,aAAa,WAAoB;EACrC,iBAAiB,UAAU,OAAO,aAAa,MAAM;EACrD,WAAW;CACb;CAEA,OAAO;EAEL,aAAW,eAAe,IAAI,WAAW;GACvC,MAAM,EAAE,MAAM,eAAe,OAAO,eAAe;GAGnD,KAAK,MAAM,MAAM,OAAO,cACtB,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,gBAAgB,GAAG;IAC/B,UAAU,OAAO;IACjB,IAAI,SAAS;KACX,UAAU,IAAI;KACd,KAAK,SAAS,EACZ,SAAS,oBAAoB,GAAG,EAAE,QAAQ,KAAK,CAAC,EAClD,CAAC;IACH,OACE,KAAK,SAAS,EACZ,SAAS,oBAAoB,GAAG,EAAE,QAAQ,MAAM,CAAC,EACnD,CAAC;GAEL;GAIJ,IAAI,CAAC,SACH;GAKF,IAAI,aAAa,eAAe,WAAW,IAAI,WAAW,MAAM,MAAM,IAAI,SAAS,GAAG;IACpF,cAAc;IACd,cAAc;IACd,4BAA4B;KAC1B,KAAK,UAAU,YAAY,KAAK,UAAU;KAC1C,cAAc;IAChB,CAAC;IACD;GACF;GACA,cAAc;GAGd,IAAI,aACF;GAKF,IAAI,eAAe;IACjB,IAAI,UAAU;KAEZ,MAAM,EAAE,WAAW,cAAc,iBAAiB,KAAK;KACvD,MAAM,QAAQ,eAAe,YAAY;KACzC,IAAI,OAAO,YACT,WAAW;KAEb,IAAI,QAAQ,GAAG;MACb,UAAU,IAAI;MAMd,KAAK,SAAS,EAAE,SAAS,oBAAoB,GAAG;OAAE,QAAQ;OAAM,SAAS,CAAC;MAAS,CAAC,EAAE,CAAC;KACzF,OAAO,IAAI,QAAQ,IACjB,UAAU,KAAK;IAEnB,OAEE,IAAI,MAAM,IAAI,WAAW,GACvB,UAAU,IAAI;GAGpB;EACF,CAAC;EAKD,iBACI,WAAW,UACT,MAAM;GACJ;GACA,mBAA2B;GAC3B,YAAoB;GACpB,YAAY,MAAkB;IAG5B,MAAM,WAAW,eAAe;KAC9B,IAAI,KAAK,aAAa,CAAC,SACrB;KAGF,UAAU,IAAI;KACd,4BAA4B;MAC1B,IAAI,KAAK,WACP;MAGF,KAAK,UAAU,SAAS;OAAE,KAAK,KAAK,UAAU;OAAc,UAAU;MAAU,CAAC;MACjF,KAAK,SAAS,EAAE,SAAS,oBAAoB,GAAG,EAAE,QAAQ,MAAM,CAAC,EAAE,CAAC;KACtE,CAAC;IACH,GAAG,EAAE;IAEL,KAAK,WAAW,IAAI,qBAAqB;KAEvC,IAAI,KAAK,kBAAkB;MACzB,KAAK,mBAAmB;MACxB;KACF;KACA,SAAS;IACX,CAAC;IAED,KAAK,SAAS,QAAQ,KAAK,SAAS;GACtC;GACA,UAAU;IACR,KAAK,YAAY;IACjB,KAAK,SAAS,WAAW;GAC3B;EACF,CACF,IACA,CAAC;EAGL,WAAW,UACT,MAAM;GACJ;GACA,YAAY,MAAkB;IAO5B,MAAM,eAAe,0BAA0B;KAC7C,4BAA4B;MAC1B,MAAM,EAAE,WAAW,cAAc,iBAAiB,KAAK;MACvD,MAAM,QAAQ,eAAe,YAAY;MAGzC,MAAM,SAAS,KAAK,IAAI,KAAK,KAAK;MAClC,UAAU,MAAM;MAChB,IAAI,CAAC,QACH,KAAK,SAAS,EAAE,SAAS,oBAAoB,GAAG,EAAE,QAAQ,MAAM,CAAC,EAAE,CAAC;KAExE,CAAC;IACH,GAAG,GAAG;IAEN,KAAK,UAAU,yBAAyB,KAAK,iBAAiB;KAC5D,IAAI,UAAU;MACZ,UAAU,KAAK;MACf,KAAK,SAAS,EAAE,SAAS,oBAAoB,GAAG,EAAE,QAAQ,MAAM,CAAC,EAAE,CAAC;KACtE;KACA,aAAa;IACf,CAAC;GACH;GACA,UAAU;IACR,KAAK,QAAQ;GACf;EACF,CACF;EAGA,WAAW,UACT,MAAM;GACJ,YAAY,MAAkB;IAC5B,MAAM,SAAS,OAAO,GAAG,QAAQ,CAAA,CAC9B,WAAW,uCAAuC,CAAA,CAClD,WAAW;KAAE,aAAa;KAAM,gBAAgB;IAAU,CAAC,CAAA,CAC3D,OAAO,OAAO,GAAG,SAAS,CAAC,CAAC,WAAW,QAAQ,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,MAAM,0BAA0B,CAAC,CAAC,CAAA,CAClG,GAAG,eAAe;KACjB,UAAU,IAAI;KACd,KAAK,SAAS,EACZ,SAAS,CACP,kBAAkB,GAAG;MAAE,MAAM;MAAI,UAAU;MAAO,UAAU;KAAS,CAAC,GAGtE,oBAAoB,GAAG;MAAE,QAAQ;MAAM,SAAS,CAAC;KAAS,CAAC,CAC7D,EACF,CAAC;IACH,CAAC;IAEH,kBAAkB,OAAO,GAAG,KAAK,CAAA,CAC9B,WAAW,gEAAgE,CAAA,CAC3E,OAAO,MAAM,CAAC,CAAC;IAIlB,KAAK,UAAU,eAAe,YAAY,eAAe;GAC3D;GAEA,UAAU;IACR,iBAAiB,OAAO;IACxB,kBAAkB,KAAA;GACpB;EACF,CACF;CACF;AACF;;;;;;;;;AAYA,SAAS,yBAAyB,SAAsB,cAAsC;CAC5F,OAAO,QACL,iBAAiB,SAAS,eAAe,aAAa,GAAG,EAAE,SAAS,KAAK,CAAC,GAC1E,iBAAiB,SAAS,gBAAgB,UAAU;EAElD,IAAI,MAAM,UAAU,QAAQ,sBAAsB,CAAC,CAAC,SAAS,QAAQ,cAAc,QAAQ,cACzF,aAAa;CAEjB,CAAC,CACH;AACF;;;;;;;;AC/PA,IAAM,sBAAsB,WAAW,UACrC,MAAM;CACJ,UAAU;CACV,SAAmC,EAAE,OAAO,mBAAmB,KAAK,QAAQ,IAAI;CAEhF,OAAO,EAAE,QAA8B;EACrC,MAAM,gBAAgB,KAAK,YAAY,KAAK,MAAM,IAAI,MAAM;EAC5D,MAAM,SAAS,KAAK,IAAI,eAAe,cAAc,SAAS,KAAK,gBAAgB,MAAM;EACzF,IAAI,UAAU,KAAK,WAAW,KAAK,SAAS;GAC1C,KAAK,UAAU;GACf,KAAK,SAAS,EAAE,OAAO,mBAAmB,OAAO,IAAI;EACvD;CACF;AACF,CACF;;AAGA,IAAa,sBAAiC,CAC5C,qBACA,aAAW,kBAAkB,IAAI,SAAS,KAAK,OAAO,mBAAmB,CAAC,EAAE,UAAU,IAAI,CAC5F;;;;;;;;ACZA,IAAa,qBAAqB,EAAE,UAAU,QAAkC,CAAC,MAAiB,CAChG,WAAW,UAKT,MAAM;CACJ;CACA;CAEA,YAAY,MAAkB;EAC5B,KAAK,YAAY,KAAK;EACtB,KAAK,UAAU,iBAAiB,UAAU,KAAK,eAAe,EAAE,SAAS,KAAK,CAAC;CACjF;CAEA,UAAgB;EACd,KAAK,UAAU,oBAAoB,UAAU,KAAK,aAAa;EAC/D,aAAa,KAAK,MAAM;EACxB,KAAK,UAAU,UAAU,OAAO,cAAc;CAChD;CAGA,sBAA6C;EAC3C,KAAK,UAAU,UAAU,IAAI,cAAc;EAC3C,aAAa,KAAK,MAAM;EACxB,KAAK,SAAS,iBAAiB,KAAK,UAAU,UAAU,OAAO,cAAc,GAAG,OAAO;CACzF;AACF,CACF,GACA,QACF;AAEA,IAAM,WAAS,aAAW,MAAM;CAE9B,sDAAsD,EACpD,iBAAiB,+BACnB;CAGA,oEAAoE,EAClE,iBAAiB,cACnB;AACF,CAAC;;;;;;ACtCD,IAAa,YAAY,EAAE,YAAY,gBAAgB,YAAA,eAAa,SAA0B,CAAC,MAAmB;CAChH,OAAO,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,gBAAc,WAAoB,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC,OAAO,QAAQ;AACzG;;;;;;;ACiBA,IAAa,WAAW,aAAW,kBAAkB,GAAG,EAAE,UAAU,IAAI,CAAC;;;;AAKzE,IAAa,eAAe,UAAkB;CAC5C,OAAO,cAAY,kBAAkB,IAAI,gBAAgB;EACvD,IAAI,CAAC,YAAY,YACf,OAAO;EAGT,MAAM,UAAwB,CAAC;EAC/B,YAAY,QAAQ,aAAa,OAAO,KAAK,OAAO,KAAK,SAAS;GAChE,MAAM,WAAW,KAAK,SAAS;GAC/B,MAAM,WAAW,SAAS,QAAQ,OAAO,EAAE;GAC3C,IAAI,aAAa,UACf,QAAQ,KAAK;IACX,MAAM;IACN,IAAI;IACJ,QAAQ;GACV,CAAC;EAEL,CAAC;EAED,IAAI,QAAQ,QACV,OAAO,CAAC,aAAa;GAAE;GAAS,YAAY;EAAK,CAAoB;EAGvE,OAAO;CACT,CAAC;AACH;AAkCA,IAAM,sBAA8C;CAClD,yBAAyB;CACzB,iBAAiB;CACjB,eAAe;CACf,eAAe;CACf,OAAO;CACP,SAAS;CACT,QAAQ;CACR,cAAc;CACd,QAAQ;AACV;AAEA,IAAM,UAAoD;CAExD,UAAU;CAEV,SAAS;AACX;;;;;AAMA,IAAa,yBAAyB,cAAkD;CACtF,MAAM,QAAQ,aAAa,CAAC,GAAG,WAAW,mBAAmB;CAC7D,OAAO;EAEL,aAAW,cAAc,IAAI,QAAQ;GACnC,IAAI,MAAM,KAAE,KAAA,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;EACf,CAAC;EAED,MAAM,2BAA2B,cAAY,wBAAwB,GAAG,IAAI;EAC5E,MAAM,mBAAmB,gBAAgB;EACzC,MAAM,iBAAiB,cAAc;EACrC,MAAM,cAAc,WAAW;EAC/B,MAAM,iBAAiB,cAAc,EAAE,iBAAiB,KAAM,CAAC;EAC/D,MAAM,aAAa,KAAA,KAAa,aAAW,SAAS,GAAG,MAAM,QAAQ;EACrE,MAAM,SAAS;EACf,MAAM,uBAAuB,oBAAoB;EACjD,MAAM,WAAW,QAAQ;EACzB,MAAM,eAAe,CAAC,YAAY,GAAG,YAAY;EACjD,MAAM,gBAAgB,aAAW;EACjC,MAAM,eAAe,cAAY,MAAM,WAAW;EAClD,MAAM,aAAa,KAAA,KAAa,cAAY,SAAS,GAAG,MAAM,QAAQ;EAOtE,MAAM,YACJ,cAAY,kBAAkB,IAAI,OAChC,GAAG,eACF,GAAG,YAAY,OAAO,KAAK,GAAG,YAAY,QAAQ,KAAK,GAAG,YAAY,MAAM,KAAK,GAAG,YAAY,MAAM,KACnG,CAAC,IACD,EACN;EACF,MAAM,iBAAiB,cAAc;EACrC,MAAM,YAAY;EAClB,MAAM,WAAW,cAAY,QAAQ,GAAG,MAAM,OAAO;EAGrD,SAAO,GACL;GACE,IAAK,MAAM,UAAU,QAAQ,MAAM,YAAY,CAAC;GAGhD,GAAI,MAAM,gBAAgB,CAAC,aAAa,IAAI,CAAC;GAE7C,GAAI,MAAM,gBAAgB,sBAAsB,CAAC;GAEjD,GAAI,MAAM,UAAU,gBAAgB,CAAC;GAErC,GAAI,MAAM,SAAS,eAAe,CAAC;GAGnC;IACE,KAAK;IACL,gBAAgB;IAChB,WAAW;GACb;EACF,CAAC,CAAC,OAAO,QAAQ,CACnB;CACF,CAAC,CAAC,OAAO,QAAQ;AACnB;AAwBA,IAAa,OAAwC,EACnD,QAAQ,EACN,WAAW,UACb,EACF;AAEA,IAAa,YAA6C,EACxD,QAAQ,EACN,WAAW,SACb,EACF;AAEA,IAAa,oBAAoB;AAEjC,IAAa,gBAAgB;CAC3B,MAAM;CACN,OAAO;AACT;;;;AAKA,IAAa,yBAAyB,EACpC,WACA,eACA,OAAO,WACP,oBAAoB,wBACpB,cAC0B,CAAC,MAAiB;CAC5C,MAAM,QAAsD,aAAa,CAAC,GAAG,WAAW,iBAAiB;CACzG,OAAO;EACL;EACA,aAAW,UAAU,GAAG,cAAc,MAAM;EAC5C,gBAAgB,EAAE,UAAU,CAAC;EAC7B,0BACE,mBAAmB,eAAe,OAAO,cAAc,SAAS,cAAc,OAAO,cAAc,KAAK,CAAC;EAC3G,MAAM,QAAQ,aAAa,aAAW,iBAAiB,GAAG,EAAE,OAAO,MAAM,OAAO,UAAU,CAAC;EAC3F,MAAM,SAAS,aAAa,aAAW,kBAAkB,GAAG,EAAE,OAAO,MAAM,QAAQ,UAAU,CAAC;GAC7F,MAAM,UAAU,aAAa,kBAC5B,WAAW,UACT,MAAM;GACJ,YAAY,MAAkB;IAC5B,IAAI,MAAM,UAAU,WAClB,KAAK,UAAU,UAAU,IAAI,GAAG,MAAM,SAAS,UAAU,MAAM,KAAK,CAAC;IAEvE,IAAI,eACF,KAAK,UAAU,MAAM,YAAY,oBAAoB,KAAK;GAE9D;EACF,CACF;CACJ,CAAC,CAAC,OAAO,QAAQ;AACnB;;;;AA6BA,IAAa,wBAA2B,EAAE,IAAI,MAAM,WAAW,eAAoD;CACjH,MAAM,aAA0B,CAAC;CACjC,IAAI,MACF,WAAW,KAAK,UAAU,IAAI,CAAC;CAGjC,IAAI,aAAa,UAAU,aAAa;EACtC,MAAM,SAAS,SAAS;EACxB,MAAM,MAAO,SAAS,MAAM,OAAwC,SAAS,MAAM;EACnF,WAAW,KACT,UACE,IAAI,uBAAuB;GACzB;GACA,SAAS,aAAa;GACtB;GACA,MAAM;IACJ,WAAW,eAAe,IAAI;IAC9B,YAAY,eAAe,IAAI;IAC/B,aAAa,SAAS,eAAe,aAAa,MAAM;GAC1D;EACF,CAAC,CACH,CACF;CACF;CAEA,OAAO;AACT;;;AClTA,IAAa,kBAAkB,iBAAoC,CAAC,CAAC;;;;AAKrE,IAAa,sBAAqD;CAChE,SAAS,CAAC;CACV,QAAQ,CAEN,gBAAgB,GAAG,EAAE,MAAM,SAAS,CAAC,GACrC,SAAO,GAAG,YAAY,CACxB;CACA,KAAK;EAEH,IAAI;EACJ,gBAAgB,GAAG;GAAE,MAAM;GAAO,cAAc;EAAK,CAAC;EACtD,SAAO,GAAG,CACR;GACE,KAAK;GACL,MAAM,SAAS;IAEb,KAAK,IAAI,eAAe,MAAM;IAC9B,OAAO;GACT;EACF,CACF,CAAC;CACH;AACF;;;;;;AChCA,IAAa,mBAAmB,MAAgC,QAAQ,QAAmB;CACzF,MAAM,aAAa,UAAuB,WAAW,KAAK,CAAC,CAAC,QAAQ,EAAE,QAAQ,SAAS,IAAI,KAAK,IAAI,EAAE,CAAC;CACvG,OAAO,WAAW,OAAY;EAC5B,SAAS,UAAU,UAAU,KAAK;EAClC,SAAS,GAAkB,OAAoB,UAAU,GAAG,KAAK;CACnE,CAAC;AACH;;;;;;ACHA,IAAa,eAAe,EAAE,UAA6B,CAAC,MAAiB;CAC3E,OAAO,CACL,WAAW,UACT,MAAM;EACJ,cAA6B,WAAW;EACxC,OAAO,QAAoB;GACzB,MAAM,UAAU,IAAI,gBAA4B;GAChD,IAAI,OAAO;IAET,MAAM,EAAE,MAAM,OAAO,OAAO,KAAK;IAEjC,MAAM,UADO,OAAO,MAAM,IAAI,YAAY,MAAM,EAChC,CAAA,CAAK,SAAS,KAAK;IACnC,KAAK,MAAM,KAAK,SACd,IAAI,EAAE,UAAU,KAAA,GAAW;KAEzB,MAAM,QAAQ,OAAO,EAAE;KACvB,MAAM,MAAM,QAAQ,EAAE,EAAE,CAAC;KACzB,QAAQ,IAAI,OAAO,KAAK,cAAc;IACxC;GAEJ;GAEA,KAAK,cAAc,QAAQ,OAAO;EACpC;CACF,GACA,EACE,cAAc,MAAM,EAAE,YACxB,CACF,GAEA,QACF;AACF;AAEA,IAAM,iBAAiB,WAAW,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAEjE,IAAM,WAAS,aAAW,MAAM,EAC9B,kBAAkB;CAChB,gBAAgB;CAChB,qBAAqB;CACrB,qBAAqB;AACvB,EACF,CAAC;;;;;;;ACvBD,IAAa,aAAa,YAA2C;CACnE,MAAM,SAAS,iBAAiB,SAAS,eAAe;CACxD,MAAM,QAAQ;EACZ,MAAM,OAAO,iBAAiB,mBAAmB,CAAC,CAAC,KAAK;EACxD,SAAS,OAAO,iBAAiB,oBAAoB,CAAC,CAAC,KAAK;EAC5D,OAAO,OAAO,iBAAiB,kBAAkB,CAAC,CAAC,KAAK;CAC1D;CAEA,OAAO,CACL,gBAAgB,OAAO,GACvB,aAAW,UAAU;EACnB,sBAAsB,EACpB,iBAAiB,UAAU,MAAM,IAAI,EACvC;EACA,yBAAyB,EACvB,iBAAiB,UAAU,MAAM,OAAO,EAC1C;EACA,uBAAuB,EACrB,iBAAiB,UAAU,MAAM,KAAK,EACxC;EAEA,qBAAqB,EACnB,WAAW,8CACb;EACA,4BAA4B,EAC1B,SAAS,kBACX;EACA,oBAAoB,EAClB,OAAO,6BACT;EACA,8BAA8B;GAC5B,OAAO;GACP,iBAAiB;GACjB,aAAa;EACf;EACA,8CAA8C;GAC5C,OAAO;GACP,iBAAiB;EACnB;EACA,iCAAiC;GAC/B,SAAS;GACT,qBAAqB;GACrB,YAAY;EACd;EACA,oDAAoD,EAClD,cAAc,iBAChB;EACA,4DAA4D,EAC1D,QAAQ,kBACV;EACA,kBAAkB;GAChB,SAAS;GACT,YAAY;EACd;EACA,uBAAuB,EACrB,QAAQ,kBACV;CACF,CAAC,CACH;AACF;AAEA,IAAM,aAAa,UAAkB;CACnC,MAAM,MAAM,IAAI;;oDAEkC,MAAM;;;CAIxD,OAAO,kCAAkC,KAAK,GAAG,EAAE;AACrD;AAYA,IAAM,gBAAgB,UACpB,OAAO,UAAU,YACjB,UAAU,QACV,OAAQ,MAAqB,aAAa,YAC1C,OAAQ,MAAqB,gBAAgB,YAC7C,OAAQ,MAAqB,YAAY;;;;AAK3C,IAAM,uBAAuB,SAAiB,eAAmC;CAC/E,MAAM,WAAW,QAAQ,QAAQ,WAAW,QAAQ;CACpD,IAAI,aAAa,IACf,OAAO;CAIT,MAAM,YAAY,QAAQ,QAAQ,WAAW,UAAU,WAAW,CAAC;CACnE,IAAI,cAAc,IAChB,OAAO;CAIT,MAAM,aAAa,QAAQ,QAAQ,WAAW,OAAO;CACrD,IAAI,eAAe,IAAI;EACrB,MAAM,aAAa,aAAa,WAAW,QAAQ;EACnD,IAAI,aAAa,cAAc,aAAa,YAC1C,OAAO;CAEX;CAEA,OAAO;AACT;AAEA,IAAM,iCAAiC,MAAkB,MAAc,IAAY,WAAmB;CACpG,MAAM,OAAqB,CAAC;CAC5B,kBAAkB,KAAK,QAAQ,YAAY,gBAAgB,iBAAiB;EAC1E,IAAI,iBAAiB,MAAM,eAAe,MACxC;EAEF,KAAK,KAAK;GAAE,GAAG;GAAY,MAAM;GAAgB,IAAI;EAAa,CAAC;CACrE,CAAC;CACD,MAAM,YAAY,UAAU,GAAG;EAAE;EAAM;EAAI;CAAO,GAAG,KAAK,MAAM,IAAI,MAAM;CAC1E,MAAM,OAAO,KAAK,KAAK,OAAO;EAC5B,GAAG;EACH,MAAM,UAAU,OAAO,EAAE,MAAM,CAAC;EAChC,IAAI,UAAU,OAAO,EAAE,IAAI,EAAE;CAC/B,EAAE;CACF,KAAK,SAAS;EACZ,SAAS;GAAE;GAAM;GAAI;EAAO;EAC5B,GAAG,eAAe,KAAK,OAAO,IAAI;CACpC,CAAC;AACH;AAEA,IAAM,uBAAuB,IAAI;;;;;;AAOjC,IAAM,mBAAmB,EACvB,UACA,eAAe,sBACf,YAAY,MACZ,QAAQ,UAER,OACE,OAAO,SAAS;CACd,IAAI;EACF,MAAM,UAAU,KAAK,MAAM,IAAI,SAAS;EAGxC,MAAM,CAAC,UAAS,MAFK,SAAS;GAAE;GAAc;EAAQ,CAAC,EAAA,CAEhC,MAAM,SAAS,KAAK,CAAC;EAC5C,MAAM,SAAS,QAAQ,cAAyB,OAAO,CAAC,CAAC,IAAI,CAAC;EAC9D,MAAM,cAAc,MAAM,QAAQ,MAAM,IAAI,OAAO,OAAO,YAAY,IAAI,CAAC;EAC3E,IAAI,eAAe,YAAY,SAAS,GAAG;GACzC,MAAM,cAA4B,CAAC;GACnC,KAAK,MAAM,cAAc,aAAa;IACpC,MAAM,MAAM,oBAAoB,SAAS,UAAU;IACnD,IAAI,QAAQ,IACV,YAAY,KAAK;KACf,MAAM;KACN,IAAI,MAAM,WAAW,SAAS;KAC9B,UAAU;KACV,SAAS,eAAe,WAAW;KACnC,SAAS,CACP;MACE,MAAM;MACN,QAAQ,MAAM,MAAM,OAAO;OACzB,8BAA8B,MAAM,MAAM,IAAI,WAAW,WAAW;MACtE;KACF,CACF;IACF,CAAC;GAEL;GAEA,OAAO;EACT;CACF,SAAS,KAAK;EACZ,IAAI,MAAM,KAAE,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,KAAA;EAAA,CAAC;CACf;CAEA,OAAO,CAAC;AACV,GACA;CACE;CACA;AACF,CACF;;;;;;;;AC/MF,IAAa,SAAS,UAAwB,CAAC,MAAiB;CAC9D,MAAM,eAAe;CACrB,MAAM,iBAAiB,QAAQ,YAAY;CAE3C,IAAI,YAAY;CAChB,MAAM,OAAO,aAAuB;EAClC,IAAI,SAAS,WAAW,WACtB,YAAY,SAAS;CAGzB;CAGA,MAAM,UAAU,YAAY,OAAe;CAU3C,MAAM,YAAY,WAAW,OAAmB;EAC9C,eAAe;GAAE,aAAa,WAAW;GAAM,UAAU,CAAC;GAAG,YAAY;EAAE;EAC3E,SAAS,EAAE,aAAa,UAAU,cAAc,OAAO;GAErD,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,OAAO,GAAG;IACtB,MAAM,MAAM,OAAO;IACnB,IAAI,cAAc;IAClB,OAAO,cAAc,SAAS,UAAU,SAAS,gBAAgB,KAC/D;IAEF,IAAI,cAAc,GAAG;KACnB,WAAW,SAAS,MAAM,WAAW;KACrC,IAAI,UAAU;KACd,cAAc,YAAY,OAAO,EAC/B,cAAc;MACZ,IAAI,UAAU,aAAa;OACzB;OACA,OAAO;MACT;MACA,OAAO;KACT,EACF,CAAC;IACH;GACF;GAGF,IAAI,CAAC,GAAG,YAAY;IAClB,IAAI,QAAQ;IACZ,OAAO;KAAE;KAAa;KAAU;IAAW;GAC7C;GAGA,IAAI,UAAU,GAAG,MAAM,IAAI,WAAW;GACtC,IAAI,CAAC,WAAW,GAAG,WAAW,IAAI,SAAS,GACzC,GAAG,QAAQ,aAAa,OAAO,QAAQ;IACrC,IAAI,UAAU,KAAK,QAAQ,GAAG,WAAW,IAAI,QAC3C,UAAU;GAEd,CAAC;GAEH,IAAI,SAAS;IACX,IAAI,CAAC,CAAC;IACN,OAAO;KAAE,aAAa,WAAW;KAAM,UAAU,CAAC;KAAG,YAAY;IAAE;GACrE;GAGA,MAAM,MAAM,KAAK,IAAI;GACrB,MAAM,MAAsC,CAAC;GAC7C,GAAG,QAAQ,aAAa,OAAO,KAAK,OAAO,KAAK,aAAa;IAC3D,IAAI,QAAQ,GAAG,WAAW,IAAI,UAAU,SAAS,SAAS,GACxD,IAAI,KAAK;KAAE,MAAM;KAAO,IAAI;IAAI,CAAC;GAErC,CAAC;GAED,IAAI,IAAI,SAAS,GAAG;IAElB,IADoB,SAAS,SAAS,KAAK,aAAa,KAAK,MAAM,aAAa,gBAC/D;KAEf,IAAI,WAAW;KACf,IAAI,SAAS;KACb,YAAY,QAAQ,GAAG,GAAG,MAAM,IAAI,SAAS,MAAM,OAAO;MACxD,WAAW;MACX,SAAS;KACX,CAAC;KAED,IAAI,YAAY,GAAG;MACjB,cAAc,YAAY,OAAO,EAC/B,SAAS,MAAM,OAAO,EAAE,SAAS,YAAY,OAAO,QACtD,CAAC;MAED,MAAM,aAAa,KAAK,IAAI,UAAU,IAAI,EAAE,CAAC,IAAI;MACjD,MAAM,WAAW,IAAI,IAAI,SAAS,EAAE,CAAC;MACrC,cAAc,YAAY,OAAO,EAC/B,KAAK,CAAC,WAAW,KAAK,EAAE,OAAO,WAAW,CAAC,CAAC,CAAC,MAAM,YAAY,QAAQ,CAAC,EAC1E,CAAC;MAGD,WAAW,CAAC,GAAG,SAAS,MAAM,GAAG,EAAE,GAAG,MAAM,YAAY;KAC1D;IACF,OAAO;KAEL,aAAa;KACb,WAAW,CAAC,GAAG,UAAU,MAAM,YAAY;KAC3C,cAAc,YAAY,OAAO,EAC/B,KAAK,IAAI,KAAK,EAAE,MAAM,SAAS,WAAW,KAAK,EAAE,OAAO,WAAW,CAAC,CAAC,CAAC,MAAM,MAAM,EAAE,CAAC,EACvF,CAAC;IACH;GACF;GAEA,IAAI,QAAQ;GACZ,OAAO;IAAE;IAAa;IAAU;GAAW;EAC7C;EACA,UAAU,MAAM,aAAW,YAAY,KAAK,IAAI,UAAU,MAAM,WAAW;CAC7E,CAAC;CAyCD,OAAO;EAAC;EAtCQ,WAAW,UACzB,MAAM;GAGgB;GAFpB;GAEA,YAAY,MAA0B;IAAlB,KAAA,OAAA;IAClB,KAAK,UAAU;GACjB;GAEA,SAAS;IACP,KAAK,UAAU;GACjB;GAEA,YAAY;IACV,MAAM,EAAE,aAAa,KAAK,KAAK,MAAM,MAAM,SAAS;IACpD,IAAI,SAAS,WAAW,GAAG;KACzB,aAAa,KAAK,MAAM;KACxB,KAAK,SAAS,KAAA;KACd;IACF;IAEA,IAAI,KAAK,WAAW,KAAA,GAClB;IAIF,MAAM,QAAQ,KAAK,IAAI,kBAAkB,SAAS,KAAK,KAAK,IAAI,CAAC;IACjE,KAAK,SAAS,iBAAiB;KAC7B,KAAK,SAAS,KAAA;KACd,KAAK,KAAK,SAAS,EAAE,SAAS,QAAQ,GAAG,KAAK,IAAI,CAAC,EAAE,CAAC;IACxD,GAAG,KAAK;GACV;GAEA,UAAU;IACR,aAAa,KAAK,MAAM;GAC1B;EACF,CAGiB;EAAS;CAAM;AACpC;AAEA,IAAM,wBAAwB;AAC9B,IAAM,0BAA0B;AAChC,IAAM,mBAAmB;AAEzB,IAAM,WAAS,aAAW,MAAM;CAC9B,aAAa,EACX,WAAW,6BACb;CACA,oBAAoB;EAClB,MAAM,EACJ,YAAY,qEACd;EACA,QAAQ,CAAC;CACX;AACF,CAAC;;;;;;AC3KD,IAAa,gBAA2B,CACtC,WAAW,UACT,MAAM;CACJ;CACA,YAAY,MAAkB;EAC5B,KAAK,OAAO,WAAW,WAAW,IAAI;CACxC;CAEA,OAAO,QAAoB;EACzB,KAAK,OAAO,WAAW,WAAW,QAAQ,KAAK,IAAI;CACrD;AACF,GACA;CACE,cAAc,aAAa,SAAS;CACpC,UAAU,WACR,aAAW,aAAa,IAAI,SAAS;EACnC,OAAO,KAAK,OAAO,MAAM,CAAC,EAAE,QAAQ,WAAW;CACjD,CAAC;AACL,CACF,GAEA,QACF;AAEA,IAAM,YAAN,cAAwB,WAAW;CACb;CAApB,YAAY,OAAuB;EACjC,MAAM;EADY,KAAA,QAAA;CAEpB;CAEA,QAAyB;EACvB,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,MAAM,EAAE,SAAS,OAAO,cAAc,KAAK,KAAK;EAChD,KAAK,YAAY,GAAG,UAAU,SAAS,EAAE;EACzC,KAAK,cAAc,KAAK;EACxB,OAAO;CACT;AACF;AAEA,IAAM,aAAa,IAAI,eAAe;CAGpC,QAAQ;CAIR,aAAa,UAAW,QAAQ,KAAK,MAAM,EAAE,IAAI,OAAO,WAAW,QAAQ,EAAE,QAAQ,IAAI,UAAU,MAAM,EAAE,EAAE,CAAC;AAChH,CAAC;AAED,IAAM,WAAS,aAAW,MAAM,EAC9B,WAAW;CACT,cAAc;CACd,aAAa;CACb,SAAS;AACX,EACF,CAAC;;;;;;;ACrDD,IAAa,oBACX,aAAW,MAAM;CACf,GAAG;CAKH,sCAAsC;EACpC,oBAAoB;EACpB,iBAAiB;EACjB,WAAW;EACX,OAAO;CACT;CAEA,mBAAmB,EACjB,SAAS,eACX;CAGA,uBAAuB;EACrB,SAAS;EACT,eAAe;EACf,mBAAmB;CACrB;CACA,sBAAsB;EACpB,SAAS;EACT,YAAY;EACZ,KAAK;CACP;CACA,qBAAqB;EACnB,SAAS;EACT,SAAS;EACT,cAAc;EACd,QAAQ;CACV;CACA,2BAA2B,EACzB,iBAAiB,wBACnB;CACA,mCAAmC,EACjC,iBAAiB,0BACnB;CACA,iCAAiC,EAC/B,iBAAiB,wBACnB;AACF,CAAC;;AAGH,IAAa,cAAc,KAAgB,UAAmE,CAAC,MAC7G,WAAW,KAAK;CACd,OAAO,CAAC,aAAa,QAAQ,KAAK,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAC5D,YAAY;EAAE,YAAY;EAAK,GAAG,QAAQ;CAAW;AACvD,CAAC;;AAYH,IAAa,cACX,MACA,EAAE,KAAK,WAAW,MAAM,qBACA;CACxB,MAAM,OAAO,OAAO,GAAG,MAAM,CAAA,CAC1B,WAAW,kBAAkB,SAAS,CAAA,CACtC,WAAW,EAAE,YAAY,IAAI,CAAC,CAAA,CAC9B,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,IAAI,CAAC;CACtC,IAAI,MAEF,KAAK,OAAO,OAAO,IAAI,IAAI,CAAC,CAAC,WAAW,4BAA4B,cAAc,CAAC;CAGrF,OAAO;AACT;;AAWA,IAAa,iBAAiB,YAC5B,OAAO,GAAG,MAAM,CAAA,CACb,WAAW,mBAAmB,CAAA,CAC9B,OACC,GAAG,QAAQ,KAAK,WACd,OAAO,GAAG,QAAQ,CAAA,CACf,WAAW,oBAAoB,OAAO,SAAS,CAAA,CAC/C,WAAW;CACV,QAAQ;CACR,cAAc,OAAO;CACrB,GAAI,OAAO,SAAS,EAAE,eAAe,OAAO,OAAO,IAAI,CAAC;AAC1D,CAAC,CAAA,CACA,OAAO,OAAO,IAAI,OAAO,IAAI,CAAC,CAAA,CAE9B,GAAG,cAAc,UAAU;CAC1B,MAAM,eAAe;CACrB,OAAO,QAAQ;AACjB,CAAC,CACL,CACF;AAGJ,IAAM,UAAU,OAAO,YACrB,KAAK,SAAS,QACZ,CAAC,cAAc,iBAAiB,CAAC,CAAC,KAAK,aAAa,CAClD,GAAG,SAAS,aAAa,IAAI,KAC7B;CACE,uBAAuB,eAAe,IAAI;CAC1C,oBAAoB,eAAe,IAAI;AACzC,CACF,CAAC,CACH,CACF;;;;;;;;;;;ACjHA,IAAa,OAAO,EAAE,OAAO,aAAa,KAAK,YAAY,MAAM,UAAU,UAAsB,CAAC,MAAiB;CACjH,MAAM,aAA0B;EAAC;EAAkB;EAAgB;CAAU;CAC7E,IAAI,WACF,WAAW,KAAK,SAAS,CAAC;CAE5B,IAAI,SACF,WAAW,KAAK,WAAW,CAAC;CAE9B,IAAI,OACF,WAAW,KAAK,eAAe,OAAO,UAAU,CAAC;CAGnD,OAAO;AACT;;AAcA,IAAa,cAAc,YAAY,OAAyD;;AAGhG,IAAa,gBAAgB,YAAY,OAAa;;AAGtD,IAAa,YAAY,YAAY,OAAqC;;;;;AAM1E,IAAa,mBAAmB,WAAW,OAAkB;CAC3D,cAAc,CAAC;CACf,SAAS,OAAO,OAAO;EACrB,IAAI,OAAO;EAGX,IAAI,GAAG,YACL,OAAO,KAAK,KAAK,UAAU;GACzB,GAAG;GACH,MAAM,GAAG,QAAQ,OAAO,KAAK,MAAM,CAAC;GACpC,IAAI,GAAG,QAAQ,OAAO,KAAK,IAAI,EAAE;EACnC,EAAE;EAGJ,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,WAAW,GAAG;GAC1B,MAAM,EAAE,MAAM,IAAI,aAAa,OAAO;GAGtC,OAAO,CAAC,GADQ,KAAK,QAAQ,SAAS,EAAE,KAAK,SAAS,QAAQ,KAAK,OAAO,GAC/D,GAAS;IAAE;IAAM;IAAI,YAAY,SAAS;IAAY;IAAU,OAAO;GAAM,CAAC;EAC3F,OAAO,IAAI,OAAO,GAAG,aAAa,GAChC,OAAO,CAAC;OACH,IAAI,OAAO,GAAG,SAAS,GAAG;GAC/B,MAAM,EAAE,MAAM,OAAO,OAAO;GAC5B,OAAO,KAAK,KAAK,SAAU,KAAK,SAAS,QAAQ,KAAK,OAAO,KAAK;IAAE,GAAG;IAAM,OAAO;GAAK,IAAI,IAAK;EACpG;EAGF,OAAO;CACT;AACF,CAAC;;AAGD,IAAa,YAAY,UAAkC,MAAM,MAAM,gBAAgB;;AAGvF,IAAM,WAAW,MAAY,UAC3B,WAAW,KAAK,EAAE,OAAO;CAAC;CAAU,UAAU;CAAQ,SAAS;AAAc,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;;;;;AAM5G,IAAa,iBAAiB,aAAW,YAAY,QAAQ,CAAC,gBAAgB,IAAI,UAAU;CAC1F,MAAM,UAAU,IAAI,gBAA4B;CAIhD,MAAM,QAHQ,MAAM,MAAM,gBAGZ,CAAA,CACX,SAAS,SACR,KAAK,SAAS,UAAU,SAAS,aAC/B,SAAS,OAAO,KAAK,WAAW;EAC9B,MAAM,KAAK,OAAO,MAAM;EACxB,IAAI,KAAK,OAAO,MAAM;EACtB,MAAM,QAAQ,MAAM,MAAM,KAAK,KAAK;CACtC,EAAE,CACJ,CACF,CAAA,CACC,QAAQ,SAAS,KAAK,MAAM,MAAM,IAAI,UAAU,KAAK,OAAO,KAAK,EAAE,CAAA,CACnE,MAAM,OAAO,UAAU,MAAM,OAAO,MAAM,QAAQ,MAAM,KAAK,MAAM,EAAE;CACxE,KAAK,MAAM,QAAQ,OACjB,QAAQ,IAAI,KAAK,MAAM,KAAK,IAAI,KAAK,IAAI;CAE3C,OAAO,QAAQ,OAAO;AACxB,CAAC;AAID,IAAM,UAAyC;CAC7C,MAAM;CACN,OAAO;CACP,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,KAAK;CACL,KAAK;CACL,OAAO;CACP,OAAO;CACP,MAAM;AACR;;AAGA,IAAM,aAAmC;CACvC,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,OAAO;CACP,KAAK;CACL,MAAM;CACN,MAAM;CACN,KAAK;CACL,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,OAAO;CACP,KAAK;CACL,MAAM;CACN,GAAG;AACL;;AAUA,IAAa,cAAc,OAAoB,aAA2C;CACxF,MAAM,QAAQ,MAAM,MAAM,kBAAkB,KAAK,KAAK,CAAC;CACvD,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,WAAW,KAAK,QAAQ,WAAW,KAAK,IAC1C;EAEF,KAAK,MAAM,YAAY,KAAK,SAAS,WACnC,KAAK,MAAM,SAAS,SAAS,QAAQ;GACnC,MAAM,OAAO,KAAK,OAAO,MAAM;GAC/B,MAAM,KAAK,KAAK,OAAO,MAAM;GAC7B,IAAI,YAAY,QAAQ,WAAW,MAAM,MAAM,MAAM,IAAI,QACvD,OAAO;IAAE;IAAM;IAAI,MAAM,MAAM;IAAM,OAAO,KAAK;GAAM;EAE3D;CAEJ;AAEF;;AAGA,IAAa,mBAA8B,CACzC,cAAc,MAAM,aAAa;CAC/B,MAAM,QAAQ,WAAW,KAAK,OAAO,QAAQ;CAC7C,IAAI,CAAC,OACH,OAAO;CAET,OAAO;EACL,KAAK,MAAM;EACX,KAAK,MAAM;EACX,OAAO;EACP,cAAc;GACZ,MAAM,MAAM,SAAS,cAAc,KAAK;GACxC,IAAI,YAAY;GAChB,MAAM,SAAS,SAAS,cAAc,MAAM;GAC5C,OAAO,YAAY;GACnB,OAAO,MAAM,aAAa,eAAe,QAAQ,MAAM,SAAS,UAAU;GAC1E,MAAM,MAAM,SAAS,cAAc,MAAM;GACzC,IAAI,YAAY;GAChB,IAAI,cAAc,MAAM;GACxB,MAAM,QAAQ,SAAS,cAAc,MAAM;GAC3C,MAAM,YAAY;GAClB,MAAM,cAAc,WAAW,MAAM;GACrC,IAAI,OAAO,QAAQ,KAAK,KAAK;GAC7B,OAAO,EAAE,IAAI;EACf;CACF;AACF,CAAC,GACD,YACF;;AAGA,IAAa,gBAAgB,SAAiB,SAC5C,WAAW,QAAQ,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC,MAAM,KAAK;;;;;;;AAQzD,IAAM,kBAAkB,OAAyC,eAAkC;CAIjG,IAAI,SAAS;CACb,MAAM,cAAc,SAAqB;EACvC,MAAM,OAAO,KAAK,MAAM,IAAI,SAAS;EACrC,MAAM,KAAK,EAAE;EACb,MAAW,IAAI,CAAA,CACZ,MAAM,aAAa;GAClB,IAAI,OAAO,UAAU,KAAK,MAAM,IAAI,SAAS,MAAM,MACjD;GAEF,KAAK,SAAS,EAAE,SAAS,YAAY,GAAG;IAAE,MAAM;IAAG,IAAI,KAAK;IAAQ;GAAS,CAAC,EAAE,CAAC;EACnF,CAAC,CAAA,CACA,YAAY,CAAC,CAAC;CACnB;CAEA,MAAM,MAAM,UAAU,SAAqB,WAAW,IAAI,GAAG,UAAU;CAiCvE,OAAO,CA7BS,WAAW,QAAQ,SAAS;EAC1C,IAAI,KAAK,MAAM,IAAI,SAAS,GAC1B,WAAW,IAAI;EAEjB,OAAO,CAAC;CACV,CAwBQ,GAtBO,aAAW,eAAe,IAAI,WAAuB;EAClE,IAAI,CAAC,OAAO,YACV;EAIF,MAAM,UAAmC,CAAC;EAC1C,OAAO,QAAQ,mBAAmB,QAAQ,MAAM,OAAO,QAAQ,QAAQ,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC;EACzF,MAAM,gBAAgB,SAAkB,QAAQ,MAAM,CAAC,MAAM,QAAQ,QAAQ,KAAK,MAAM,MAAM,KAAK,IAAI;EAGvG,MAAM,OAAO,OAAO,MAAM,IAAI,SAAS;EACvC,MAAM,UAAU,OAAO,MACpB,MAAM,gBAAgB,CAAA,CACtB,QAAQ,SAAS,CAAC,KAAK,SAAS,aAAa,IAAI,KAAK,aAAa,MAAM,IAAI,CAAC,CAAA,CAC9E,KAAK,SAAS,UAAU,GAAG;GAAE,MAAM,KAAK;GAAM,IAAI,KAAK;EAAG,CAAC,CAAC;EAC/D,IAAI,QAAQ,SAAS,GACnB,OAAO,KAAK,SAAS,EAAE,QAAQ,CAAC;EAElC,IAAI,OAAO,IAAI;CACjB,CAEiB,CAAM;AACzB;;AAGA,IAAa,iBACX,aAAW,MAAM;CACf,WAAW,EACT,cAAc,WAChB;CACA,GAAG,OAAO,YACR,OAAO,QAAQ,OAAO,CAAC,CAAC,KAAK,CAAC,MAAM,SAAS,CAC3C,WAAW,QACX,EACE,cAAc,yBAAyB,IAAI,OAC7C,CACF,CAAC,CACH;AACF,CAAC;AAIH,IAAM,aAAa,aAAW,MAAM,EAClC,iBAAiB,EACf,SAAS,MACX,EACF,CAAC;AAED,IAAM,eAAe,aAAW,MAAM;CACpC,mBAAmB;EACjB,SAAS;EACT,YAAY;EACZ,KAAK;EACL,SAAS;CACX;CACA,kBAAkB;EAChB,OAAO;EACP,QAAQ;EACR,cAAc;EACd,YAAY;CACd;CACA,uBAAuB,EACrB,YAAY,MACd;CACA,yBAAyB,EACvB,SAAS,MACX;AACF,CAAC;;;;ACxTD,IAAa,iBAA+B;CAC1C,QAAQ;CACR,cAAc;CACd,iBAAiB;CACjB,sBAAsB;CACtB,cAAc;EAAE,KAAK;EAAG,KAAK;CAAE;CAC/B,kBAAkB;EAAE,KAAK;EAAG,KAAK;CAAG;CACpC,uBAAuB;EAAE,GAAG,CAAC,IAAI,CAAC;EAAG,GAAG,CAAC,MAAM,IAAI;CAAE;CACrD,oBAAoB;CACpB,gBAAgB;AAClB;;;;AAKA,IAAa,SAAS,UAAiC,mBAA8B;CACnF,IAAI;CACJ,IAAI,OAAO;CAEX,MAAM,YAAY,MAAkB,QAAgB;EAClD,MAAM,EAAE,MAAM,IAAI,GAAG,KAAK,IAAI,MAAM,KAAK,cAAc,GAAG,KAAK,CAAC;EAChE,MAAM,UAAU,SAAS,iBAAiB,GAAG,CAAC;EAE9C,MAAM,EAAE,YAAY,OAAO,GAAG,WAAW,MAAM,MAAM,KAAK,UAAU,iBAAiB,CAAC;EAGtF,OAAO;GAAE;GAAS,OAAA;IAFF,GAAG,IAAI;IAAM,GAAG,IAAI;GAElB;EAAM;CAC1B;CAEA,OAAO,CAEL,aAAW,eAAe,IAAI,WAAW;EAEvC,IAAI,SAAS,SAAS,OAAO,KAAK,WAAW;GAC3C,IAAI,SACF,QAAQ,QAAQ;GAGlB,UAAU,IAAI,QACZ,OAAO,KAAK,WACZ,aACE;IACE,iBAAiB;IACjB,oBAAoB;IACpB,aAAa;KAAC,MAAM,KAAK,OAAO,IAAI;KAAK;KAAG;IAAC;GAC/C,GACA,SACA,cACF,CACF;GACA,QAAQ,WAAW;GACnB,QAAQ,MAAM;EAChB,OACE,QAAQ,OAAO;EAGjB,MAAM,UAAU,OAAO,MAAM,UAAU,KAAK;EAC5C,IAAI,YAAY,MAAM;GACpB,OAAO;GAEP,MAAM,EAAE,SAAS,UAAU,SAAS,OAAO,MAAM,UAAU,CAAC;GAC5D,IAAI,WAAW,OACb,QAAQ,MAAM;IAAE;IAAS;GAAM,CAAC;EAEpC;CACF,CAAC,GAED,SAAO,GAAG,CACR,EACE,MAAM,GAAG,UAAU;EACjB,IAAI,SACE;OAAA,MAAM,QAAQ,WAAW,MAAM,UAAU;IAC3C,QAAQ,MAAM,EAAE,MAAM,GAAI,CAAC;IAC3B,OAAO;GACT;;EAGF,OAAO;CACT,EACF,CACF,CAAC,CACH;AACF;AAMA,IAAM,UAAN,MAAc;CAiBO;CACA;CAjBnB;CAEA;CACA;CAEA,WAAW;CACX;CACA,aAAa;CACb,gBAAgB;CAEhB,aAAyB,CAAC;CAC1B,mBAAmB;CAEnB,aAAa;EAAE,GAAG;EAAG,GAAG;CAAE;CAE1B,YACE,OACA,UACA;EAFiB,KAAA,QAAA;EACA,KAAA,WAAA;EAEjB,KAAK,UAAU,KAAK,SAAS,WAAW,IAAI,IAAI,QAAQ,QAAQ,IAAI,IAAI,QAAQ,QAAQ;CAC1F;CAEA,IAAI,OAAO;EACT,OAAO,KAAK;CACd;CAEA,aAAmB;EAEjB,UAAU,CAAC,KAAK,WAAW,CAAC,KAAK,MAAG,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,CAAA,+BAAA,EAAA;EAAA,CAAC;EAErC,KAAK,UAAU,SAAS,cAAc,QAAQ;EAC9C,KAAK,QAAQ,KAAK;EAClB,KAAK,QAAQ,MAAM,WAAW;EAC9B,KAAK,QAAQ,MAAM,SAAS;EAC5B,KAAK,QAAQ,MAAM,gBAAgB;EAGnC,KAAK,OAAO,KAAK,QAAQ,WAAW,IAAI;EAGxC,CADe,KAAK,MAAM,eAAe,cAAA,EACjC,YAAY,KAAK,OAAO;EAEhC,KAAK,OAAO;CACd;CAEA,UAAgB;EACd,KAAK,KAAK;EAEV,IAAI,KAAK,SAAS;GAChB,KAAK,QAAQ,OAAO;GACpB,KAAK,UAAU,KAAA;GACf,KAAK,OAAO,KAAA;EACd;CACF;CAEA,SAAe;EACb,IAAI,KAAK,MAAM,iBAAiB,KAAK,SAAS;GAC5C,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,aAAa,OAAO,cAAc,WAAW,KAAK,MAAM;GAC7F,KAAK,QAAQ,MAAM,MAAM,GAAG,EAAE;GAC9B,KAAK,QAAQ,MAAM,OAAO,GAAG,EAAE;GAC/B,KAAK,QAAQ,QAAQ;GACrB,KAAK,QAAQ,SAAS;EACxB;CACF;CAEA,QAAc;EAEZ,UAAU,KAAK,WAAW,KAAK,MAAG,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,CAAA,6BAAA,EAAA;EAAA,CAAC;EACnC,KAAK,WAAW;EAChB,KAAK,KAAK;CACZ;CAEA,OAAa;EAEX,KAAK,WAAW;EAChB,KAAK,MAAM,MAAM,YAAY;CAC/B;CAEA,OAAa;EACX,IAAI,CAAC,KAAK,YAAY,CAAC,KAAK,WAAW,CAAC,KAAK,MAC3C;EAGF,KAAK,KAAK,UAAU,GAAG,GAAG,KAAK,QAAQ,SAAS,GAAG,KAAK,QAAQ,UAAU,CAAC;EAG3E,MAAM,uBAAM,IAAI,KAAK,EAAA,CAAE,QAAQ;EAC/B,KAAK,cAAc;EACnB,MAAM,MAAM,MAAM,KAAK,aAAa;EACpC,KAAK,YAAY;EAEjB,IAAI,KAAK,aAAa,GAAG;GACvB,KAAK,cAAc;GACnB,MAAM,YAAa,KAAK,aAAa,KAAK,gBAAiB,KAAK,SAAS;GACzE,MAAM,SAAS,OAAO,CAAC,WAAW,SAAS;GAC3C,MAAM,SAAS,OAAO,CAAC,WAAW,SAAS;GAC3C,KAAK,MAAO,MAAM,YAAY,aAAa,OAAO,KAAK,OAAO;EAChE;EAEA,KAAK,cAAc;EAEnB,sBAAsB,KAAK,KAAK,KAAK,IAAI,CAAC;CAC5C;CAEA,QAAQ,UAAU,EAAE,WAA6B;EAC/C,KAAK,aAAa,KAAK,iBAAiB;EACxC,KAAK,gBAAgB;CACvB,GAAG,GAAG;CAEN,QAAQ,UAAU,EAAE,SAAS,YAAmE;EAC9F,MAAM,QAAQ,iBAAiB,SAAS,KAAK,SAAS,KAAK;EAC3D,MAAM,eAAe,OAAO,KAAK,SAAS,iBAAiB,KAAK,KAAK,SAAS,iBAAiB,GAAG;EAClG,MAAM,MAAM,KAAK,WAAW,MAAM,MAAM,IAAI,IAAI,KAAK,WAAW,IAAI,MAAM,IAAI,IAAI;EAClF,KAAK,aAAa;EAClB,KAAK,IAAI,IAAI,cAAc,MAAY;GACrC,KAAK,WAAW,KAAK,oBAAoB,KAAK,QAAQ,OAAO,MAAM,IAAI,MAAM,IAAI,MAAM,GAAG,KAAK;GAC/F,KAAK,oBAAoB,KAAK,mBAAmB,KAAK,KAAK,SAAS;EACtE;CACF,GAAG,GAAG;CAEN,gBAAsB;EACpB,KAAK,IAAI,IAAI,KAAK,WAAW,QAAQ,MAAY;GAC/C,MAAM,WAAW,KAAK,WAAW;GACjC,IAAI,CAAC,UACH;GAGF,IAAI,SAAS,QAAQ,OAAQ,SAAS,QAAQ,IAC5C;GAGF,KAAK,QAAQ,OAAO,KAAK,MAAO,QAAQ;EAC1C;CACF;AACF;AAmBA,IAAe,SAAf,MAAsB;CACW;CAA/B,YAAY,UAA2C;EAAxB,KAAA,WAAA;CAAyB;AAG1D;AAEA,IAAM,UAAN,cAAsB,OAAO;CAC3B,OAAO,GAAW,GAAW,OAA0B;EACrD,OAAO;GACL;GACA,GAAG,IAAI;GACP,IAAI,OAAO,KAAK,SAAS,sBAAsB,EAAE,IAAI,KAAK,SAAS,sBAAsB,EAAE,EAAE;GAC7F,IAAI,OAAO,KAAK,SAAS,sBAAsB,EAAE,IAAI,KAAK,SAAS,sBAAsB,EAAE,EAAE;GAC7F,MAAM,OAAO,KAAK,SAAS,aAAa,KAAK,KAAK,SAAS,aAAa,GAAG;GAC3E;GACA,OAAO;EACT;CACF;CAEA,OAAO,KAA+B,UAA0B;EAC9D,SAAS,MAAM,KAAK,SAAS;EAC7B,SAAS,KAAK,SAAS;EACvB,SAAS,KAAK,SAAS;EACvB,SAAS,SAAS,KAAK,SAAS;EAEhC,IAAI,YAAY,QAAQ,SAAS,MAAM,GAAG,GAAG,SAAS,MAAM,GAAG,GAAG,SAAS,MAAM,GAAG,GAAG,SAAS,MAAM;EACtG,IAAI,SAAS,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,SAAS,MAAM,SAAS,IAAI;CACnG;AACF;;;;;AAMA,IAAM,UAAN,cAAsB,OAAO;CAC3B,OAAO,GAAW,GAAW,OAA0B;EACrD,OAAO;GACL;GACA,GAAG,IAAI;GACP,IAAI,OAAO,KAAK,SAAS,sBAAsB,EAAE,IAAI,KAAK,SAAS,sBAAsB,EAAE,EAAE;GAC7F,IAAI,OAAO,KAAK,SAAS,sBAAsB,EAAE,IAAI,KAAK,SAAS,sBAAsB,EAAE,EAAE;GAC7F,MAAM,OAAO,KAAK,SAAS,aAAa,KAAK,KAAK,SAAS,aAAa,GAAG;GAC3E;GACA,OAAO;GACP,OAAQ,OAAO,GAAG,GAAG,IAAI,KAAK,KAAM;GACpC,MAAM;GACN,QAAQ;EACV;CACF;CAEA,OAAO,KAA+B,UAA0B;EAC9D,SAAS,MAAM,KAAK,SAAS;EAC7B,SAAS,KAAK,SAAS;EACvB,SAAS,KAAK,SAAS;EACvB,SAAS,MAAM,SAAS;EACxB,SAAS,MAAM,SAAS;EACxB,SAAS,SAAU,OAAO,KAAM,EAAG;EACnC,SAAS,MAAM,KAAK,IAAI,SAAS,KAAM,IAAI;EAC3C,SAAS,MAAM,KAAK,IAAI,SAAS,KAAM,IAAI;EAC3C,SAAS,QAAQ,KAAK,SAAS;EAC/B,SAAS,SAAS,KAAK,SAAS;EAEhC,IAAI,YAAY,QAAQ,SAAS,MAAM,GAAG,GAAG,SAAS,MAAM,GAAG,GAAG,SAAS,MAAM,GAAG,GAAG,SAAS,MAAM;EACtG,IAAI,UAAU;EACd,IAAI,IAAI,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,KAAK,MAAM,SAAS,IAAI,CAAC,GAAG,SAAS,MAAM,GAAG,IAAI,KAAK,EAAE;EAC7F,IAAI,KAAK;CACX;AACF;AAMA,IAAM,oBAAoB,MAAe,UAAoD;CAC3F,IAAI,OAAO,UAAU,YACnB,OAAO,MAAM;CAGf,MAAM,UAAU,iBAAiB,IAAI,CAAC,CAAC;CACvC,IAAI,SAAS;EACX,MAAM,IAAI,QAAQ,MAAM,qBAAqB,CAAC,EAAE,MAAM,CAAC;EACvD,IAAI,GACF,OAAO;CAEX;CAEA,OAAO;EAAC;EAAI;EAAI;CAAE;AACpB;AAEA,IAAM,UAAU,KAAa,QAAgB;CAC3C,IAAI,CAAC,KAAK;EACR,MAAM;EACN,MAAM;CACR;CAEA,OAAO,MAAM,CAAC,EAAE,KAAK,OAAO,KAAK,MAAM,MAAM;AAC/C;;;ACtWA,IAAM,eAAe;CAAC;CAAgB;CAAmB;AAAkC;;;;AAW3F,IAAa,YAAY,EAAE,QAAQ,IAAI,QAAQ,iBAAkC,CAAC,MAAiB;CACjG,IAAI;CACJ,IAAI,QAAQ;CAEZ,OAAO,CACL,SAAO,GAAG,CACR;EAEE,KAAK;EACL,WAAW;GACT,aAAa,KAAK;GAClB,QAAQ;GACR,OAAO;EACT;CACF,GACA;EAGE,KAAK;EACL,MAAM,SAAS;GACb,aAAa,KAAK;GAElB,IAAI,MAAM,WAAW,GACnB,OAAO;GAGT,MAAM,OAAO,MAAM;GACnB,IAAI,SAAS,MAAM,QACjB,QAAQ;GAGV,IAAI,SAAS;GACb,MAAM,UAAU,UAAU,MAAM;IAC9B,QAAQ,iBAAiB;KACvB,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;KACtC,KAAK,SAAS;MACZ,SAAS;OAAE,MAAM;OAAK,QAAQ,KAAK;MAAU;MAC7C,WAAW,EAAE,QAAQ,MAAM,EAAE;KAC/B,CAAC;KAED,IAAI,SAAS,KAAK,QAChB,OAAO,KAAK,OAAO,IAAI,SAAS,KAAK,YAAY,MAAM,IAAI,EAAE;IAEjE,GAAG,OAAO;GACZ;GAEA,OAAO;GACP,OAAO;EACT;CACF,CACF,CAAC,CACH;AACF;;;;ACzDA,IAAM,aAAa;CACjB,aAAa,OAAK;CAClB,cAAc,OAAK;CACnB,QAAQ,OAAK;CACb,QAAQ,OAAK;CAEb,UAAU,OAAK,QAAQ,OAAK,MAAM;CAClC,UAAU,OAAK;CAEf,MAAM,OAAK,WAAW,OAAK,SAAS,OAAK,YAAY,CAAC;CACtD,WAAW,OAAK,SAAS,OAAK,YAAY;CAC1C,MAAM,OAAK;CACX,UAAU,OAAK;CACf,WAAW,OAAK;CAChB,YAAY,OAAK;CACjB,SAAS,OAAK;CACd,WAAW,OAAK;CAChB,YAAY,OAAK;CACjB,OAAO,OAAK;CACZ,OAAO,OAAK;CACZ,SAAS,OAAK;AAChB;AAWA,IAAM,6BAAa,IAAI,IAAI;CAAC;CAAS;CAAO;CAAO;AAAK,CAAC;AAEzD,IAAM,cAAwD;CAC5D,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;AACP;AAEA,IAAM,oBAAoB,QAAsB,UAAsC;CACpF,MAAM,UAAU,CAAC,OAAO,OAAO,IAAI;CACnC,IAAI,MAAM,SACR,OAAO,UAAU;MAEjB,OAAO,OAAO;CAEhB,OAAO;AACT;AAEA,IAAM,SAAS,QAAsB,UAA6C;CAChF,IAAI,MAAM,SACR,OAAO,iBAAiB,QAAQ,KAAK;CAEvC,IAAI,OAAO,SAAS,GAClB,OAAO;CAET,IAAI,OAAO,MAAM,GAAG,KAAK,OAAO,MAAM,IAAI,GAAG;EAC3C,OAAO,UAAU;EACjB,OAAO;CACT;CACA,IAAI,OAAO,MAAM,IAAI,GACnB,OAAO,iBAAiB,QAAQ,KAAK;CAGvC,MAAM,QAAQ,OAAO,KAAK;CAC1B,IAAI,UAAU,QAAO,UAAU,KAAK;EAClC,OAAO,KAAK;EACZ,IAAI,UAAU;EACd,IAAI;EACJ,QAAQ,OAAO,OAAO,KAAK,MAAM,MAAM;GACrC,IAAI,SAAS,SAAS,CAAC,SACrB,OAAO;GAET,UAAU,CAAC,WAAW,SAAS;EACjC;EAEA,OAAO;CACT;CAEA,IAAI,OAAO,MAAM,+BAA+B,GAC9C,OAAO;CAET,MAAM,OAAO,OAAO,MAAM,4BAA4B;CACtD,IAAI,QAAQ,OAAO,SAAS,WAAW;EACrC,MAAM,OAAO,KAAK;EAClB,IAAI,MAAM,KAAK,IAAI,GACjB,OAAO;EAET,IAAI,UAAU,KAAK,IAAI,GACrB,OAAO;EAGT,IAAI,SAAS,SAAS,CAAC,OAAO,MAAM,UAAU,KAAK,GACjD,OAAO;EAGT,IAAI,WAAW,IAAI,IAAI,KAAK,OAAO,MAAM,cAAc,KAAK,GAC1D,OAAO;EAET,IAAI,OAAO,MAAM,UAAU,KAAK,GAAG;GACjC,IAAI,MAAM,MAAM;IACd,MAAM,OAAO;IACb,OAAO;GACT;GACA,OAAO;EACT;EACA,OAAO;CACT;CAEA,IAAI,OAAO,MAAM,mBAAmB,GAClC,OAAO;CAET,IAAI,OAAO,MAAM,IAAI,GAAG;EACtB,MAAM,OAAO;EACb,OAAO;CACT;CACA,MAAM,OAAO,OAAO,KAAK;CACzB,IAAI,SAAS,KACX,MAAM,OAAO;CAEf,QAAQ,OAAO,YAAY,QAAQ,KAAA,MAAc;AACnD;;AAGA,IAAa,kBAAkB,eAAe,OAAqB;CACjE,MAAM;CACN,mBAAmB;EAAE,SAAS;EAAO,MAAM;CAAK;CAChD,YAAY,WAAW,EAAE,GAAG,MAAM;CAClC;CACA;CACA,cAAc;EACZ,eAAe;GAAE,MAAM;GAAK,OAAO;IAAE,MAAM;IAAM,OAAO;GAAK;EAAE;EAC/D,eAAe,EAAE,UAAU;GAAC;GAAK;GAAK;GAAK;EAAG,EAAE;CAClD;AACF,CAAC;;AAGD,IAAa,gBAAiC,IAAI,gBAAgB,eAAe;;;;;;;;ACxIjF,IAAa,QAAQ,EAAE,WAAwB,CAAC,MAAiB;CAC/D,IAAI,aAAyB,gBAAgB;CAC7C,IAAI,QAIF,aAAa,aADI,IADD,IAAI;EAAE,WAAW;EAAO,QAAQ;CAAM,CACrC,CAAA,CAAI,QAAQ,MACH,CAAQ;CAGpC,OAAO,CAAC,OAAa,GAAG,OAAO,UAAU,CAAC;AAC5C;AAEA,IAAM,gBACH,cACA,SAAS;CACR,IAAI;EACF,MAAM,WAAW,KAAK,MAAM,IAAI,SAAS;EAGzC,IADc,SADG,KAAK,MAAM,QACL,CACnB,GACF,OAAO,CAAC;EAGV,OACE,SAAS,QAAQ,KAAK,SAAS;GAC7B,MAAM;GACN,IAAI,SAAS;GACb,UAAU;GACV,SAAS,GAAG,IAAI,gBAAgB,SAAS,GAAG,IAAI;EAClD,EAAE,KAAK,CAAC;CAEZ,SAAS,KAAc;EACrB,OAAO,CACL;GACE,MAAM;GACN,IAAI,KAAK,MAAM,IAAI;GACnB,UAAU;GACV,SAAS,oBAAoB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;EAC9E,CACF;CACF;AACF;;;ACDF,IAAa,oBAAoB,GAAe,MAC9C,EAAE,cAAc,EAAE,aAClB,EAAE,WAAW,EAAE,UACf,EAAE,aAAa,EAAE,YACjB,EAAE,kBAAkB,EAAE,iBACtB,EAAE,SAAS,EAAE,QACb,EAAE,SAAS,EAAE,QACb,EAAE,cAAc,EAAE,aAClB,EAAE,eAAe,EAAE;AAErB,IAAY,SAAL,yBAAA,QAAA;CACL,OAAA,OAAA,YAAA,KAAA;CACA,OAAA,OAAA,cAAA,KAAA;CACA,OAAA,OAAA,mBAAA,KAAA;CACA,OAAA,OAAA,UAAA,KAAA;;AACF,EAAA,CAAA,CAAA;AAEA,IAAY,OAAL,yBAAA,MAAA;CACL,KAAA,KAAA,aAAA,KAAA;CACA,KAAA,KAAA,YAAA,KAAA;CACA,KAAA,KAAA,UAAA,KAAA;;AACF,EAAA,CAAA,CAAA;;;;AAWA,IAAa,oBAAoB,WAA8B,CAAC,MAAiB;CAC/E,OAAO,CACL,SAAO,GAAG,CACR;EACE,KAAK;EACL,KAAK;CACP,GACA;EACE,KAAK;EACL,KAAK;CACP,CACF,CAAC,CACH;AACF;AAMA,IAAa,cAAc,UAAgC;CACzD,QAAQ,EAAE,OAAO,eAAe;EAC9B,MAAM,EACJ,WAAW,EAAE,UACb,QACE;EACJ,MAAM,UAAwB,CAAC;EAC/B,IAAI,YAAY;EAChB,KAAK,MAAM,SAAS,QAAQ;GAC1B,IAAI,WAAW;GACf,WAAW,KAAK,CAAC,CAAC,QAAQ;IACxB,MAAM,MAAM;IACZ,IAAI,MAAM;IACV,QAAQ,SAAS;KACf,IAAI,CAAC,OAAO,OAAO,YAAY,KAAK,IAAI,KAAK,cAAc,KAAK,MAC9D;KAEF,WAAW;KACX,YAAY,KAAK;KACjB,MAAM,YAAY,WAAW,KAAK;KAClC,MAAM,YAAY,cAAc,KAAK,SAAS;KAC9C,MAAM,WAAW,YAAY,CAAC,UAAU,KAAK,KAAK,SAAS,cAAc,IAAI;KAC7E,IAAI,WAAW,KAAK,aAAa,OAC/B;KAEF,IAAI,aAAa,GACf,QAAQ,KAAK;MAAE,MAAM,KAAK;MAAM,QAAQ,IAAI,OAAO,KAAK,IAAI;KAAI,CAAC;UAC5D,IAAI,KAAK,SAAS,oBAAoB,KAAK,SAAS,kBAAkB;MAE3E,MAAM,WAAW,IAAI,OAAO,KAAK,EAAE;MACnC,IAAI,OACF,QAAQ,KAAK;OAAE,MAAM,KAAK;OAAM,QAAQ,IAAI,OAAO,KAAK,IAAI;MAAI,CAAC;MAEnE,QAAQ,KAAK;OAAE,MAAM,SAAS,OAAO;OAAG,IAAI,SAAS;MAAG,CAAC;KAC3D,OAEE,IAAI,UAAU,GACZ,QAAQ,KAAK;MAAE,MAAM,KAAK;MAAM,IAAI,KAAK,IAAI,KAAK,IAAI,KAAK,OAAO,WAAW,CAAC;KAAE,CAAC;UAC5E,IAAI,QAAQ,UACjB,QAAQ,KAAK;MAAE,MAAM,KAAK;MAAM,IAAI,KAAK,QAAQ,WAAW;KAAO,CAAC;UAEpE,QAAQ,KAAK;MAAE,MAAM,KAAK;MAAM,QAAQ,IAAI,OAAO,QAAQ,QAAQ;KAAE,CAAC;IAG5E;GACF,CAAC;GACD,IAAI;GACJ,IAAI,CAAC,YAAY,MAAM,SAAS,QAAQ,KAAK,CAAC,KAAK,MAAM,OAAO,MAAM,IAAI,OAAO,MAAM,IAAI,EAAA,CAAG,IAAI,GAChG,QAAQ,KAAK;IAAE,MAAM,KAAK;IAAM,IAAI,KAAK;IAAI,QAAQ,IAAI,OAAO,KAAK,IAAI;GAAI,CAAC;EAElF;EAEA,IAAI,CAAC,QAAQ,QACX,OAAO;EAGT,MAAM,YAAY,MAAM,QAAQ,OAAO;EACvC,SACE,MAAM,OAAO;GACX,SAAS;GACT,WAAW,MAAM,UAAU,IAAI,WAAW,CAAC;GAC3C,WAAW;GACX,gBAAgB;EAClB,CAAC,CACH;EACA,OAAO;CACT;AACF;AAMA,IAAa,YAAY,MAAc,WAAkC;CACvE,QAAQ,EAAE,OAAO,eAAe;EAC9B,MAAM,SAAS,iBAAiB,IAAI;EACpC,MAAM,UAAU,MAAM,eAAe,UAAU;GAE7C,IAAI,CAAC,UAAU,MAAM,OAAO;IAC1B,MAAM,QAAQ,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,OAAO,CAAC;IAC9D,MAAM,QAAQ,MAAM,QAAQ,MAAM;IAClC,IAAI,SAAS,KAAK,WAAW,KAAK,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG;KACxD,MAAM,SAAS,MAAM,IAAI,YAAY,MAAM,OAAO,GAAG,MAAM,IAAI;KAC/D,IACE,OAAO,MAAM,OAAO,SAAS,QAAQ,OAAO,QAAQ,OAAO,SAAS,KAAK,MAAM,UAC/E,CAAC,GAAG,OAAO,MAAM,OAAO,SAAS,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,MAAM,MAAM,GAAG,KAAK,GAEpF,OAAO;MACL,SAAS,CACP;OAAE,MAAM,MAAM,OAAO,OAAO,SAAS;OAAO,IAAI,MAAM,OAAO;MAAM,GACnE;OAAE,MAAM,MAAM,OAAO;OAAO,IAAI,MAAM,OAAO,QAAQ,OAAO;MAAO,CACrE;MACA,OAAO,kBAAgB,OAAO,MAAM,OAAO,OAAO,MAAM;KAC1D;IAEJ;GACF;GAEA,MAAM,UAAwB,CAAC;GAE/B,MAAM,eAA6B,CAAC;GACpC,IAAI,aAAa;GACjB,IAAI,WAAW;GACf,IAAI,eAAuD;GAC3D,IAAI,aAAqD;GACzD,IAAI,EAAE,MAAM,OAAO;GAInB,WAAW,KAAK,CAAC,CAAC,QAAQ;IACxB;IACA;IACA,QAAQ,SAAS;KACf,MAAM,EAAE,SAAS;KACjB,IAAI,OAAO,OAAO,YAAY,IAAI,KAAK,WAAW,UAAU,aAAa;MAEvE,aAAa,kBAAkB,IAAI;MACnC,WAAW,gBAAgB,MAAM,MAAM,GAAG;MAC1C,eAAe,aAAa;KAC9B,OAAO,IAAI,SAAS,UAAW,SAAS,WAAW,QAAS;MAE1D,IAAI,OAAO,KAAK,QAAQ,KAAK,KAAK,QAAQ,MAAM,KAAK,IACnD,KAAK,KAAK;WACL,IAAI,KAAK,KAAK,MAAM,QAAQ,KAAK,QAAQ,OAAO,KAAK,IAC1D,OAAO,KAAK;KAEhB,OAAO,IAAI,aAAa,IAAI,IAAI,KAAK,QAAQ;MAE3C,IAAI,KAAK,OAAO,QAAQ,KAAK,KAAK,MAAM;OACtC,IAAI,OAAO,MACT,KAAK,KAAK;OAEZ,OAAO,KAAK;MACd;MACA,IAAI,KAAK,OAAO,MAAM,KAAK,KAAK,IAC9B,KAAK,KAAK;KAEd,OAAO,IAAI,OAAO,OAAO,cAAc,IAAI,GAAG;MAE5C,MAAM,WAAW,aAAa;MAC9B,MAAM,OAAO,iBAAiB,QAAQ,CAAC,CAAC;MACxC,MAAM,UAAU,KAAK,OAAO;MAC5B,MAAM,aAAa,KAAK,KAAK;MAG7B,IAAI,aAAa,MAAM;OACrB,IAAI,WAAW,QAAQ,cAAc,MACnC,eACE,CAAC,UAAU,YAAY,YAAY,MAAM,KAAK,MAAM,IAAI,OAAO,IAC3D;QAAE,MAAM,KAAK;QAAM,IAAI;OAAQ,IAC/B;OAER,IAAI,WAAW,MAAM,cAAc,IACjC,aACE,CAAC,UAAU,eAAe,YAAY,IAAI,KAAK,MAAM,GAAG,UAAU,IAC9D;QAAE,MAAM;QAAY,IAAI,KAAK;OAAG,IAChC;MAEV;MAEA,IAAI,aAAa,QAAS,SAAA,KAAwB,QAAS;OACzD,IAAI,KAAK,QAAQ,QAAQ,WAAW,IAAI;QACtC,QAAQ,KAAK;SAAE,MAAM,KAAK;SAAM,IAAI;QAAQ,CAAC;QAC7C,IAAI,aAAa,QAAQ,cAAc,IAErC,aAAa,KAAK;SAChB,MAAM,WAAW,KAAK,IAAI,IAAI,QAAQ,GAAG,MAAM,KAAK,GAAG,QAAQ;SAC/D,QAAQ,iBAAiB,QAAQ;QACnC,CAAC;OAEL;OACA,IAAI,cAAc,QAAQ,KAAK,MAAM,IAAI;QACvC,QAAQ,KAAK;SAAE,MAAM;SAAY,IAAI,KAAK;QAAG,CAAC;QAC9C,IAAI,aAAa,QAAQ,WAAW,MAElC,QAAQ,KAAK;SACX,MAAM,WAAW,KAAK,IAAI,MAAM,UAAU,GAAG,MAAM,KAAK,IAAI,UAAU;SACtE,QAAQ,iBAAiB,QAAQ;QACnC,CAAC;OAEL;MACF;KACF;IACF;IACA,QAAQ,SAAS;KACf,IAAI,OAAO,OAAO,YAAY,KAAK,IAAI,KAAK,WAAW,KAAK,UAAU,aAAa;MAEjF,MAAM,aAAa,KAAK,IAAI,MAAM,UAAU;MAC5C,MAAM,WAAW,KAAK,IAAI,IAAI,QAAQ;MACtC,IAAI,QAAQ;OACV,IAAI,CAAC,cACH,QAAQ,KAAK;QAAE,MAAM;QAAY,QAAQ;OAAO,CAAC;OAEnD,IAAI,CAAC,YACH,QAAQ,KAAK;QAAE,MAAM;QAAU,QAAQ;OAAO,CAAC;MAEnD,OAAO;OACL,IAAI,OAAO,iBAAiB,UAC1B,QAAQ,KAAK,YAAY;YACpB,IAAI,cACT,QAAQ,KAAK;QAAE,MAAM,WAAW,YAAY,MAAM,KAAK,IAAI,UAAU;QAAG,QAAQ;OAAO,CAAC;OAE1F,IAAI,OAAO,eAAe,UACxB,QAAQ,KAAK,UAAU;YAClB,IAAI,YACT,QAAQ,KAAK;QAAE,MAAM,WAAW,UAAU,MAAM,KAAK,GAAG,QAAQ;QAAG,QAAQ;OAAO,CAAC;MAEvF;KACF;IACF;GACF,CAAC;GAED,IAAI,aAAa,KAAK,MAAM,SAAS,UAAU,CAAC,KAAK,KAAK,MAAM,IAAI,OAAO,MAAM,IAAI,CAAC,CAAC,IAAI,GACzF,OAAO;IACL,SAAS;KAAE,MAAM,MAAM;KAAM,QAAQ,SAAS;IAAO;IACrD,OAAO,kBAAgB,OAAO,MAAM,OAAO,OAAO,MAAM;GAC1D;GAGF,MAAM,YAAY,MAAM,QAAQ,QAAQ,OAAO,YAAY,CAAC;GAC5D,OAAO;IACL,SAAS;IACT,OACE,MAAM,SAAS,CAAC,UAAU,QACtB,kBAAgB,OAAO,MAAM,OAAO,OAAO,MAAM,IACjD,kBAAgB,MAAM,UAAU,OAAO,MAAM,MAAM,CAAC,GAAG,UAAU,OAAO,MAAM,IAAI,EAAE,CAAC;GAC7F;EACF,CAAC;EAED,SACE,MAAM,OAAO,SAAS;GACpB,WAAW,SAAS,qBAAqB;GACzC,gBAAgB;EAClB,CAAC,CACH;EAEA,OAAO;CACT;AACF;AAEA,IAAa,YAAY,UAAgC,SAAS,OAAO,IAAI;AAE7E,IAAa,eAAe,UAAgC,SAAS,OAAO,KAAK;AAEjF,IAAa,eAAe,UAAgC;CAC1D,QAAQ,QAAQ;EACd,MAAM,OAAO,cAAc,IAAI,KAAK;EACpC,OAAO,SACL,OACA,UAAA,IACI,CAAC,KAAK,SACN,UAAA,IACE,CAAC,KAAK,WACN,UAAA,IACE,CAAC,KAAK,gBACN,CAAC,KAAK,IAChB,CAAC,CAAC,GAAG;CACP;AACF;AAEA,IAAa,eAAe,YAAA,CAAyB;AACrD,IAAa,iBAAiB,YAAA,CAA2B;AACzD,IAAa,sBAAsB,YAAA,CAAgC;AACnE,IAAa,mBAAmB,YAAA,CAAuB;AAEvD,IAAM,oBAAoB,SACxB,SAAA,IAAyB,OAAO,SAAA,IAAgC,OAAO,SAAA,IAA2B,MAAM;AAM1G,IAAM,qBAAqB,SAAwB;CACjD,MAAM,MAAM,kBAAkB,KAAK,KAAK,IAAI;CAC5C,IAAI,KACF,OAAO,KAAK,IAAI,KAAK,IAAI,KAAK,OAAO,CAAC,IAAI,KAAK,CAAC;CAElD,OAAO,KAAK;AACd;AAEA,IAAM,mBAAmB,MAAqB,QAAc;CAC1D,MAAM,SAAS,qBAAqB,KAAK,KAAK,IAAI;CAClD,MAAM,WAAW,IAAI,OAAO,KAAK,EAAE;CACnC,IAAI,UAAU,WAAW,KAAK,SAAS,IAAI,GACzC,OAAO,SAAS,OAAO;CAEzB,OAAO,KAAK;AACd;AAEA,IAAM,cAAc,KAAa,KAAW,KAAa,UAAmB;CAC1E,MAAM,OAAO,IAAI,OAAO,GAAG;CAC3B,OAAO,QAAQ,SAAS,KAAK,KAAK,MAAM,KAAK,QAAQ,MAAM,IAAI,IAAI,QAAQ,KACzE,OAAO;CAET,OAAO;AACT;AAEA,IAAM,eAAe,KAAa,MAAkB,KAAa,UAAmB;CAClF,SAAS;EACP,MAAM,OAAO,KAAK,QAAQ,KAAK,GAAG;EAClC,IAAI,CAAC,aAAa,IAAI,KAAK,IAAI,GAC7B,OAAO;EAET,MAAM,SAAS,MAAM,IAAI,KAAK,OAAO,KAAK;EAC1C,IAAI,SAAS,SAAS,MAAM,IAAI,SAAS,QAAQ,SAAS,QACxD,OAAO;EAET,MAAM;CACR;AACF;AAGA,IAAM,aAAW;CACf,WAAW,QAAQ;EAAC;EAAU;EAAI;CAAK,CAAC,CAAC,KAAK,IAAI,CAAC;CACnD,OAAO,QACL,IAAI;;;;;;QAMA,IACN;AACF;;;;;;AAWA,IAAa,eAAe,MAAkB,OAAgB;CAC5D,MAAM,EACJ,WAAW,EAAE,QACb,QACE,KAAK;CACT,MAAM,EAAE,WAAW,IAAI,OAAO,MAAM,KAAK,MAAM;CAC/C,MAAM,EAAE,SAAS,IAAI,KAAK,MAAM;CAEhC,WAAS,MAAM,MAAM,MAAM,MAAM,IAAI;AACvC;AAOA,IAAM,mBAAmB,MAAc,IAAY,SAAuB,UAAuB;CAC/F,WAAW,KAAK,CAAC,CAAC,QAAQ;EACxB;EACA;EACA,QAAQ,SAAS;GACf,IAAI,KAAK,SAAS,UAAU,KAAK,OAAO,MAAM,KAAK,KAAK,MAAM;IAC5D,KAAK,KAAK,OAAO,CAAC,CAAC,SAAS,SAAS;KACnC,MAAM,EAAE,SAAS;KACjB,IAAI,SAAS,cAAc,SAAS,aAClC,QAAQ,KAAK;MAAE,MAAM,KAAK;MAAM,IAAI,KAAK;KAAG,CAAC;UACxC,IAAI,SAAS,eAAe,SAAS,OAC1C,QAAQ,KAAK;MAAE,MAAM,WAAW,KAAK,MAAM,MAAM,KAAK,EAAE;MAAG,IAAI,WAAW,KAAK,IAAI,MAAM,KAAK,CAAC;KAAE,CAAC;IAEtG,CAAC;IACD,OAAO;GACT;EACF;CACF,CAAC;AACH;AAGA,IAAa,cAA4B,EAAE,OAAO,eAAe;CAC/D,MAAM,UAAwB,CAAC;CAC/B,KAAK,MAAM,EAAE,MAAM,QAAQ,MAAM,UAAU,QACzC,gBAAgB,MAAM,IAAI,SAAS,KAAK;CAE1C,IAAI,CAAC,QAAQ,QACX,OAAO;CAET,SAAS,MAAM,OAAO;EAAE;EAAS,WAAW;EAAsB,gBAAgB;CAAK,CAAC,CAAC;CACzF,OAAO;AACT;AAGA,IAAa,WAAW,EAAE,KAAK,UAA6C,CAAC,MAAoB;CAC/F,QAAQ,EAAE,OAAO,eAAe;EAC9B,MAAM,UAAU,MAAM,eAAe,UAAU;GAC7C,IAAI,EAAE,MAAM,OAAO;GACnB,MAAM,YAA0B,CAAC;GACjC,IAAI,OAAuB;GAE3B,WAAW,KAAK,CAAC,CAAC,QAAQ;IACxB;IACA;IACA,QAAQ,SAAS;KACf,IAAI,OAAO,OAAO,YAAY,KAAK,IAAI,GAErC,OACE,WAAW,KAAK,UAAU,eAC1B,QAAQ,kBAAkB,IAAI,KAC9B,MAAM,gBAAgB,MAAM,MAAM,GAAG;UAClC,IAAI,OAAO,OAAO,cAAc,KAAK,IAAI,GAAG;MAGjD,MAAM,QAAQ,KAAK;MACnB,IAAI,KAAK,OAAO,QAAQ,KAAK,MAAM,IAAI;OACrC,IAAI,MAAM,WAAY,OAAO,MAC3B,OAAO,KAAK;YAEZ,UAAU,KAAK,KAAK;MAExB,OAAO,IAAI,KAAK,QAAQ,QAAQ,KAAK,KAAK,IAAI;OAC5C,IAAI,MAAM,UAAW,SAAS,IAC5B,KAAK,KAAK;YAEV,UAAU,KAAK,KAAK;MAExB;KACF;IACF;GACF,CAAC;GAED,IAAI,SAAS,MAAM;IAEjB,MAAM,OAAO,MAAM,IAAI,OAAO,IAAI;IAClC,OAAO,MAAM,KAAK,MAAM,CAAC,KAAK,KAAK,KAAK,KAAK,MAAM,OAAO,KAAK,IAAI,CAAC;GACtE;GACA,IAAI,CAAC,MACH,OAAO,EAAE,MAAM;GAGjB,MAAM,UAAwB,CAAC;GAE/B,MAAM,eAA6B,CAAC;GAEpC,gBAAgB,MAAM,IAAI,cAAc,KAAK;GAC7C,IAAI,eAAe;GAEnB,KAAK,MAAM,SAAS,WAAW;IAC7B,MAAM,OAAO,aAAa,MAAM;IAChC,MAAM,OAAO,iBAAiB,IAAI;IAClC,IAAI,MAAM,OAAO,MAAM;KAErB,QAAQ,KAAK;MAAE,MAAM,WAAW,MAAM,MAAM,KAAK,EAAE;MAAG,QAAQ;KAAK,CAAC;KACpE,aAAa,KAAK;MAAE,MAAM,WAAW,MAAM,MAAM,KAAK,GAAG,EAAE;MAAG,QAAQ;KAAK,CAAC;IAC9E,OAAO;KACL,QAAQ,KAAK;MAAE,MAAM,WAAW,IAAI,MAAM,KAAK,IAAI,IAAI;MAAG,QAAQ;KAAK,CAAC;KACxE,MAAM,QAAQ,WAAW,IAAI,MAAM,KAAK,CAAC;KACzC,IAAI,UAAU,IACZ,gBAAgB,KAAK;KAEvB,aAAa,KAAK;MAAE,MAAM;MAAO,QAAQ;KAAK,CAAC;IACjD;GACF;GAGA,QAAQ,KAAK;IAAE;IAAM,QAAQ,QAAQ,OAAO;GAAI,GAAG;IAAE,MAAM;IAAI,QAAQ,KAAK,OAAO,GAAG;GAAG,CAAC;GAC1F,MAAM,YAAY,MAAM,QAAQ,QAAQ,OAAO,YAAY,CAAC;GAE5D,OAAO;IACL,SAAS;IACT,OAAO,kBAAgB,OAAO,UAAU,OAAO,IAAI,CAAC,IAAI,gBAAgB,MAAM,IAAI,SAAS,IAAI,EAAE;GACnG;EACF,CAAC;EAED,IAAI,QAAQ,QAAQ,OAClB,OAAO;EAGT,SACE,MAAM,OAAO,SAAS;GACpB,WAAW;GACX,gBAAgB;EAClB,CAAC,CACH;EACA,OAAO;CACT;AACF;AAMA,IAAa,WAAW,SAA6B;CACnD,QAAQ,EAAE,OAAO,eAAe;EAC9B,IAAI,YAAY;EAChB,IAAI,UAAU;EACd,IAAI,QAAQ;EACZ,IAAI,eAA8B;EAGlC,MAAM,SAA+E,CAAC;EACtF,KAAK,MAAM,EAAE,MAAM,QAAQ,MAAM,UAAU,QACzC,WAAW,KAAK,CAAC,CAAC,QAAQ;GACxB;GACA;GACA,QAAQ,SAAS;IACf,IAAK,OAAO,OAAO,YAAY,KAAK,IAAI,KAAK,KAAK,SAAS,eAAgB,KAAK,SAAS,SAAS;KAChG,IAAI,OAAO;MAGT,IAAI,SAAS,KAAK,KAAK;MACvB,OAAO,UAAU,QAAQ,KAAK,OAAO,IAAI,GACvC,SAAS,OAAO;MAElB,IAAI,QAAQ,UAAU,SAAA,IAAwB,gBAAgB,eAAe;OAC3E,MAAM,OAAO,OAAO;OACpB,MAAM,WAAW,MAAM,IAAI,OAAO,KAAK,IAAI;OAC3C,MAAM,WAAW,SAAS,KAAK,MAAM,KAAK,OAAO,SAAS,IAAI;OAC9D,eAAe,KAAK,OAAO,SAAS,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAE,EAAE,CAAC;OACrE,IAAI,SAAA,GAAuB;QACzB,MAAM,OAAO,gBAAgB,KAAK,QAAQ;QAC1C,IAAI,MAAM;SACR,gBAAgB,KAAK,EAAE,CAAC;SACxB,UAAU,CAAC,KAAK,KAAK;QACvB;OACF;MACF;MACA,QAAQ;KACV;KACA,IAAI,KAAK,SAAS,WAChB;KAGF,YAAY,KAAK;KACjB,OAAO,KAAK;MAAE,MAAM,KAAK;MAAM;MAAS;KAAa,CAAC;KACtD;KACA,OAAO;IACT;GACF;GACA,QAAQ,SAAS;IAEf,IAAI,KAAK,SAAS,gBAAgB,KAAK,SAAS,iBAAiB,KAAK,SAAS,cAAc;KAC3F,UAAU;KACV,eAAe;IACjB;GACF;EACF,CAAC;EAGH,IAAI,CAAC,OAAO,QAAQ;GAElB,MAAM,EAAE,MAAM,OAAO,MAAM,IAAI,OAAO,MAAM,UAAU,KAAK,MAAM;GACjE,IAAI,SAAS,IAAI;IACf,MAAM,SAAS,SAAA,IAAuB,OAAO,SAAA,IAAwB,QAAQ;IAC7E,SACE,MAAM,OAAO;KACX,SAAS,CACP;MACE;MACA;KACF,CACF;KACA,WAAW,EAAE,QAAQ,OAAO,OAAO,OAAO;KAC1C,WAAW;KACX,gBAAgB;IAClB,CAAC,CACH;IACA,OAAO;GACT;GAEA,OAAO;EACT;EAEA,MAAM,UAA0B,CAAC;EACjC,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;GACtC,MAAM,EAAE,MAAM,SAAS,iBAAiB,OAAO;GAC/C,MAAM,WAAW,KAAK,SAAS,cAAc,KAAK,OAAO,IAAI,KAAK;GAElE,IAAI,UAAU,WAAW,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,YAAY,WAAW,GAAG,QAAQ,CAAC,IAAI,IAAI;GAE9F,IAAI,SAAA,GACF,WAAW,OAAO,OAAO,CAAC,CAAC;GAE7B,IAAI,OAAO,MAAM,IAAI,OAAO,QAAQ;GACpC,MAAM,SAAS,WAAW,KAAK;GAE/B,IAAI,iBAAiB,QAAQ,eAAe,QAC1C,UAAU,KAAK,IAAI,SAAS,eAAe,MAAM;GAGnD,IAAI;GACJ,IAAI,SAAA,GAAuB;IAEzB,IAAI,MAAM;IACV,KAAK,IAAI,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;KAC1C,IAAI,OAAO,EAAE,CAAC,YAAY,MAAM,GAC9B;KAEF;IACF;IACA,MAAM,MAAM,OAAO,OAAO;IAC1B,UAAU,KAAK,IAAI,OAAO,GAAG,CAAC,CAAC,QAAQ,OAAO;IAC9C,OAAO,IAAI,OAAO,KAAK,IAAI,GAAG,UAAU,IAAI,MAAM,CAAC,IAAI,MAAM;GAC/D,OACE,OAAO,IAAI,OAAO,OAAO,IAAI,QAAQ,SAAA,IAAqB,SAAS;GAGrE,QAAQ,KAAK;IAAE,MAAM;IAAU,QAAQ;GAAK,CAAC;GAE7C,OAAO,KAAK,KAAK,KAAK,IAAI;IACxB,OAAO,MAAM,IAAI,OAAO,KAAK,KAAK,CAAC;IACnC,MAAM,OAAO,UAAU,KAAK,KAAK,IAAI,CAAC,CAAE,EAAE,CAAC;IAC3C,QAAQ,KAAK;KAAE,MAAM,KAAK,OAAO,KAAK,IAAI,MAAM,MAAM;KAAG,QAAQ,IAAI,OAAO,KAAK,MAAM;IAAE,CAAC;GAC5F;EACF;EAIA,IAAI,SAAA,GAAuB;GACzB,MAAM,OAAO,OAAO,OAAO,SAAS;GACpC,IAAI,OAAO,KAAK,KAAK;GACrB,OAAO,QAAQ,QAAQ,KAAK,KAAK,IAAI,GACnC,OAAO,KAAK;GAEd,IAAI,MAAM,SAAS,eACjB,kBAAkB,KAAK,YAAY,KAAK,UAAU,GAAG,SAAS,MAAM,GAAG;EAE3E;EACA,MAAM,YAAY,MAAM,QAAQ,OAAO;EACvC,SACE,MAAM,OAAO;GACX,SAAS;GACT,WAAW,MAAM,UAAU,IAAI,WAAW,CAAC;GAC3C,WAAW;GACX,gBAAgB;EAClB,CAAC,CACH;EACA,OAAO;CACT;AACF;;;;;;AAmBA,IAAM,mBAAmB,OAAoB,MAAY,aAAoD;CAC3G,IAAI,YAAY;CAChB,MAAM,QAAkB,CAAC;CACzB,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,EAAE,MAAM,QAAQ,MAAM,UAAU,QACzC,WAAW,KAAK,CAAC,CAAC,QAAQ;EACxB;EACA;EACA,QAAQ,SAAS;GACf,MAAM,EAAE,SAAS;GACjB,IAAI,SAAS,gBAAgB,SAAS,iBAAiB,SAAS,cAAc;IAE5E,MAAM,KAAK,IAAI;IACf,SAAS,KAAK,CAAC;GACjB;EACF;EACA,QAAQ,SAAS;GACf,MAAM,EAAE,SAAS;GACjB,IAAI,SAAS,gBAAgB,SAAS,iBAAiB,SAAS,cAAc;IAC5E,MAAM,IAAI;IACV,SAAS,IAAI;GACf,OAAO,IAAI,SAAS,cAAc,KAAK,SAAS,WAAW;IACzD,MAAM,UAAU,MAAM,MAAM,SAAS;IACrC,MAAM,SAAS,KAAK,KAAK,SAAS,MAAM,KAAK;IAK7C,IAAI,EAHF,SAAA,IACI,YAAY,gBACZ,YAAY,gBAAiB,SAAA,MAAwB,SAEzD;IAEF,YAAY,KAAK;IACjB,MAAM,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI;IACvC,MAAM,OAAO,qCAAqC,KAAK,KAAK,KAAK,MAAM,KAAK,OAAO,KAAK,IAAI,CAAC;IAC7F,IAAI,CAAC,MACH;IAEF,SAAS,SAAS,SAAS,EAAE;IAC7B,SAAS;KACP,MAAM,KAAK;KACX;KACA,YAAY,KAAK,EAAE,CAAC;KACpB,SAAS,SAAS,SAAS,SAAS;KACpC,YAAY,KAAK,MAAM;IACzB,CAAC;GACH;EACF;CACF,CAAC;AAEL;;;;;;AASA,IAAM,yBAAyB,YAA4B,YAA4C;CACrG,MAAM,OAAuB,CAAC;CAC9B,MAAM,YAAY,GAAiB,MAAoB,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE;CACxG,KAAK,MAAM,aAAa,YACtB,IAAI,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC,MAAM,WAAW,SAAS,WAAW,MAAM,CAAC,GACrE,KAAK,KAAK,SAAS;CAGvB,OAAO;AACT;AAEA,IAAa,cAAc,SAA6B;CACtD,QAAQ,EAAE,OAAO,eAAe;EAC9B,MAAM,UAA0B,CAAC;EACjC,MAAM,YAA4B,CAAC;EACnC,gBAAgB,OAAO,OAAO,EAAE,MAAM,MAAM,WAAW,YAAY,iBAAiB;GAClF,MAAM,SAAS,KAAK,OAAO,UAAU;GAErC,QAAQ,KAAK;IAAE,MAAM,KAAK;IAAM,IAAI,KAAK,OAAO;GAAW,CAAC;GAE5D,IAAI,OAAO;GACX,OAAO,KAAK,KAAK,KAAK,IAAI;IACxB,OAAO,MAAM,IAAI,OAAO,KAAK,KAAK,CAAC;IACnC,MAAM,OAAO,UAAU,KAAK,KAAK,IAAI,CAAC,CAAE,EAAE,CAAC;IAC3C,IAAI,OAAO,QACT,QAAQ,KAAK;KAAE,MAAM,KAAK,OAAO;KAAQ,IAAI,KAAK,OAAO,KAAK,IAAI,SAAS,YAAY,IAAI;IAAE,CAAC;GAElG;GACA,IAAI,YACF,kBAAkB,KAAK,aAAa,GAAG,WAAW,MAAM,GAAG;EAE/D,CAAC;EACD,IAAI,CAAC,QAAQ,QACX,OAAO;EAGT,SACE,MAAM,OAAO;GACX,SAAS,CAAC,GAAG,SAAS,GAAG,sBAAsB,WAAW,OAAO,CAAC;GAClE,WAAW;GACX,gBAAgB;EAClB,CAAC,CACH;EACA,OAAO;CACT;AACF;AAEA,IAAa,cAAc,SAA6B;CACtD,QAAQ,WAAW;EACjB,MAAM,aAAa,cAAc,OAAO,KAAK;EAG7C,IADE,WAAW,eAAe,SAAA,IAAuB,WAAW,SAAA,IAAwB,YAAY,SAEhG,OAAO,WAAW,IAAI,CAAC,CAAC,MAAM;EAEhC,IAAI,WAAW,WAGb,OAAO,YADL,WAAW,cAAc,WAAA,IAAyB,WAAW,cAAc,YAAA,IAAA,GACjD,IAAI,CAAC,CAAC,MAAM;EAE1C,OAAO,QAAQ,IAAI,CAAC,CAAC,MAAM;CAC7B;AACF;;;;;;AAOA,IAAa,eAAe,MAAY,OAA2B;CACjE,QAAQ,EAAE,OAAO,eAAe;EAC9B,MAAM,UAA0B,CAAC;EACjC,MAAM,YAA4B,CAAC;EACnC,gBAAgB,OAAO,OAAO,EAAE,MAAM,MAAM,WAAW,YAAY,SAAS,iBAAiB;GAC3F,MAAM,UAAU,OAAA,IAAsB,GAAG,QAAQ,MAAM,OAAA,IAAmB,WAAW;GACrF,QAAQ,KAAK;IAAE,MAAM,KAAK;IAAM,IAAI,KAAK,OAAO;IAAY,QAAQ;GAAQ,CAAC;GAE7E,MAAM,QAAQ,QAAQ,SAAS;GAC/B,MAAM,SAAS,KAAK,OAAO,UAAU;GACrC,IAAI,OAAO;GACX,OAAO,KAAK,KAAK,KAAK,IAAI;IACxB,OAAO,MAAM,IAAI,OAAO,KAAK,KAAK,CAAC;IACnC,MAAM,OAAO,UAAU,KAAK,KAAK,IAAI,CAAC,CAAE,EAAE,CAAC;IAC3C,IAAI,QAAQ,QACV;IAEF,IAAI,QAAQ,GACV,QAAQ,KAAK;KAAE,MAAM,KAAK,OAAO;KAAQ,QAAQ,IAAI,OAAO,KAAK;IAAE,CAAC;SAC/D,IAAI,QAAQ,GACjB,QAAQ,KAAK;KAAE,MAAM,KAAK,OAAO;KAAQ,IAAI,KAAK,OAAO,KAAK,IAAI,SAAS,OAAO,IAAI;IAAE,CAAC;GAE7F;GACA,IAAI,SAAA,KAAyB,YAC3B,kBAAkB,KAAK,aAAa,GAAG,WAAW,MAAM,GAAG;EAE/D,CAAC;EAED,IAAI,CAAC,QAAQ,QACX,OAAO;EAGT,MAAM,YAAY,MAAM,QAAQ,CAAC,GAAG,SAAS,GAAG,sBAAsB,WAAW,OAAO,CAAC,CAAC;EAC1F,SACE,MAAM,OAAO;GACX,SAAS;GACT,WAAW,MAAM,UAAU,IAAI,WAAW,CAAC;GAC3C,WAAW;GACX,gBAAgB;EAClB,CAAC,CACH;EACA,OAAO;CACT;AACF;AAEA,IAAM,qBAAqB,MAAyB,SAAiB,SAAyB,QAAc;CAC1G,OAAO,MAAM,OAAO,KAAK,aACvB,IAAI,KAAK,SAAS,YAAY;EAC5B,MAAM,SAAS,iBAAiB,KAAK,IAAI,YAAY,KAAK,MAAM,KAAK,OAAO,EAAE,CAAC;EAC/E,IAAI,CAAC,UAAU,CAAC,OAAO,OAAO,SAC5B;EAEF,MAAM,OAAO,OAAO,EAAE,CAAC,SAAS,OAAO,EAAE,CAAC;EAC1C,MAAM,SAAS,OAAO,OAAO;EAC7B,QAAQ,KAAK;GAAE,MAAM,KAAK,OAAO,KAAK,IAAI,GAAG,OAAO,OAAO,MAAM;GAAG,IAAI,KAAK,OAAO;GAAM,QAAQ;EAAO,CAAC;EAC1G;CACF;AAEJ;AAMA,IAAa,iBAAiB,WAAkC;CAC9D,QAAQ,EAAE,OAAO,eAAe;EAC9B,MAAM,QAAgB,CAAC;EACvB,IAAI,YAAY;EAChB,KAAK,MAAM,EAAE,MAAM,QAAQ,MAAM,UAAU,QAAQ;GACjD,IAAI,WAAW;GACf,WAAW,KAAK,CAAC,CAAC,QAAQ;IACxB;IACA;IACA,QAAQ,SAAS;KACf,IAAI,OAAO,OAAO,YAAY,KAAK,IAAI,KAAK,KAAK,SAAS,SAAS;MACjE,IAAI,KAAK,SAAS,WAChB,OAAO;MAET,WAAW;MACX,YAAY,KAAK;MACjB,IAAI,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI;MACrC,IAAI,KAAK,SAAS,GAAG;OACnB,MAAM,WAAW,MAAM,IAAI,KAAK,KAAK,SAAS,CAAC;OAC/C,IAAI,WAAW,KAAK,SAAS,IAAI,GAC3B;YAAA,CAAC,UAAW,MAAM,UAAU,MAAM,MAAM,SAAS,EAAE,CAAC,WAAW,SAAS,SAAS,GACnF,MAAM,KAAK,QAAQ;OAAA;MAGzB;MACA,SAAS;OACP,MAAM,KAAK,IAAI;OACf,IAAI,KAAK,MAAM,KAAK,IAClB;OAEF,OAAO,MAAM,IAAI,KAAK,KAAK,SAAS,CAAC;MACvC;MACA,IAAI,CAAC,UAAU,KAAK,SAAS,MAAM,IAAI,OAAO;OAC5C,MAAM,WAAW,MAAM,IAAI,KAAK,KAAK,SAAS,CAAC;OAC/C,IAAI,WAAW,KAAK,SAAS,IAAI,GAC/B,MAAM,KAAK,QAAQ;MAEvB;MACA,OAAO;KACT;IACF;GACF,CAAC;GACD,IAAI;GACJ,IAAI,CAAC,YAAY,UAAU,SAAS,MAAM,CAAC,KAAK,MAAM,OAAO,MAAM,IAAI,OAAO,IAAI,EAAA,CAAG,IAAI,GACvF,MAAM,KAAK,IAAI;EAEnB;EAEA,MAAM,UAAwB,CAAC;EAC/B,KAAK,MAAM,QAAQ,OACjB,IAAI,QACF,QAAQ,KAAK;GAAE,MAAM,KAAK;GAAM,QAAQ,KAAK,KAAK,KAAK,IAAI,IAAI,OAAO;EAAI,CAAC;OACtE;GACL,MAAM,QAAQ,+BAA+B,KAAK,KAAK,IAAI;GAC3D,IAAI,OACF,QAAQ,KAAK;IAAE,MAAM,KAAK,OAAO,MAAM,EAAE,CAAC;IAAQ,IAAI,KAAK,OAAO,MAAM,EAAE,CAAC;GAAO,CAAC;EAEvF;EAEF,IAAI,CAAC,QAAQ,QACX,OAAO;EAGT,MAAM,YAAY,MAAM,QAAQ,OAAO;EACvC,SACE,MAAM,OAAO;GACX,SAAS;GACT,WAAW,MAAM,UAAU,IAAI,WAAW,CAAC;GAC3C,WAAW,SAAS,0BAA0B;GAC9C,gBAAgB;EAClB,CAAC,CACH;EACA,OAAO;CACT;AACF;AAEA,IAAa,gBAAgB,cAAc,IAAI;AAE/C,IAAa,mBAAmB,cAAc,KAAK;AAEnD,IAAa,oBAAkC,WAAW;CACxD,QAAQ,cAAc,OAAO,KAAK,CAAC,CAAC,aAAa,mBAAmB,cAAA,CAAe,MAAM;AAC3F;AAMA,IAAa,gBAA8B,WAAW;CACpD,MAAM,EAAE,OAAO,aAAa;CAC5B,MAAM,EAAE,cAAc;CAGtB,IAAI,UAAU,OAAO,WAAW,KAAK,UAAU,KAAK,OAAO;EACzD,MAAM,EAAE,SAAS,UAAU;EAC3B,MAAM,OAAO,MAAM,IAAI,OAAO,IAAI;EAClC,IAAI,CAAC,KAAK,KAAK,KAAK,IAAI,KAAK,SAAS,KAAK,MAAM;GAC/C,WAAS,UAAU,QAAQ,MAAM,KAAK,MAAM,KAAK,EAAE;GACnD,OAAO;EACT;CACF;CAGA,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,EAAE,MAAM,QAAQ,UAAU,QAAQ;EAC3C,IAAI,YAAY;EAChB,IAAI,UAAU;EACd,WAAW,KAAK,CAAC,CAAC,QAAQ;GACxB;GACA;GACA,QAAQ,SAAS;IACf,IAAI,OAAO,OAAO,YAAY,KAAK,IAAI,GAAG;KACxC,IAAI,QAAQ,KAAK,QAAQ,MAAM,KAAK,IAAI;MAEtC,YAAY,KAAK;MACjB,UAAU,KAAK;KACjB,OAAO;MAEL,YAAY,KAAK,IAAI,WAAW,MAAM,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,IAAI;MAChE,UAAU,KAAK,IAAI,SAAS,MAAM,IAAI,OAAO,KAAK,EAAE,CAAC,CAAC,EAAE;KAC1D;IACF;GACF;EACF,CAAC;EACD,IAAI,OAAO,UAAU,OAAO,OAAO,SAAS,EAAE,CAAC,MAAM,YAAY,GAC/D,OAAO,OAAO,SAAS,EAAE,CAAC,KAAK;OAE/B,OAAO,KAAK;GAAE,MAAM;GAAW,IAAI;EAAQ,CAAC;CAEhD;CACA,IAAI,CAAC,OAAO,QACV,OAAO;CAGT,MAAM,UAAwB,OAAO,KAAK,EAAE,MAAM,SAAS;EACzD,MAAM,SAAS,OAAO,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC;EAC7C,OAAO,CACL;GAAE;GAAM,QAAQ,UAAU,IAAI,OAAO,MAAM;EAAE,GAC7C;GAAE,MAAM;GAAI,QAAQ,OAAO,IAAI,OAAO,MAAM,IAAI;EAAM,CACxD;CACF,CAAC;CAED,SACE,MAAM,OAAO;EACX;EACA,WAAW;EACX,gBAAgB;CAClB,CAAC,CACH;CAEA,OAAO;AACT;AAEA,IAAa,mBAAiC,EAAE,OAAO,eAAe;CACpE,IAAI,YAAY;CAGhB,MAAM,UAAwB,CAAC;CAC/B,KAAK,MAAM,EAAE,MAAM,QAAQ,MAAM,UAAU,QACzC,WAAW,KAAK,CAAC,CAAC,QAAQ;EACxB;EACA;EACA,QAAQ,SAAS;GACf,IAAI,WAAW,KAAK,UAAU,eAAe,cAAc,KAAK,MAAM;IACpE,YAAY,KAAK;IACjB,MAAM,YAAY,MAAM,IAAI,OAAO,KAAK,IAAI;IAC5C,IAAI,KAAK,SAAS,cAAc;KAC9B,QAAQ,KAAK;MAAE,MAAM,KAAK;MAAM,IAAI,UAAU,KAAK,IAAI,KAAK,OAAO,UAAU;KAAK,CAAC;KACnF,MAAM,WAAW,MAAM,IAAI,OAAO,KAAK,EAAE;KACzC,IAAI,8BAA8B,KAAK,SAAS,IAAI,GAClD,QAAQ,KAAK;MACX,MAAM,SAAS,QAAQ,SAAS,WAAW,UAAU,SAAS,IAAI,IAAI;MACtE,IAAI,SAAS;KACf,CAAC;IAEL,OAAO;KAEL,MAAM,SAAS,KAAK,OAAO,UAAU;KACrC,KAAK,IAAI,OAAO,YAAa,OAAO,MAAM,IAAI,KAAK,KAAK,SAAS,CAAC,GAAG;MACnE,QAAQ,KAAK;OAAE,MAAM,KAAK,OAAO,SAAS;OAAG,IAAI,KAAK,OAAO;MAAO,CAAC;MACrE,IAAI,KAAK,MAAM,KAAK,IAClB;KAEJ;IACF;GACF;EACF;CACF,CAAC;CAEH,IAAI,CAAC,QAAQ,QACX,OAAO;CAGT,SAAS,MAAM,OAAO;EAAE;EAAS,WAAW;EAA2B,gBAAgB;CAAK,CAAC,CAAC;CAC9F,OAAO;AACT;AAEA,IAAa,mBAAiC,WAAW;CACvD,QAAQ,cAAc,OAAO,KAAK,CAAC,CAAC,cAAc,cAAc,kBAAkB,aAAA,CAAc,MAAM;AACxG;AAEA,IAAM,eAA2C;CAC/C,UAAA;CACA,gBAAA;CACA,YAAA;CACA,eAAA;AACF;AAEA,IAAM,+BAAe,IAAI,IAAI;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,IAAM,aAAuE;CAC3E,aAAa;CACb,aAAa;CACb,aAAa;CACb,aAAa;CACb,aAAa;CACb,aAAa;CACb,WAAW;CACX,YAAY;CACZ,WAAW;CACX,gBAAgB;CAChB,gBAAgB;CAChB,WAAW;CACX,MAAM;AACR;;;;AAKA,IAAa,iBAAiB,UAAmC;CAG/D,IAAI,YAA6C;CAGjD,MAAM,SAA6B;EAAC;EAAM;EAAM;EAAM;CAAI;CAC1D,IAAI,OAAgB;CACpB,IAAI,aAA6B;CAEjC,IAAI,YAAoD;CAGxD,MAAM,QAAsE,CAAC;CAI7E,IAAI,eAAuE;CAE3E,MAAM,iBAAiB,SAAiB;EACtC,IAAI,CAAC,cACH;EAEF,OAAO,KAAK,IAAI,MAAM,aAAa,GAAG;EACtC,IAAI,QAAQ,aAAa,KACvB;EAEF,KAAK,IAAI,IAAI,GAAG,IAAI,aAAa,OAAO,QAAQ,KAC9C,IAAI,OAAO,OAAO,OAChB;OACK,IAAI,aAAa,OAAO,IAC7B,OAAO,KAAK;OACP,IAAI,KAAK,KAAK,MAAM,IAAI,YAAY,aAAa,KAAK,IAAI,CAAC,GAChE,OAAO,KAAK;EAGhB,aAAa,MAAM;CACrB;CAGA,MAAM,cAAc,SAAiB;EACnC,IAAI,gBAAgB,OAAO,aAAa,KACtC,aAAa,MAAM,KAAK,IAAI,MAAM,aAAa,GAAG;CAEtD;CAEA,MAAM,EAAE,cAAc;CACtB,KAAK,MAAM,SAAS,UAAU,QAAQ;EACpC,IAAI,MAAM,SAAS,OAAO,MAAM,MAAM,MAAM,IAAI,GAAG;GAEjD,MAAM,cAAc,KAAK,IAAI,MAAM,MAAM,CAAC;GAC1C,MAAM,gBAAgB,MAAM,IAAI,YAAY,MAAM,OAAO,aAAa,MAAM,IAAI;GAChF,IAAI,eAAe,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,OAAO,WAAW;GAC7E,KAAK,IAAI,IAAI,GAAG,IAAI,aAAa,KAAK;IACpC,MAAM,KAAK,aAAa;IACxB,IAAI,OAAO,cAAc,cAAc,SAAS,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,GAAG;KAC3E,eAAe,aAAa,MAAM,GAAG,CAAC;KACtC;IACF;GACF;GACA,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;IACtC,MAAM,OAAO,iBAAiB,CAAC;IAC/B,MAAM,QAAQ,aAAa,QAAQ,IAAI;IACvC,IAAI,QAAQ,IAAI;KACd,eAAe,aAAa,MAAM,GAAG,KAAK,IAAI,aAAa,MAAM,QAAQ,KAAK,MAAM;KACpF,IAAI,OAAO,OAAO,MAChB,OAAO,KAAK;IAEhB;GACF;EACF;EAEA,WAAW,KAAK,CAAC,CAAC,QAAQ;GACxB,MAAM,MAAM;GACZ,IAAI,MAAM;GACV,QAAQ,SAAS;IACf,cAAc,KAAK,IAAI;IACvB,MAAM,EAAE,SAAS;IACjB,IAAI,SAAS,gBAAgB,SAAS,iBAAiB,SAAS,cAE9D,MAAM,KAAK,IAAI;SACV,IAAI,SAAS,QAClB,OAAO;SACF,IAAI,OAAO,OAAO,YAAY,IAAI,MAAM,MAAM,SAAS,KAAK,KAAK,MAAM,QAAQ,KAAK,OAAO,MAAM,KAAK;KAC3G,IAAI,SAAS,UAAU,MAAM,MAAM,SAAS,OAAO,cACjD,MAAM,MAAM,SAAS,KAAK;KAE5B,MAAM,YAAY,WAAW;KAC7B,IAAI,cAAc,MAChB,YAAY;UACP,IAAI,cAAc,WACvB,YAAY;KAEd,IAAI,cAAc,eAAe,OAAO,MAAM,MAAM,MAAM,KAAK,GAE7D,eAAe;MACb,KAAK,KAAK,IAAI,MAAM,MAAM,KAAK,IAAI;MACnC,KAAK,KAAK,IAAI,MAAM,IAAI,KAAK,EAAE;MAC/B,QAAQ;OAAC;OAAO;OAAO;OAAO;MAAK;KACrC;IAEJ,OAAO,IAAI,OAAO,OAAO,cAAc,IAAI,KAAK,cAAc;KAC5D,MAAM,QAAQ,aAAa;KAE3B,IAAI,MAAM,SAAS,OAAO,WAAW,MACnC,OAAO,SAAS;KAElB,aAAa,OAAO,SAAS;IAC/B,OAAO,IAAI,aAAa,IAAI,IAAI,GAC9B,WAAW,KAAK,EAAE;GAEtB;GACA,QAAQ,SAAS;IACf,cAAc,KAAK,EAAE;IACrB,MAAM,EAAE,SAAS;IACjB,IAAI,SAAS,gBAAgB,SAAS,iBAAiB,SAAS,cAE9D,MAAM,IAAI;SACL,IAAI,OAAO,OAAO,YAAY,IAAI,GAAG;KAG1C,IAAI,UAA2C;KAC/C,IAAI,WAAW;KACf,KAAK,IAAI,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KACrC,IAAI,MAAM,OAAO,cACf,WAAW;UACN,IAAI,CAAC,SACV,UAAU,MAAM,OAAO,aAAa,SAAS,MAAM,OAAO,eAAe,WAAW;KAGxF,IAAI,eAAe,MACjB,aAAa;UACR,IAAI,CAAC,YAAY,YACtB,aAAa;KAEf,IAAI,cAAc,MAChB,YAAY;UACP,IAAI,cAAc,SACvB,YAAY;KAId,eAAe;IACjB,OAAO,IAAI,OAAO,OAAO,cAAc,IAAI,KAAK,cAE9C,aAAa,OAAO,aAAa,SAAS;GAE9C;EACF,CAAC;CACH;CAEA,MAAM,EAAE,MAAM,OAAO,MAAM,IAAI,OAAO,UAAU,KAAK,MAAM;CAG3D,OAAO;EACL,WAHgB,SAAS;EAIzB,WAAW,aAAa;EACxB,YAAY,cAAc;EAC1B,MAAM,OAAA,MAAuB;EAC7B,UAAU,OAAA,MAA2B;EACrC,QAAQ,OAAA,MAAyB;EACjC,eAAe,OAAA,MAAgC;EAC/C;EACA,WAAW,aAAa;CAC1B;AACF;;;;;;AAOA,IAAa,sBAAsB,eAA4C,QAAQ,QAAmB;CACxG,OAAO,aAAW,eAAe,GAC/B,qBAAqB,WAAuB;EAC1C,IAAI,OAAO,cAAc,OAAO,cAC9B,cAAc,cAAc,OAAO,KAAK,CAAC;CAE7C,GAAG,KAAK,CACV;AACF;;;ACvyCA,IAAa,wBAA8C,MAAM,EAAE,MAAM,WAAW;CAClF,IAAI,YAAY;CAChB,QAAQ,MAAR;EACE,KAAK;GACH,WAAW,SAAS,IAAI,CAAC,CAAC,CAAC,IAAI;GAC/B;EAEF,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;GACH,aACE,SAAS,WACL,OAAO,SACP,SAAS,aACP,OAAO,WACP,SAAS,kBACP,OAAO,gBACP,OAAO;GACjB,CAAC,OAAO,SAAS,YAAY,SAAS,YAAY,IAAI,IAAI,YAAY,UAAU,EAAA,CAAG,IAAI;GACvF;EAEF,KAAK;EACL,KAAK;EACL,KAAK;GACH,WAAW,SAAS,iBAAiB,KAAK,UAAU,SAAS,gBAAgB,KAAK,SAAS,KAAK;GAChG,CAAC,SAAS,QAAQ,WAAW,QAAQ,IAAI,SAAS,OAAO,QAAQ,QAAQ,IAAI,WAAW,QAAQ,EAAA,CAAG,IAAI;GACvG;EAEF,KAAK;GACH,CAAC,SAAS,QAAQ,mBAAmB,SAAS,OAAO,gBAAgB,iBAAA,CAAkB,IAAI;GAC3F;EACF,KAAK;GACH,CAAC,SAAS,QAAQ,kBAAkB,aAAA,CAAc,IAAI;GACtD;EACF,KAAK;GACH,YAAY,IAAI;GAChB;EAEF,KAAK;GACH,CAAC,SAAS,QAAQ,aAAa,QAAQ,EAAA,CAAG,IAAI;GAC9C;EAEF,KAAK;GACH,QAAQ;IAAE,KAAK;IAAM,OAAO;GAAK,CAAC,CAAC,CAAC,IAAI;GACxC;EAEF,KAAK,WACH,cAAc,IAAI;CAEtB;CAEA,4BAA4B;EAC1B,IAAI,CAAC,KAAK,UACR,KAAK,MAAM;CAEf,CAAC;AACH;;;;;;;;;;;;;;;ACjGA,IAAa,yBAAyB,WAAoC,CAAC,MACzE,eAAe,OAAO;CAEpB;EACE,KAAK;GAAC,YAAY;GAAa,cAAc;GAAa,cAAc;GAAS,cAAc;EAAW;EAC1G,OAAO;CACT;CAGA;EAAE,KAAK,CAAC,cAAc,MAAM,cAAc,QAAQ;EAAG,OAAO;CAAiB;CAE7E;EACE,KAAK;GAAC,cAAc;GAAU,cAAc;GAAc,cAAc;GAAQ,cAAc;EAAM;EACpG,OAAO;CACT;CACA;EAAE,KAAK,CAAC,cAAc,WAAW;EAAG,OAAO;CAAgB;AAC7D,CAAC;;;;;AAMH,IAAa,2BAA2B,UAAmC,CAAC,MAAmB,CAC7F,QAAgB,GAChB,mBAAmB,sBAAsB,OAAO,CAAC,CACnD;;;;;;;;AC9BA,IAAM,uBAAuB,IAAI,YAAY;;AAG7C,IAAM,+BAAe,IAAI,IAAgB;;AAGzC,IAAI;;AAGJ,IAAI;AAEJ,IAAM,4BAA2C;CAC/C,IAAI,oBACF,OAAO,QAAQ,QAAQ;CAEzB,YAAA,QAAA,QAAA,CAAA,CAAA,WAAA,iBAAA,CAAA,CACG,MAAM,EAAE,4BAA4B;EACnC,qBAAqB,mBAAmB,sBAAsB,CAAC;EAC/D,KAAK,MAAM,QAAQ,cACjB,KAAK,SAAS,EAAE,SAAS,qBAAqB,YAAY,kBAAkB,EAAE,CAAC;EAEjF,aAAa,MAAM;CACrB,CAAC,CAAA,CACA,OAAO,QAAQ;EAEd,UAAU,KAAA;EACV,MAAM;CACR,CAAC;CAEH,OAAO;AACT;;AAGA,IAAa,6BAA6B,oBAAoB,GAAG;CAC/D,MAAM;CACN,OAAO,CAAC,SAAS;CACjB,YAAY,CAAC,KAAK;CAClB,MAAM,YAAY;EAChB,MAAM,CAAC,EAAE,aAAa,MAAM,QAAQ,IAAI,CAAC,OAAO,4BAA4B,oBAAoB,CAAC,CAAC;EAClG,OAAO,QAAQ;CACjB;AACF,CAAC;;AAGD,IAAa,4BAAuC,CAClD,qBAAqB,GAAG,sBAAsB,CAAC,CAAC,GAChD,WAAW,QAAQ,SAAS;CAC1B,IAAI,CAAC,oBACH,aAAa,IAAI,IAAI;CAEvB,OAAO,EACL,eAAe,aAAa,OAAO,IAAI,EACzC;AACF,CAAC,CACH;;;;;;;;;;;;;;AC3CA,IAAa,0BAA0B,WAA6B,CAAC,MAAM;CACzE,OAAO,eAAe,OACpB;EACE;GACE,KAAK;IACH,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK,SAAS,OAAK,IAAI;IACvB,OAAK,SAAS,OAAK,IAAI;IACvB,OAAK,WAAW,OAAK,IAAI;IACzB,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK,QAAQ,OAAK,MAAM;IACxB,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK,QAAQ,OAAK,YAAY;IAC9B,OAAK;IACL,OAAK;IACL,OAAK;IACL,OAAK;GACP;GAEA,OAAO;EACT;EAGA;GACE,KAAK;IACH,OAAK;IACL,OAAK;IACL,aAAa;IACb,aAAa;IACb,aAAa;GACf;GACA,OAAO,cAAc;EACvB;EAGA;GACE,KAAK;IAEH,aAAa;IACb,aAAa;IACb,aAAa;IACb,aAAa;GACf;GACA,OAAO,cAAc;EACvB;EAGA;GACE,KAAK,CAEH,OAAK,SAAS,OAAK,YAAY,GAC/B,OAAK,SACP;GACA,OAAO,cAAc;EACvB;EAGA;GACE,KAAK,CAAC,OAAK,WAAW,OAAK,OAAO;GAClC,OAAO;EACT;EAMA;GACE,KAAK,OAAK;GACV,OAAO;GACP,YAAY;EACd;EACA;GAAE,KAAK,OAAK;GAAU,GAAG,cAAc,QAAQ,CAAC;EAAE;EAClD;GAAE,KAAK,OAAK;GAAU,GAAG,cAAc,QAAQ,CAAC;EAAE;EAClD;GAAE,KAAK,OAAK;GAAU,GAAG,cAAc,QAAQ,CAAC;EAAE;EAClD;GAAE,KAAK,OAAK;GAAU,GAAG,cAAc,QAAQ,CAAC;EAAE;EAClD;GAAE,KAAK,OAAK;GAAU,GAAG,cAAc,QAAQ,CAAC;EAAE;EAClD;GAAE,KAAK,OAAK;GAAU,GAAG,cAAc,QAAQ,CAAC;EAAE;EAGlD;GAAE,KAAK,OAAK;GAAU,OAAO;EAAS;EACtC;GAAE,KAAK,OAAK;GAAQ,OAAO;EAAY;EACvC;GAAE,KAAK,OAAK;GAAe,OAAO;EAAe;EAOjD;GACE,KAAK,CAAC,aAAa,UAAU,aAAa,UAAU;GACpD,OAAO,cAAc;EACvB;EAEA;GACE,KAAK,CAAC,aAAa,SAAS;GAC5B,OAAO;EACT;CACF,GACA;EACE,OAAO;EACP,KAAK,EACH,YAAY,SACd;CACF,CACF;AACF;;;;;;AAOA,IAAa,eAAe;CAC1B,YAAY,IAAI,OAAO;CACvB,UAAU,IAAI,OAAO;CACrB,UAAU,IAAI,OAAO;CACrB,cAAc,IAAI,OAAO;CACzB,YAAY,IAAI,OAAO;CACvB,YAAY,IAAI,OAAO;CACvB,WAAW,IAAI,OAAO;CACtB,eAAe,IAAI,OAAO;CAC1B,UAAU,IAAI,OAAO;CACrB,WAAW,IAAI,OAAO;CACtB,KAAK,IAAI,OAAO;CAGhB,WAAW,IAAI,OAAO;AACxB;AAIA,MAAM,aAAa,SAAS,SAAS;CACnC,IAAI,OAAO,SAAS,YAAY,SAAS,QAAQ,KAAK,SAAS,aAC7D,KAAK,QAAQ,aAAa;AAE9B,CAAC;;;;;AAMD,IAAa,yBAA2C,CACtD,OACA,EACE,OAAO,CAAC,UAAU,YAAY,CAAC,EACjC,CACF;;;;;;;;;;;AC5JA,IAAa,4BAA4B,UAAiC,CAAC,MAAmB;CAC5F,OAAO;EACL,KAAK,KAAK,SAAO,GAAG,CAAC;GAAE,KAAK;GAAS,KAAK,mCAAmC,EAAE,eAAe,MAAM,CAAC;EAAE,CAAC,CAAC,CAAC;EAK1G,SAAS;GAMP,MAAM;GAMN,qBAAqB;GACrB,eAAe;IAAC,GAAI,QAAQ,iBAAiB,CAAC;IAAI;IAA4B,GAAG;GAAS;GAG1F,kBAAkB;GAGlB,YAAY;IAEV;IACA,GAAI,QAAQ,cAAc,kBAAkB;IAI5C;GACF;EACF,CAAC;EAID,oBAAoB;EACpB,mBAAmB,uBAAuB,CAAC;EAE3C,SAAO,GACL;GAEE,QAAQ,kBAAkB,SAAS;GAGnC,GAAG;GAGH,GAAG;EACL,CAAC,CAAC,OAAO,QAAQ,CACnB;CACF;AACF;;;;;;;;AASA,IAAM,sBAAsC,EAC1C,OAAO,CACL,aAAa,IAAI,EACf,kBAAkB,KACpB,CAAC,CACH,EACF;;;;;AAMA,IAAa,0BAA4C,CAAC,iBAAiB,MAAM;;;;AAKjF,IAAM,kBAAkC,EACtC,QAAQ,CAAC,eAAe,EAC1B;;;;AAKA,IAAM,SAAyB,CAE/B;;;;;;AClHA,IAAa,aAAa,OACxB,WAAW,OAAO;CAChB,SAAS,UAAU,GAAG,kBAAkB,KAAK,CAAC;CAC9C,SAAS,OAAO,OAAO,GAAG,kBAAkB,GAAG,KAAK,CAAC;AACvD,CAAC;AAUH,IAAa,qBAAqB,UAAkC;CAClE,MAAM,cAAc,WAAkC;EACpD,MAAM,OAAkB;GACtB,MAAM,OAAO,KAAK;GAClB,MAAM,OAAO;GACb,IAAI,OAAO;GACX,MAAM,MAAM,IAAI,MAAM,OAAO,MAAM,OAAO,EAAE,CAAC,CAAC,SAAS;GACvD,UAAU,CAAC;EACb;EAEA,IAAI,OAAO,WAAW,GAAG;GACvB;IACE,KAAK,SAAS,KAAK,WAAW,MAAM,CAAC;UAC9B,OAAO,YAAY;GAC5B,OAAO,OAAO;EAChB;EAEA,OAAO;CACT;CAEA,OAAO,WAAW,WAAW,KAAK,CAAC,CAAC,OAAO,CAAC;AAC9C;;;;ACRA,IAAa,mBAAmB,YAAY,OAAiD;;AAG7F,IAAa,oBAAoB,YAAY,OAAe;;AAG5D,IAAa,oBAAoB,YAAY,OAAe;;AAG5D,IAAa,kBAAkB,YAAY,OAAe;;AAG1D,IAAa,oBAAoB,YAAY,OAAa;AAE1D,IAAM,WAAW,QAAgB,iBAA4C;CAC3E;CACA,OAAO;CACP,SAAS;CACT;AACF;;AAGA,IAAa,mBAAmB,WAAW,OAAgC;CACzE,cAAc;CACd,SAAS,OAAO,OAAO;EAErB,IAAI,OAAO,SAAS,GAAG,aAAa;GAAE,GAAG;GAAO,QAAQ,GAAG,QAAQ,OAAO,MAAM,MAAM;EAAE,IAAI;EAC5F,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,gBAAgB,GAC5B,OAAO,QAAQ,OAAO,MAAM,QAAQ,OAAO,MAAM,WAAW;OACvD,IAAI,OAAO,GAAG,iBAAiB,GAAG;GACvC,MAAM,OAAO,QAAQ,QAAQ,GAAG,MAAM,UAAU,KAAK,IAAI;GACzD,OAAO;IAAE,GAAG;IAAM,OAAO,KAAK,QAAQ,OAAO;IAAO,SAAS;IAAI,aAAa,KAAA;GAAU;EAC1F,OAAO,IAAI,OAAO,GAAG,iBAAiB,GAEpC,OAAO;GAAE,GADI,QAAQ,QAAQ,GAAG,MAAM,UAAU,KAAK,IAAI;GACvC,SAAS,OAAO;EAAM;OACnC,IAAI,OAAO,GAAG,eAAe,GAG9B;OAAA,MACF,OAAO;IAAE,GAAG;IAAM,OAAO,OAAO;IAAO,aAAa,KAAA;GAAU;EAAA,OAE3D,IAAI,OAAO,GAAG,iBAAiB,GACpC,OAAO;EAIX,OAAO;CACT;AACF,CAAC;AAED,IAAM,cAAc,UAClB,MAAM,MAAM,SAAS,KAAK,MAAM,QAAQ,SAAS,MAAM,MAAM,aAAa,UAAU,KAAK;;;;;;AAW3F,IAAa,iBAA0B,SAAS;CAC9C,MAAM,QAAQ,KAAK,MAAM,MAAM,kBAAkB,KAAK;CACtD,IAAI,CAAC,SAAS,MAAM,MAAM,WAAW,GACnC,OAAO;CAGT,MAAM,QAAQ,MAAM,UAAU,KAAK,MAAM,IAAI;CAC7C,MAAM,SAAS,MAAM,SAAS,QAAQ,SAAS;CAC/C,KAAK,SAAS;EAEZ,SAAS;GAAE,MAAM,MAAM;GAAQ;EAAO;EACtC,WAAW,EAAE,QAAQ,MAAM,SAAS,MAAM,MAAM,SAAS,EAAE;EAC3D,SAAS,kBAAkB,GAAG;CAChC,CAAC;CACD,OAAO;AACT;;AAGA,IAAa,iBAA0B,SAAS;CAC9C,IAAI,CAAC,KAAK,MAAM,MAAM,kBAAkB,KAAK,GAC3C,OAAO;CAGT,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,EAAE,CAAC;CACjD,OAAO;AACT;AAIA,IAAM,0BAAmC,SAAS;CAChD,MAAM,QAAQ,KAAK,MAAM,MAAM,kBAAkB,KAAK;CACtD,IAAI,CAAC,OACH,OAAO;CAGT,OAAO,MAAM,MAAM,SAAS,IAAI,cAAc,IAAI,IAAI;AACxD;AAQA,IAAM,WAAsB;AAC5B,IAAM,kBAA6B;AAEnC,IAAM,oBAAN,cAAgC,WAAW;CACZ;CAA7B,YAAY,QAA2C;EACrD,MAAM;EADqB,KAAA,SAAA;CAE7B;CAEA,GAAY,OAAmC;EAC7C,OACE,MAAM,OAAO,UAAU,KAAK,OAAO,SACnC,MAAM,OAAO,YAAY,KAAK,OAAO,WACrC,MAAM,OAAO,gBAAgB,KAAK,OAAO;CAE7C;CAEA,MAAe,MAA+B;EAC5C,MAAM,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,WAAW,iBAAiB;EAC3D,IAAI,KAAK,OAAO,MAAM,SAAS,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;GAClE,MAAM,SAAS,OAAO,GAAG,MAAM,CAAC,CAAC,WAAW,gBAAgB,CAAC,CAAC,WAAW,EAAE,YAAY,SAAS,CAAC;GACjG,IAAI,KAAK,OAAO,MAAM,SAAS,GAC7B,OAAO,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,KAAK,OAAO,KAAK,CAAC;GAEzD,IAAI,KAAK,OAAO,QAAQ,SAAS,GAC/B,OAAO,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,WAAW,yBAAyB,CAAC,CAAC,KAAK,KAAK,OAAO,OAAO,CAAC;GAEjG,KAAK,OAAO,MAAM;EACpB,OAAO,IAAI,KAAK,OAAO,aACrB,KAAK,OACH,WAAW,KAAK,OAAO,aAAa;GAClC,KAAK;GACL,WAAW;GACX,MAAM;GACN,gBAAgB;EAClB,CAAC,CACH;EAGF,KAAK,OACH,cAAc,CACZ;GACE,MAAM;GACN,OAAO;GACP,WAAW;GACX,QAAQ;GACR,eAAe,cAAc,IAAI;EACnC,GACA;GACE,MAAM;GACN,OAAO;GACP,WAAW;GACX,QAAQ;GACR,eAAe,cAAc,IAAI;EACnC,CACF,CAAC,CACH;EACA,OAAO,KAAK;CACd;CAEA,cAAgC;EAC9B,OAAO;CACT;AACF;AAEA,IAAM,qBAAqB,aAAW,YAAY,QAAQ,CAAC,gBAAgB,IAAI,UAAU;CACvF,MAAM,QAAQ,MAAM,MAAM,gBAAgB;CAC1C,IAAI,CAAC,SAAS,CAAC,WAAW,KAAK,GAC7B,OAAO,WAAW;CAGpB,OAAO,WAAW,IAAI,CAAC,WAAW,OAAO;EAAE,QAAQ,IAAI,kBAAkB,KAAK;EAAG,MAAM;CAAE,CAAC,CAAC,CAAC,MAAM,MAAM,MAAM,CAAC,CAAC;AAClH,CAAC;;;;;;;;;;;;;AAcD,IAAM,oBAAoB,aAAW,kBAAkB,QAAQ,CAAC,gBAAgB,IAAI,UAAU;CAC5F,MAAM,QAAQ,MAAM,MAAM,gBAAgB;CAC1C,MAAM,aAAqC,CAAC;CAC5C,IAAI,SAAS,WAAW,KAAK,GAC3B,WAAW,uBAAuB;CAEpC,OAAO;AACT,CAAC;AAOD,IAAM,eAAe,WAAW,UAC9B,MAAM;;CAEJ,aAAa;CAEb,OAAO,QAA0B;EAG/B,MAAM,YAAY,OAAO,WAAW,MAAM,kBAAkB,KAAK,KAAK;EACtE,MAAM,SAAS,OAAO,MAAM,MAAM,gBAAgB,KAAK;EACvD,IAAI,WAAW,WACb;EAGF,qBAAqB;GACnB,IAAI,KAAK,YACP;GAEF,IAAK,OAAO,KAAK,MAAM,MAAM,gBAAgB,KAAK,SAAU,QAC1D,OAAO,KAAK,SAAS,EAAE,SAAS,QAAQ,GAAG,MAAM,EAAE,CAAC;EAExD,CAAC;CACH;CAEA,UAAgB;EACd,KAAK,aAAa;CACpB;AACF,CACF;;;;;;;;;AAcA,IAAa,oBAA+B;CAC1C;CACA,KAAK;CACL;CACA;CACA;CACA,YAAY;CACZ;CACA,SAAO,GAAG,CACR;EAAE,KAAK;EAAS,KAAK;CAAuB,GAC5C;EAAE,KAAK;EAAU,KAAK;CAAc,CACtC,CAAC;AACH;AAEA,IAAM,WAAS,aAAW,MAAM;CAE9B,kDAAkD,EAChD,SAAS,OACX;CAEA,oBAAoB;EAClB,SAAS;EACT,YAAY;EACZ,KAAK;CACP;CAEA,4BAA4B,EAC1B,SAAS,GACX;CAEA,8BAA8B,EAC5B,YAAY,SACd;AACF,CAAC;;;;AC5RD,IAAa,yBAAyB,UAAuC;CAC3E,QAAQ,EAAE,SAAS,KAAK,MAAM,UAAU,KAAK,MAAM,gBAAgB,CAAC,MAClE,KAAK,SAAS,EAAE,SAAS,iBAAiB,GAAG;EAAE;EAAQ;CAAY,CAAC,EAAE,CAAC;CACzE,UAAU,SAAS,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,IAAI,EAAE,CAAC;CACxE,cAAc,SAAS,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,IAAI,EAAE,CAAC;CAC5E,eAAe,SAAS,KAAK,SAAS,EAAE,SAAS,gBAAgB,GAAG,IAAI,EAAE,CAAC;CAC3E,cAAc,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,EAAE,CAAC;AACjE;;AAUA,IAAa,yBACX,aAC8D;CAC9D,IAAI,QAA6B;EAAE,QAAQ;EAAO,OAAO;EAAI,SAAS;EAAI,aAAa,KAAA;CAAU;CACjG,MAAM,OAAO,SAA8B;EACzC,QAAQ;EACR,WAAW,KAAK;CAClB;CAEA,OAAO;EACL,QAAQ,EAAE,gBAAgB,CAAC,MAAM,IAAI;GAAE,QAAQ;GAAM,OAAO;GAAI,SAAS;GAAI;EAAY,CAAC;EAC1F,UAAU,SAAS,IAAI;GAAE,GAAG;GAAO,SAAS;EAAK,CAAC;EAClD,cAAc,SAAS,IAAI;GAAE,GAAG;GAAO,OAAO,MAAM,QAAQ;GAAM,SAAS;GAAI,aAAa,KAAA;EAAU,CAAC;EACvG,eAAe,SAAS,IAAI;GAAE,GAAG;GAAO,OAAO;GAAM,aAAa,KAAA;EAAU,CAAC;EAC7E,cAAc,IAAI;GAAE,QAAQ;GAAO,OAAO;GAAI,SAAS;GAAI,aAAa,KAAA;EAAU,CAAC;EACnF,gBAAgB;CAClB;AACF;AAYA,IAAM,mBAA8B;CAClC,aAAa,UAAU,OAAO,WAAW,UAAU,EAAE;CACrD,eAAe,WAAW,aAAa,MAAuC;AAChF;AAiBA,IAAM,aAAW;CACf,iBAAiB;CACjB,MAAM;CACN,gBAAgB;CAChB,uBAAuB;AACzB;;;;;;AAOA,IAAa,sBAAb,MAAiC;CAC/B;CACA;CAEA;CAEA,WAAW;CACX,aAAa;CACb,SAAS;CACT,SAAmB,CAAC;CACpB,gBAAyB;CACzB,cAAuB;CACvB,cAAuB;;CAEvB,eAAqC;CACrC,YAAY;CAEZ,YAAY,MAAuB,UAAsC,CAAC,GAAG;EAC3E,KAAK,QAAQ;EACb,KAAK,WAAW;GAAE,GAAG;GAAU,GAAG;EAAQ;EAC1C,KAAK,aAAa,QAAQ,aAAa;CACzC;;CAGA,MAAM,UAAqD,CAAC,GAAS;EACnE,KAAK,WAAW;EAChB,KAAK,SAAS;EACd,KAAK,SAAS,CAAC;EACf,KAAK,MAAM,MAAM,OAAO;EACxB,IAAI,KAAK,SAAS,kBAAkB,GAAG;GACrC,KAAK,aAAa;GAClB,KAAK,gBAAgB,KAAK,WAAW,iBAAiB;IACpD,KAAK,aAAa;IAClB,KAAK,gBAAgB;IACrB,KAAK,OAAO;GACd,GAAG,KAAK,SAAS,eAAe;EAClC;CACF;;CAGA,KAAK,MAAoB;EACvB,IAAI,CAAC,KAAK,UACR,KAAK,MAAM;EAEb,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,QAAQ,WAAW,GACrB;EAEF,KAAK,OAAO,KAAK,GAAI,KAAK,SAAS,SAAS,SAAS,QAAQ,MAAM,KAAK,IAAI,CAAC,OAAO,CAAE;EACtF,KAAK,OAAO;CACd;;CAGA,YAAY,MAAoB;EAC9B,IAAI,CAAC,KAAK,UACR,KAAK,MAAM;EAEb,IAAI,CAAC,KAAK,YACR,KAAK,MAAM,QAAQ,IAAI;CAE3B;;;;;CAMA,MAAM,QAAuB;EAC3B,KAAK,OAAO,KAAK,aAAa;EAC9B,KAAK,gBAAgB;EACrB,KAAK,aAAa;EAClB,KAAK,OAAO,KAAK,WAAW;EAC5B,KAAK,cAAc;EACnB,OAAO,KAAK,OAAO,SAAS,GAC1B,KAAK,aAAa,KAAK,OAAO,MAAM,CAAE;EAExC,KAAK,MAAM,QAAQ,EAAE;EACrB,KAAK,OAAO,KAAK,WAAW;EAC5B,KAAK,cAAc;EACnB,IAAI,KAAK,SAAS,eAAe,KAAK,OAAO,SAAS,GAAG;GAGvD,IAAI,KAAK,cACP,MAAM,KAAK;GAEb,KAAK,eAAe,KAAK,gBAAgB;GACzC,MAAM,KAAK;EACb;CACF;CAEA,UAAgB;EACd,KAAK,YAAY;EACjB;GAAC,KAAK;GAAe,KAAK;GAAa,KAAK;EAAW,CAAC,CAAC,SAAS,WAAW,KAAK,OAAO,MAAM,CAAC;EAChG,KAAK,gBAAgB,KAAK,cAAc,KAAK,cAAc;CAC7D;CAEA,SAAe;EACb,IAAI,KAAK,YACP;EAEF,IAAI,KAAK,SAAS,SAAS,QACrB;OAAA,KAAK,eAAe,MACtB,KAAK,UAAU;EAAA,OAGjB,OAAO,KAAK,OAAO,SAAS,GAC1B,KAAK,aAAa,KAAK,OAAO,MAAM,CAAE;CAG5C;CAEA,YAAkB;EAChB,MAAM,OAAO,KAAK,OAAO,MAAM;EAC/B,IAAI,QAAQ,MAAM;GAChB,KAAK,cAAc;GACnB;EACF;EACA,KAAK,aAAa,IAAI;EACtB,KAAK,cAAc,KAAK,WAAW,iBAAiB,KAAK,UAAU,GAAG,KAAK,SAAS,cAAc;CACpG;CAEA,aAAa,MAAoB;EAC/B,MAAM,SAAS,KAAK,OAAO,SAAS,IAAI,MAAM,MAAM;EACpD,KAAK,UAAU;EACf,KAAK,MAAM,YAAY,KAAK;EAC5B,KAAK,qBAAqB;CAC5B;CAEA,uBAA6B;EAC3B,IAAI,CAAC,KAAK,SAAS,aACjB;EAEF,KAAK,OAAO,KAAK,WAAW;EAC5B,KAAK,cAAc,KAAK,WAAW,iBAAiB;GAClD,KAAK,cAAc;GACnB,KAAK,eAAe,KAAK,gBAAgB;EAC3C,GAAG,KAAK,SAAS,qBAAqB;CACxC;CAEA,MAAM,kBAAiC;EACrC,MAAM,EAAE,gBAAgB,KAAK;EAC7B,IAAI,CAAC,aACH;EAEF,MAAM,QAAQ,KAAK;EACnB,IAAI;GACF,MAAM,SAAS,MAAM,YAAY,KAAK;GAGtC,IAAI,CAAC,KAAK,aAAa,WAAW,SAAS,KAAK,WAAW,OAAO;IAChE,KAAK,SAAS;IACd,KAAK,MAAM,aAAa,MAAM;GAChC;EACF,QAAQ,CAER;CACF;CAEA,OAAO,QAAuB;EAC5B,IAAI,UAAU,MACZ,KAAK,WAAW,aAAa,MAAM;CAEvC;AACF;;;;;;;;;ACzOA,IAAa,cAAc,UAA6B,CAAC,MAAiB;CACxE,MAAM,gBAAgB,QAAQ,iCAAiB,IAAI,IAAY;CAC/D,MAAM,iBAAiB,QAAQ,kBAAkB;CACjD,MAAM,gBAAgB,QAAQ,iBAAiB;CAC/C,MAAM,gBAAgB,QAAQ,iBAAiB;CAG/C,MAAM,iBAAiB,YAAY,OAAuC;CAE1E,MAAM,cAAc,YAAY,OAAuC;CAGvE,MAAM,cAAc,WAAW,OAAoB;EACjD,eAAe;GAAE,MAAM;GAAI,MAAM;GAAG,UAAU;EAAE;EAChD,SAAS,OAAO,OAAO;GACrB,IAAI,EAAE,MAAM,MAAM,aAAa;GAC/B,KAAK,MAAM,UAAU,GAAG,SAAS;IAC/B,IAAI,OAAO,GAAG,cAAc,GAAG;KAE7B,IAAI,KAAK,WAAW,MAClB,WAAW,OAAO,MAAM;KAE1B,QAAQ,OAAO,MAAM;IACvB;IACA,IAAI,OAAO,GAAG,WAAW,GAAG;KAC1B,QAAQ,OAAO,MAAM,KAAK;KAC1B,WAAW,OAAO,MAAM,OAAO,OAAO,MAAM,KAAK;KAEjD,IAAI,QAAQ,0BAA2B,OAAO,KAAK,OAAO,KAAK,KAAK,QAAS;MAC3E,OAAO,KAAK,MAAM,IAAI;MACtB,OAAO;KACT;IACF;GACF;GAGA,IAAI,GAAG,YAAY;IACjB,IAAI,UAAU,GAAG,MAAM,IAAI,WAAW;IACtC,IAAI,CAAC,WAAW,GAAG,WAAW,IAAI,SAAS,GACzC,GAAG,QAAQ,aAAa,OAAO,QAAQ;KACrC,IAAI,UAAU,KAAK,QAAQ,GAAG,WAAW,IAAI,QAC3C,UAAU;IAEd,CAAC;IAEH,IAAI,SACF,OAAO;KAAE,MAAM;KAAI,MAAM;KAAG,UAAU;IAAE;IAI1C,IAAI,CAAC,GAAG,QAAQ,MAAM,WAAW,OAAO,GAAG,WAAW,CAAC,GACrD,WAAW,GAAG,QAAQ,OAAO,KAAK,IAAI,UAAU,GAAG,WAAW,IAAI,MAAM,CAAC;GAE7E;GAEA,OAAO;IAAE;IAAM;IAAM;GAAS;EAChC;CACF,CAAC;CA8JD,OAAO;EAAC;EA3JO,cAAY,kBAAkB,IAAI,OAAoB;GACnE,IAAI,CAAC,GAAG,YACN,OAAO;GAIT,IAAI,GAAG,WAAW,gBAAgB,KAAK,GAAG,QAAQ,MAAM,WAAW,OAAO,GAAG,WAAW,CAAC,GACvF,OAAO;GAIT,IAAI,eAAe;GACnB,IAAI,aAAa;GACjB,IAAI,eAAe;GAEnB,GAAG,QAAQ,aAAa,OAAO,KAAK,QAAQ,MAAM,aAAa;IAC7D,IAAI,QAAQ,GAAG,WAAW,IAAI,UAAU,UAAU,OAAO,SAAS,SAAS,GAAG;KAC5E,gBAAgB,SAAS,YAAY,CAAC;KACtC,IAAI,eAAe,IACjB,aAAa;IAEjB,OACE,eAAe;GAEnB,CAAC;GAED,IAAI,CAAC,gBAAgB,aAAa,WAAW,GAC3C,OAAO;GAIT,OAAO;IACL,SAAS,UAAU,MAAM,GAAG,WAAW,IAAI,MAAM;IACjD,SAAS,eAAe,GAAG;KAAE,MAAM;KAAY,MAAM;IAAa,CAAC;GACrE;EACF,CAwHqB;EAnHD,WAAW,UAC7B,MAAM;GAIgB;GAHpB;GACA,mBAAkC;GAElC,YAAY,MAA0B;IAAlB,KAAA,OAAA;GAIpB;GAEA,OAAO,QAAoB;IACzB,MAAM,EAAE,MAAM,SAAS,OAAO,MAAM,MAAM,WAAW;IACrD,MAAM,UAAU,KAAK,SAAS;IAC9B,IAAI,YAAY,GACd,KAAK,mBAAmB;IAE1B,IAAI,UAAU,KAAK,KAAK,SAAS,KAAA,GAC/B,KAAK,OAAO;GAEhB;GAEA,SAAS;IAGP,qBAAqB;KACnB,KAAK,KAAK,SAAS;MACjB,SAAS,yBAAyB,GAAG,IAAI;MACzC,aAAa,iBAAiB,GAAG,IAAI;KACvC,CAAC;IACH,CAAC;IACD,KAAK,OAAO,sBAAsB,KAAK,KAAK;GAC9C;GAEA,cAAc;IACZ,MAAM,EAAE,MAAM,MAAM,aAAa,KAAK,KAAK,MAAM,MAAM,WAAW;IAClE,MAAM,UAAU,KAAK,SAAS;IAE9B,IAAI,YAAY,GAAG;KACjB,KAAK,KAAK,SAAS;MACjB,SAAS,yBAAyB,GAAG,KAAK;MAC1C,aAAa,iBAAiB,GAAG,IAAI;KACvC,CAAC;KACD,KAAK,OAAO,KAAA;KACZ;IACF;IAKA,IAAI,UAAU,gBAAgB;KAC5B,MAAM,QAAQ,KAAK,MAAM,IAAI;KAC7B,KAAK,mBAAmB;KACxB,KAAK,KAAK,SAAS;MACjB,SAAS;OAAE,MAAM;OAAU,QAAQ;MAAM;MACzC,SAAS,YAAY,GAAG;OAAE,MAAM;OAAU,MAAM;MAAM,CAAC;KACzD,CAAC;KACD,KAAK,OAAO,sBAAsB,KAAK,KAAK;KAC5C;IACF;IAOA,MAAM,YAAY,YAAY,IAAI;IAClC,IAAI,MAAM;IACV,IAAI,YAAY,KAAK;IACrB,IAAI,eAAe;IACnB,OAAO,MAAM,KAAK,UAAU,YAAY,IAAI,IAAI,YAAY,eAAe;KACzE,MAAM,SAAS,UAAU,MAAM,KAAK,eAAe,SAAS;KAC5D,IAAI,OAAO,UAAU,GAEnB;KAKF,IAAI,eAAe,KAAK,eAAe,OAAO,QAAQ,eACpD;KAEF,IAAI,OAAO,UACT,YAAY,OAAO;KAErB,IAAI,OAAO,SACT,YAAY;KAEd,OAAO,OAAO;KACd,gBAAgB,OAAO;IACzB;IAEA,MAAM,aAAa,MAAM;IACzB,IAAI,aAAa,GAAG;KAClB,MAAM,QAAQ,KAAK,MAAM,MAAM,OAAO,UAAU;KAChD,KAAK,mBAAmB;KACxB,KAAK,KAAK,SAAS;MACjB,SAAS;OAAE,MAAM;OAAU,QAAQ;MAAM;MACzC,SAAS,YAAY,GAAG;OAAE,MAAM;OAAU,MAAM;MAAM,CAAC;KACzD,CAAC;IACH;IAIA,KAAK,OAAO,sBAAsB,KAAK,KAAK;GAC9C;GAEA,UAAU;IACR,IAAI,KAAK,SAAS,KAAA,GAChB,qBAAqB,KAAK,IAAI;GAElC;EACF,CAG2B;EAAa,QAAQ,UAAU,iBAAiB,WAAW;CAAC,CAAC,CAAC,OACzF,OACF;AACF;;AAGA,IAAa,mBAAmB,WAAW,OAAgB;;;;;;;;;;AAW3D,IAAa,2BAA2B,YAAY,OAAgB;;AAUpE,IAAM,gBAAgB;;AAGtB,IAAM,kBAAkB;;;;;;;AAQxB,IAAM,kBAAkB;;;;;;AAOxB,IAAM,kBAAkB;;AAGxB,IAAM,yBAAyB;;;;;AAU/B,IAAM,oBAAoB,gBAAoD;CAC5E,MAAM,aAAa,YAAY,OAAO;CAEtC,MAAM,kBAAkB,WAAW,OAKhC;EACD,eAAe;GAAE,SAAS;GAAO,UAAU;GAAG,aAAa;EAAE;EAC7D,SAAS,OAAO,OAAO;GACrB,MAAM,EAAE,MAAM,MAAM,aAAa,GAAG,MAAM,MAAM,WAAW;GAE3D,IADgB,KAAK,SAAS,OAChB,GAAG;IAEf,IAAI,cAAc,GAAG,QAAQ,OAAO,KAAK,IAAI,MAAM,aAAa,GAAG,WAAW,IAAI,MAAM,CAAC;IACzF,IAAI,GAAG,YACL,GAAG,QAAQ,aAAa,QAAQ,MAAM,QAAQ,MAAM,aAAa;KAC/D,MAAM,QAAQ,SAAS,YAAY,CAAC;KACpC,IAAI,MAAM,KAAK,CAAC,CAAC,SAAS,GACxB,cAAc,SAAS,MAAM;IAEjC,CAAC;IAEH,OAAO;KAAE,SAAS;KAAM;KAAU;IAAY;GAChD;GACA,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,UAAU,GACtB,OAAO;IAAE,GAAG;IAAO,SAAS;GAAM;GAGtC,OAAO;EACT;CACF,CAAC;CAqDD,OAAO;EAAC;EAnDgB,WAAW,OAAsB;GACvD,cAAc,WAAW;GACzB,SAAS,cAAc,OAAO;IAC5B,MAAM,EAAE,SAAS,UAAU,gBAAgB,GAAG,MAAM,MAAM,eAAe;IACzE,IAAI,CAAC,SACH,OAAO,WAAW;IAGpB,MAAM,EAAE,MAAM,SAAS,GAAG,MAAM,MAAM,WAAW;IAGjD,MAAM,WAAW,KAAK,SAAS,OAAO,WAAW;IACjD,MAAM,MAAM,KAAK,IAAI,UAAU,GAAG,MAAM,IAAI,MAAM;IAClD,OAAO,WAAW,IAAI,CACpB,WAAW,OAAO;KAChB,QAAQ,IAAI,aAAa;KACzB,MAAM;IACR,CAAC,CAAC,CAAC,MAAM,GAAG,CACd,CAAC;GACH;GACA,UAAU,UAAU,aAAW,YAAY,KAAK,KAAK;EACvD,CA8ByB;EA5BL,WAAW,UAC7B,MAAM;GAGgB;GAFpB;GAEA,YAAY,MAA0B;IAAlB,KAAA,OAAA;GAAmB;GAEvC,OAAO,QAAoB;IACzB,MAAM,EAAE,MAAM,SAAS,OAAO,MAAM,MAAM,WAAW;IACrD,MAAM,EAAE,YAAY,OAAO,MAAM,MAAM,eAAe;IAGtD,IAFgB,KAAK,SAAS,OAEhB,GAAG;KACf,aAAa,KAAK,MAAM;KACxB,KAAK,SAAS,KAAA;IAChB,OAAO,IAAI,WAAW,KAAK,WAAW,KAAA,GACpC,KAAK,SAAS,iBAAiB;KAC7B,KAAK,KAAK,SAAS,EAAE,SAAS,WAAW,GAAG,IAAI,EAAE,CAAC;KACnD,KAAK,SAAS,KAAA;IAChB,GAAG,aAAa;GAEpB;GAEA,UAAU;IACR,aAAa,KAAK,MAAM;GAC1B;EACF,CAGwC;CAAW;AACvD;;;;;AAMA,IAAM,eAAN,MAAM,qBAAqB,WAAW;CAGpC,GAAY,OAA4B;EACtC,OAAO,iBAAiB;CAC1B;CAEA,QAAQ;EACN,MAAM,QAAQ,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,MAAM;GAAE,WAAW;GAAqB,gBAAgB;EAAQ,CAAC;EAC3G,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC;CACnE;AACF;;;;;AAMA,IAAM,mBAAmB;;AAGzB,IAAM,iBAAiB;;;;;;;;;;AAmBvB,IAAM,aACJ,QACA,OACA,eACA,oBACgB;CAChB,IAAI,SAAS,OAAO,QAClB,OAAO,EAAE,OAAO,EAAE;CAIpB,IAAI,iBAAiB;EACnB,MAAM,WAAW,KAAK,gBAAgB;EACtC,IAAI,OAAO,WAAW,UAAU,KAAK,GACnC,OAAO;GAAE,OAAO,SAAS;GAAQ,SAAS;EAAK;EAGjD,IAAI,OAAO,WAAW,KACpB,OAAO,EAAE,OAAO,iBAAiB,QAAQ,KAAK,EAAE;EAGlD,OAAO,EAAE,OAAO,EAAE;CACpB;CAEA,MAAM,KAAK,OAAO;CAGlB,IAAI,OAAO,KAAK;EAGd,MAAM,YADQ,OAAO,MAAM,OAAO,QAAQ,cACxB,CAAA,CAAM,MAAM,gBAAgB;EAC9C,IAAI,aAAa,cAAc,IAAI,UAAU,EAAE,GAAG;GAChD,MAAM,QAAQ,OAAO,QAAQ,KAAK,KAAK;GACvC,IAAI,UAAU,IACZ,OAAO,EAAE,OAAO,EAAE;GAGpB,IAAI,OAAO,QAAQ,OAAO,KACxB,OAAO,EAAE,OAAO,QAAQ,IAAI,MAAM;GAGpC,OAAO;IAAE,OAAO,QAAQ,IAAI;IAAO,UAAU,UAAU;GAAG;EAC5D;EAEA,OAAO,EAAE,OAAO,iBAAiB,QAAQ,KAAK,EAAE;CAClD;CAGA,IAAI,OAAO,OAAO,OAAO,SAAS,QAAQ,KAAK,OAAO,QAAQ,OAAO,KACnE,OAAO,EAAE,OAAO,WAAW,QAAQ,OAAO,QAAQ,CAAC,EAAE;CAIvD,IAAI,OAAO,KACT,OAAO,EAAE,OAAO,WAAW,QAAQ,OAAO,KAAK,EAAE;CAGnD,OAAO,EAAE,OAAO,EAAE;AACpB;;;;;;AAOA,IAAa,oBAAoB,QAAgB,QAAQ,MAAc;CACrE,MAAM,QAAQ,OAAO,QAAQ,KAAK,KAAK;CACvC,IAAI,UAAU,IACZ,OAAO;CAIT,IAAI,OAAO,QAAQ,OAAO,KACxB,OAAO,QAAQ,IAAI;CAIrB,IAAI,OAAO,QAAQ,OAAO,KACxB,OAAO,QAAQ,IAAI;CAKrB,MAAM,YADQ,OAAO,MAAM,OAAO,QAAQ,cACxB,CAAA,CAAM,MAAM,gBAAgB;CAC9C,IAAI,CAAC,WAEH,OAAO;CAGT,MAAM,UAAU,UAAU;CAC1B,IAAI,QAAQ;CAGZ,MAAM,aAAa,IAAI,OAAO,QAAQ,mBAAmB,OAAO,EAAE,eAAe,GAAG;CACpF,WAAW,YAAY;CACvB,IAAI;CACJ,QAAQ,QAAQ,WAAW,KAAK,MAAM,OAAO,MAAM;EACjD,MAAM,gBAAgB,MAAM,EAAE,CAAC,SAAS,IAAI;EAC5C,MAAM,YAAY,MAAM,OAAO;EAE/B,IAAI,eAEE;OAAA,UAAU,GACZ,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,SAAS;EAAA,OAEpC,IAAI,WAAW;GACpB;GACA,IAAI,UAAU,GACZ,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,SAAS;EAE3C,OACE;CAEJ;CAGA,OAAO;AACT;;;;;;AAOA,IAAM,cAAc,QAAgB,OAAe,cAA8B;CAC/E,MAAM,eAAe,OAAO,QAAQ,KAAK,YAAY,CAAC;CACtD,IAAI,iBAAiB,IACnB,OAAO;CAIT,IAAI,eAAe,KAAK,OAAO,QAC7B,OAAO;CAET,IAAI,OAAO,eAAe,OAAO,KAE/B,OAAO;CAGT,MAAM,aAAa,OAAO,QAAQ,KAAK,eAAe,CAAC;CACvD,IAAI,eAAe,IACjB,OAAO;CAGT,OAAO,aAAa,IAAI;AAC1B;;;;;ACxfA,IAAa,wBAAwB;;;;;;;AAQrC,IAAa,sBAAsB,MAAkB,YAA2B;CAC9E,MAAM,OAAO,QAAQ,QAAQ,IAAI,sBAAsB,EAAE;CACzD,IAAI,gBAAgB,gBAAgB,KAAK,MAAM,UAAU,KAAK,MAAM,YAAY;EAC9E,KAAK,MAAM,SAAS;EACpB,KAAK,MAAM,YAAY;EACvB,KAAK,MAAM,WAAW;EACtB,KAAK,eAAe;CACtB;AACF;AAEA,IAAa,aAAb,cAA4D,WAAW;CACrE,QAA4B;CAC5B;CAEA,yBAAyB;CAEzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,SAAoC;EAC9C,MAAM;EACN,KAAK,KAAK,QAAQ;EAClB,KAAK,YAAY,QAAQ;EACzB,KAAK,QAAQ,QAAQ;EACrB,KAAK,WAAW,QAAQ;EACxB,KAAK,YAAY,QAAQ;EACzB,KAAK,YAAY,QAAQ;EACzB,KAAK,QAAQ,QAAQ;EACrB,KAAK,cAAc,QAAQ;EAC3B,KAAK,aAAa,QAAQ;EAC1B,KAAK,YAAY,QAAQ;EACzB,KAAK,QAAQ,QAAQ;EACrB,UAAU,KAAK,IAAC,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,CAAA,WAAA,EAAA;EAAA,CAAC;CACnB;CAEA,IAAI,OAA2B;EAC7B,OAAO,KAAK;CACd;;CAGA,OAAO,OAAe,MAAsC;EAC1D,IAAI,KAAK,OACP,IAAI,KAAK,gBAAgB,SAAS;GAAE,IAAI,KAAK;GAAI,OAAO,KAAK;GAAO,GAAG;EAAK,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA;EAAA,CAAC;CAEjF;CAGA,IAAa,kBAAkB;EAC7B,OAAO,KAAK,SAAS,KAAK,eAAe,OAAO,KAAK,cAAc;CACrE;CAMA,SAAkB,KAAkB,KAAa,MAAc;EAC7D,IAAI,CAAC,KAAK,OACR,OAAO;EAET,MAAM,OAAO,IAAI,sBAAsB;EACvC,MAAM,QAAS,KAAK,MAAsB;EAC1C,MAAM,SAAS,QAAQ,MAAM,KAAK,MAAM,OAAO;EAC/C,MAAM,WAAW,SAAS,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,OAAO,IAAI,IAAI;EACtF,MAAM,IAAI,KAAK,MAAM,KAAK,SAAS;EACnC,IAAI,KAAK,OAAO;GAId,IAHiB,KAAK,WAAW,KAAM,KAAK,QAAQ,KAAK,KAAK,WAAW,GAIvE,IAAI,KAAK,sDAAsD;IAC7D,IAAI,KAAK;IACT;IACA;IACA,WAAW,IAAI;IACf,MAAM;KAAE,KAAK,KAAK;KAAK,QAAQ,KAAK;IAAO;IAC3C;GACF,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA;GAAA,CAAC;QACI,IAAI,CAAC,KAAK,wBAAwB;IAEvC,KAAK,yBAAyB;IAC9B,4BAA4B;KAC1B,KAAK,yBAAyB;IAChC,CAAC;IACD,IAAI,KAAK,yBAAyB;KAChC,IAAI,KAAK;KACT;KACA;KACA,MAAM;MAAE,KAAK,KAAK,MAAM,KAAK,GAAG;MAAG,QAAQ,KAAK,MAAM,KAAK,MAAM;KAAE;KACnE,GAAG,KAAK,MAAM,CAAC;KACf,WAAW,KAAK,MAAM,KAAK,OAAO,UAAU,aAAa,EAAE;IAC7D,GAAA;KAAA,YAAA;KAAA,GAAA;KAAA,GAAA;KAAA,GAAA;IAAA,CAAC;GACH;EACF;EACA,OAAO;GAAE,MAAM,KAAK;GAAM,OAAO,KAAK;GAAO,KAAK;GAAG,QAAQ;EAAE;CACjE;CAEA,GAAY,OAAa;EACvB,IAAI,KAAK,WACP,OAAO;EAaT,MAAM,WAAW,UAAmB,MAAsB;EAC1D,OACE,KAAK,OAAO,MAAM,MAClB,KAAK,UAAU,MAAM,SACrB,KAAK,cAAc,MAAM,aACzB,QAAQ,KAAK,KAAK,MAAM,QAAQ,MAAM,KAAK;CAE/C;CAEA,cAAuB;EACrB,OAAO;CACT;;;;;;;;;;;;CAaA,IAAI,aAAsB;EACxB,OAAO,CAAC,CAAC,KAAK,UAAU,KAAK,cAAc,QAAQ,KAAK,eAAe;CACzE;CAEA,MAAe,MAAkB;EAC/B,KAAK,QAAQ;EACb,MAAM,SAAS,KAAK,cAAc,KAAK,SAAS;EAChD,IAAI,CAAC,KAAK,OAAO;GACf,KAAK,QAAQ,KAAK,QAAQ,OAAO,GAAG,KAAK,CAAC,CAAC,WAAW,cAAc,CAAC,CAAC,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC;GAC/F,KAAK,MAAM,aAAa,uBAAuB,EAAE;GACjD,KAAK,kBAAkB,KAAK,KAAK;EACnC;EAEA,MAAM,QAAQ,OAAO,OAAO,CAAC,GAAG,KAAK,OAAO,EAAE,KAAK,CAAC;EAIpD,KAAK,SAAS,QAAQ;GACpB,IAAI,KAAK;GACT,MAAM,KAAK;GACX;GACA,WAAW,KAAK;EAClB,CAAC;EACD,KAAK,OAAO,SAAS,8BAA8B,kBAAkB;GACnE,aAAa,KAAK;GAClB,WAAW,KAAK,MAAM,KAAK,UAAU,SAAS;EAChD,CAAC;EACD,KAAK,mBAAmB,OAAO;EAC/B,OAAO,KAAK;CACd;CAIA,UAAmB,KAAkB,MAAkB;EAGrD,IAAI,KAAK,WAAW,IAAI,YAAY,QAClC,OAAO;EAET,KAAK,QAAQ;EACb,KAAK,QAAQ;EAGb,KAAK,kBAAkB,GAAG;EAC1B,MAAM,QAAQ,OAAO,OAAO,CAAC,GAAG,KAAK,OAAO,EAAE,KAAK,CAAC;EACpD,KAAK,SAAS,QAAQ;GACpB,IAAI,KAAK;GACT,MAAM,KAAK;GACX;GACA,WAAW,KAAK;EAClB,CAAC;EACD,KAAK,OAAO,+BAA+B,EAAE,WAAW,IAAI,YAAY,CAAC;EACzE,KAAK,mBAAmB,WAAW;EACnC,OAAO;CACT;CAEA,QAAiB,MAAmB;EAClC,IAAI,KAAK,YAAY;GAGnB,KAAK,OAAO,8BAA8B,EAAE,WAAW,KAAK,MAAM,KAAK,OAAO,UAAU,aAAa,EAAE,EAAE,CAAC;GAC1G;EACF;EAEA,KAAK,OAAO,kBAAkB,EAAE,WAAW,KAAK,MAAM,KAAK,OAAO,UAAU,aAAa,EAAE,EAAE,CAAC;EAC9F,KAAK,SAAS,UAAU,KAAK,IAAI,KAAK,SAAS,IAAI;EACnD,KAAK,QAAQ;EACb,KAAK,QAAQ,KAAA;CACf;;CAGA,kBAAkB,MAAyB;EACzC,IAAI,CAAC,KAAK,OACR;EAEF,MAAM,SAAS,KAAK,eAAe,QAAQ,KAAK,eAAe;EAC/D,MAAM,UAAU,KAAK,eAAe,QAAQ,KAAK,eAAe;EAGhE,KAAK,MAAM,YAAY,UAAU,GAAG,KAAK,YAAY,MAAM;EAI3D,KAAK,MAAM,SAAS,SAAS,GAAG,KAAK,YAAY,MAAM;EACvD,KAAK,MAAM,WAAW,SAAS,WAAW;CAC5C;;;;;;CAOA,mBAAmB,QAAsB;EACvC,IAAI,CAAC,KAAK,SAAS,CAAC,KAAK,OACvB;EAGF,MAAM,OAAO,KAAK;EAClB,4BAA4B;GAC1B,IAAI,CAAC,MACH;GAGF,MAAM,WAAW,KAAK,sBAAsB,CAAC,CAAC;GAC9C,MAAM,WAAW,KAAK,eAAe;GACrC,IAAI,KAAK,+BAA+B,UAAU;IAChD,IAAI,KAAK;IACT;IACA;IACA,OAAO,WAAW,IAAI,WAAW,WAAW,KAAA;IAC5C,WAAW,KAAK;GAClB,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA;GAAA,CAAC;EACH,CAAC;CACH;AACF;;;;;;;ACpTA,IAAa,yBAAyB,YAAY,OAAa;;;;AAK/D,IAAa,qBAAqB,YAAY,OAAa;;;;AAmG3D,IAAa,sBAAsB,YAAY,OAAgB;;;;AAK/D,IAAa,oBAAoB,YAAY,OAAO;;;;;AAMpD,IAAa,sBAAsB,YAAY,OAAO;;;;AAQtD,IAAa,qBAAqB,YAAY,OAAiD;;AAgD/F,IAAa,sBAAsB,MAAM,OAAgD,EACvF,UAAU,aAAa,SACzB,CAAC;AAcD,IAAM,kBAAkB,MAAM,OAA6C,EACzE,UAAU,aAAa;CACrB,YAAY,QAAQ,MAAM,WAAW,OAAO,UAAU,CAAC,EAAE;CACzD,WAAW,QAAQ,SAAS,WAAW,OAAO,aAAa,CAAC,CAAC;AAC/D,GACF,CAAC;;;;;AAMD,IAAa,cAAc,YAA0C,gBAAgB,GAAG,OAAO;;AAO/F,IAAa,YAAY,UAAmB,aAC1C,OAAO,aAAa,YAAY,SAAS,SAAS,IAAI,WAAW;;;;;AAgBnE,IAAa,gBAA4C,EACvD,KACA,IACA,OACA,UACA,WACA,gBACyD;CACzD,IAAI,IAAI,SACN,OAAO,IAAI,QAAQ,KAAK,KAAK,KAAA;CAE/B,IAAI,IAAI,WAAW;EAGjB,MAAM,cAAc,IAAI,QAAQ,IAAI,kBAAkB,KAAK,IAAI,KAAA;EAc/D,OAAO,IAbY,WAAW;GAC5B;GACA,WAAW,IAAI;GACf;GACA;GACA;GACA;GACA,OAAO,CAAC,CAAC,IAAI;GACb;GACA,YAAY,IAAI;GAChB,WAAW,IAAI;GACf,OAAO,IAAI;EACb,CACO;CACT;AAEF;;;;AASA,IAAM,0BAA0B,WAAW,OAAgB;CACzD,cAAc,KAAA;CACd,SAAS,OAAO,OAAO;EACrB,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,mBAAmB,GAC/B,OAAO,OAAO;EAIlB,OAAO;CACT;AACF,CAAC;;;;AAKD,IAAM,2BAA2B,WAAW,OAAuB;CACjE,eAAe,CAAC;CAChB,SAAS,KAAK,OAAO;EAGnB,IAAI,OAAO;EACX,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,iBAAiB,GAC7B,OAAO,CAAC;OACH,IAAI,OAAO,GAAG,kBAAkB,GAAG;GAExC,MAAM,EAAE,IAAI,UAAU,OAAO;GAC7B,IAAI,kBAAkB;IAAE;IAAI;GAAM,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;GACnC,OAAO;IAAE,GAAG;KAAO,KAAK,OAAO,UAAU,aAAa,MAAM,KAAK,GAAG,IAAI;GAAM;EAChF;EAGF,OAAO;CACT;AACF,CAAC;;AAGD,IAAa,kBAAkB,OAAoB,OACjD,MAAM,MAAM,0BAA0B,KAAK,CAAC,GAAG;;;;;;;;;;AAWjD,IAAM,0BAA0B,UAAoC;CAElE,MAAM,eADsC,MAAM,OAAO,KAAA,EAC/B,MAAM,MAAM,0BAA0B,KAAK,CAAC,GAAG,MAAM;CAC/E,OAAO,cAAc;EAAE,GAAG;EAAO,OAAO;GAAE,GAAG,MAAM;GAAO,GAAG;EAAY;CAAE,IAAI;AACjF;;;;AAKA,IAAM,mBAAmB,YAA8C,QAAQ,UAA0B;CACvG,MAAM,0BAAU,IAAI,IAAyB;CAuD7C,OAAO;EAnDL,UAAU,aAA0B;GAClC,MAAM,QAAQ,uBAAuB,QAAQ;GAC7C,MAAM,QAAQ,CAAC,QAAQ,IAAI,MAAM,EAAE;GACnC,QAAQ,IAAI,MAAM,IAAI,KAAK;GAE3B,IAAI,SAAS,OACX,IAAI,KAAK,uBAAuB;IAAE,IAAI,MAAM;IAAI,OAAO,QAAQ;GAAK,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;GAEvE,aAAa,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC;EACpC;EACA,UAAU,IAAY,gBAAsC;GAC1D,MAAM,UAAU,QAAQ,IAAI,EAAE;GAC9B,IAAI,CAAC,WAAW,CAAC,aACf;GAGF,QAAQ,IAAI,IAAI;IAAE,GAAG;IAAS,OAAO;KAAE,GAAG,QAAQ;KAAO,GAAG;IAAY;GAAE,CAAC;GAC3E,aAAa,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC;EACpC;EACA,YAAY,IAAY,SAA8B;GAIpD,MAAM,UAAU,QAAQ,IAAI,EAAE;GAC9B,IAAI,CAAC,WAAY,QAAQ,QAAQ,SAAS,MACxC;GAEF,QAAQ,OAAO,EAAE;GAEjB,IAAI,OACF,IAAI,KAAK,yBAAyB;IAAE;IAAI,OAAO,QAAQ;GAAK,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;GAE/D,aAAa,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC;EACpC;EACA,YAAY,YAAyB;GACnC,IAAI,UAAU;GACd,KAAK,MAAM,MAAM,CAAC,GAAG,QAAQ,KAAK,CAAC,GACjC,IAAI,CAAC,QAAQ,IAAI,EAAE,GAAG;IACpB,QAAQ,OAAO,EAAE;IACjB,UAAU;GACZ;GAEF,IAAI,SAAS;IACX,IAAI,OACF,IAAI,KAAK,gCAAgC;KAAE,MAAM,CAAC,GAAG,OAAO;KAAG,OAAO,QAAQ;IAAK,GAAA;KAAA,YAAA;KAAA,GAAA;KAAA,GAAA;KAAA,GAAA,KAAA;IAAA,CAAC;IAEtF,aAAa,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC;GACpC;EACF;CAGK;AACT;;AAcA,IAAM,eAAe;;;;;AAMrB,IAAM,sBACJ,OACA,OACA,UAEA,aAAa,UAC6B;CAC1C,MAAM,WAAW,MAAM,MAAM,mBAAmB;CAChD,MAAM,UAAU,MAAM,MAAM,yBAAyB,KAAK;CAC1D,MAAM,iBAAiB,MAAM,MAAM,0BAA0B,KAAK,KAAK,CAAC;CACxE,MAAM,UAAU,IAAI,gBAA4B;CAIhD,MAAM,QAAQ,aAAa,iBAAiB,OAAO,MAAM,IAAI,YAAY,IAAI,SAAS,WAAW,KAAK;CACtG,IAAI,CAAC,QAAS,KAAK,KAAK,SAAS,aAAa,KAAK,WAAW,GAC5D,OAAO;EAAE,MAAM,MAAM;EAAM,aAAa,WAAW;EAAM,QAAQ,WAAW;CAAK;CAGnF,IAAI,OAAO,MAAM;CACjB,IAAI;CACJ,MAAM,MAA0B;EAAE;EAAO;EAAS;EAAgB;EAAU,0BAAU,IAAI,IAAI;CAAE;CAGhG,MAAM,UAAyB,CAAC;CAEhC,KAAK,QAAQ;EACX,MAAM,MAAM;EACZ,IAAI,MAAM;EACV,QAAQ,SAAS;GACf,MAAM,YAAY,SAAS,QAAQ,YAAY,QAAQ,MAAM,SAAS,KAAK,KAAK,IAAI,CAAC;GACrF,IAAI,UAAU,WAAW,GACvB;GAGF,KAAK,MAAM,WAAW,WACpB,IAAI;IACF,MAAM,QAAQ,QAAQ,MAAM,MAAM,GAAG;IACrC,IAAI,OAAO;KACT,QAAQ,KAAK,KAAK;KAElB,OAAO,MAAM,KAAK;KAClB;IACF;GACF,SAAS,KAAK;IACZ,IAAI,MAAM,KAAE,KAAA,GAAA;KAAA,YAAA;KAAA,GAAA;KAAA,GAAA;KAAA,GAAA,KAAA;IAAA,CAAC;GACf;GAIF,OAAO;EACT;CACF,CAAC;CAED,KAAK,MAAM,WAAW,UAAU;EAC9B,MAAM,QAAQ,QAAQ,OAAO;GAAE;GAAO;GAAO;GAAS;GAAgB;EAAS,CAAC;EAChF,IAAI,OAAO;GAET,MAAM,OAAO,QAAQ,QAAQ,cAAc,UAAU,MAAM,MAAM,IAAI;GACrE,QAAQ,SAAS;GACjB,QAAQ,KAAK,GAAG,MAAM,KAAK;GAC3B,gBAAgB,MAAM;GACtB,OAAO,MAAM;GAEb;EACF;CACF;CAEA,KAAK,MAAM,SAAS,QAAQ,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,GACvE,QAAQ,IAAI,MAAM,MAAM,MAAM,IAAI,MAAM,UAAU;CAGpD,MAAM,cAAc,QAAQ,OAAO;CACnC,OAAO;EAAE,MAAM;EAAM;EAAe;EAAa,QAAQ,aAAa,OAAO,WAAW;CAAE;AAC5F;;;;;AAMA,IAAM,yBAAyB,WAAW,OAA4B;CACpE,SAAS,UAAU;EACjB,MAAM,EAAE,eAAe,MAAM,MAAM,eAAe;EAElD,MAAM,WAAW,gBAAgB,YADnB,MAAM,MAAM,mBAAmB,CAAC,CAAC,MAAM,YAAY,QAAQ,KAC5B,CAAK;EAElD,OAAO;GAAE,GAAG,mBAAiB,OAAO;IAAE,MAAM;IAAG,IAAI,MAAM,IAAI;GAAO,GAAG,UAAU,IAAI;GAAG;EAAS;CACnG;CACA,SAAS,EAAE,MAAM,eAAe,aAAa,QAAQ,YAAY,OAAO;EACtE,MAAM,WAAW,WAAuC;GACtD,GAAG,mBAAiB,GAAG,OAAO,OAAO,QAAQ;GAC7C;EACF;EACA,MAAM,QAAQ;GAAE,MAAM;GAAG,IAAI,GAAG,MAAM,IAAI;EAAO;EAGjD,KAAK,MAAM,UAAU,GAAG,SAAS;GAC/B,IAAI,OAAO,GAAG,iBAAiB,GAAG;IAChC,IAAI,GAAG,YACL,OAAO,QAAQ,KAAK;IAEtB,OAAO;KAAE,MAAM;KAAG,aAAa,WAAW;KAAM,QAAQ,WAAW;KAAM;IAAS;GACpF;GAEA,IAAI,OAAO,GAAG,mBAAmB,GAC/B,OAAO,QAAQ,KAAK;GAKtB,IAAI,OAAO,GAAG,mBAAmB,GAC/B,OAAO,QAAQ,KAAK;EAExB;EAEA,IAAI,GAAG,YAAY;GACjB,MAAM,EAAE,UAAU;GAGlB,IADc,GAAG,QAAQ,aAAa,GAAG,IACrC,GAAO;IACT,IAAI,kBAAkB;KAAE;KAAM,IAAI,MAAM,IAAI;IAAO,GAAA;KAAA,YAAA;KAAA,GAAA;KAAA,GAAA;KAAA,GAAA,KAAA;IAAA,CAAC;IAEpD,OAAO,QAAQ,KAAK;GACtB,OAAO;IAGL,MAAM,SAAS,mBAAiB,OAAO;KAAE,MADrB,iBAAiB;KACuB,IAAI,MAAM,IAAI;IAAO,GAAG,QAAQ;IAC5F,MAAM,OAAO,YAAY,OAAO;KAE9B,SAAS,IAAI,IAAI,SAAS,CAAC,KAAK,KAAK;KACrC,KAAK,qBAAqB,OAAO,WAAW;IAC9C,CAAC;IACD,OAAO;KACL,MAAM,OAAO;KACb,eAAe,OAAO;KACtB,aAAa;KACb,QAAQ,aAAa,OAAO,IAAI;KAChC;IACF;GACF;EACF;EAEA,OAAO;GAAE;GAAM;GAAe;GAAa;GAAQ;EAAS;CAC9D;CACA,UAAU,UAAU,CAClB,aAAW,YAAY,KAAK,QAAQ,MAAM,EAAE,WAAW,GACvD,aAAW,aAAa,IAAI,SAAS,KAAK,MAAM,MAAM,KAAK,CAAC,CAAC,UAAU,WAAW,IAAI,CACxF;AACF,CAAC;;;;;;AAOD,IAAM,gBAAgB,OAAoB,gBACxC,WAAW,IACT,qBAAqB,WAAW,CAAC,CAAC,KAAK,UACrC,MAAM,MAAM,KAAK,QACb,MAAM,MAAM,MAAM,KAAK,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,KAAK,IAAI,MAAM,IAAI,QAAQ,MAAM,KAAK,CAAC,CAAC,IACvF,MAAM,MAAM,MAAM,MAAM,MAAM,MAAM,EAAE,CAC5C,GACA,IACF;;;;;;AAWF,IAAM,2BAA2B;CAC/B,IAAI,OAAO;CACX,OAAO,aAAW,iBAAiB,EACjC,SAAS,QAAQ,SAAS;EACxB,IAAI,CAAC,KAAK,MAAM,MAAM,mBAAmB,CAAC,CAAC,MAAM,YAAY,QAAQ,KAAK,GACxE;EAEF,MAAM,MAAM,KAAK,UAAU;EAC3B,IAAI,QAAQ,KAAK,KAAK,IAAI,MAAM,IAAI,IAAI,KACtC,IAAI,KAAK,wBAAwB;GAC/B,MAAM,KAAK,MAAM,IAAI;GACrB,IAAI,KAAK,MAAM,GAAG;GAClB,OAAO,KAAK,MAAM,MAAM,IAAI;EAC9B,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,KAAA;EAAA,CAAC;EAEH,OAAO;CACT,EACF,CAAC;AACH,EAAA,CAAG;;;;;;;AAQH,IAAM,wBAAwB,WAAW,UACvC,MAAM;CACJ,oBAAoB;CAEpB,OAAO,QAAoB;EACzB,IAAI,OAAO,YACT,KAAK,oBAAoB;EAE3B,MAAM,SAAS,OAAO,MAAM,IAAI;EAChC,IAAI,KAAK,sBAAsB,UAAU,oBAAoB,OAAO,OAAO,MAAM,GAAG;GAClF,KAAK,oBAAoB;GAEzB,qBAAqB,OAAO,KAAK,SAAS,EAAE,SAAS,oBAAoB,GAAG,IAAI,EAAE,CAAC,CAAC;EACtF;CACF;AACF,CACF;;;;AAKA,IAAM,cAAc,KAAK,QACvB,SAAO,GAAG,CACR;CACE,KAAK;CACL,MAAM,SAAS;EACb,IAAI,CAAC,KAAK,MAAM,MAAM,eAAe,CAAC,CAAC,WAAW,QAChD,OAAO;EAET,KAAK,SAAS,EAAE,SAAS,uBAAuB,GAAG,EAAE,CAAC;EACtD,OAAO;CACT;AACF,GACA;CACE,KAAK;CACL,MAAM,SAAS;EACb,IAAI,CAAC,KAAK,MAAM,MAAM,eAAe,CAAC,CAAC,WAAW,QAChD,OAAO;EAET,KAAK,SAAS,EAAE,SAAS,mBAAmB,GAAG,EAAE,CAAC;EAClD,OAAO;CACT;AACF,CACF,CAAC,CACH;;;;;AAMA,IAAM,yBAAyB,aAAW,eAAe,IAAI,WAAW;CACtE,MAAM,EAAE,cAAc,OAAO,MAAM,MAAM,eAAe;CACxD,OAAO,aAAa,SAAS,gBAAgB;EAC3C,KAAK,MAAM,UAAU,YAAY,SAAS;GACxC,IAAI,OAAO,GAAG,sBAAsB,GAAG;IACrC,MAAM,OAAO,OAAO;IACpB,MAAM,YAAY,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,UAAU,KAAK,IAAI,CAAC,CAAC;IACxE,IAAI,SAA2D;IAC/D,MAAM,EAAE,gBAAgB,KAAK,MAAM,MAAM,sBAAsB;IAC/D,KAAK,MAAM,SAAS,qBAAqB,WAAW,GAClD,IAAI,MAAM,OAAO,WAAW;KAC1B,MAAM,MAAM,MAAM,MAAM,KAAK;KAC7B,IAAI,WAAW,SAAS,GAAG,GACrB;UAAA,CAAC,UAAU,MAAM,OAAO,OAAO,MACjC,SAAS;OAAE,MAAM,MAAM;OAAM,IAAI,MAAM;OAAI;MAAI;KAAA;IAGrD;IAGF,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,QAAQ,QAAQ,CAAC;IACpD,KAAK,SAAS;KACZ,WAAW;MAAE,QAAQ,KAAK;MAAM,MAAM,KAAK;KAAK;KAChD,SAAS,kBAAkB,GAAG;MAAE,MAAM,KAAK,SAAS;MAAG,QAAQ;KAAI,CAAC;IACtE,CAAC;IAED;GACF;GAEA,IAAI,OAAO,GAAG,kBAAkB,GAAG;IACjC,MAAM,OAAO,OAAO;IACpB,MAAM,YAAY,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,UAAU,KAAK,IAAI,CAAC,CAAC;IACxE,IAAI,SAA2D;IAC/D,MAAM,EAAE,gBAAgB,KAAK,MAAM,MAAM,sBAAsB;IAC/D,KAAK,MAAM,SAAS,qBAAqB,WAAW,GAClD,IAAI,MAAM,OAAO,WAAW;KAC1B,MAAM,MAAM,MAAM,MAAM,KAAK;KAC7B,IAAI,WAAW,SAAS,GAAG,GACrB;UAAA,CAAC,UAAU,MAAM,OAAO,OAAO,MACjC,SAAS;OAAE,MAAM,MAAM;OAAM,IAAI,MAAM;OAAI;MAAI;KAAA;IAGrD;IAGF,IAAI,QAAQ;KACV,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,QAAQ,IAAI;KAC/C,KAAK,SAAS;MACZ,WAAW;OAAE,QAAQ,KAAK;OAAI,MAAM,KAAK;MAAG;MAC5C,SAAS,kBAAkB,GAAG;OAAE,MAAM,KAAK,SAAS;OAAG,QAAQ;MAAI,CAAC;KACtE,CAAC;IACH,OAAO;KACL,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,IAAI,MAAM;KACxD,KAAK,SAAS;MACZ,WAAW;OAAE,QAAQ,KAAK;OAAI,MAAM,KAAK;MAAG;MAC5C,SAAS,kBAAkB,GAAG;OAAE,MAAM,KAAK,SAAS;OAAG,UAAU;MAAM,CAAC;KAC1E,CAAC;IACH;IAEA;GACF;EACF;CACF,CAAC;AACH,CAAC;;;;;AAsCD,IAAa,cAAyB;CACpC;CACA;CACA;CApCyB,WAAW,UACpC,MAAM;EACJ,OAAO,QAAoB;GACzB,MAAM,iBAAiB,OAAO,MAAM,MAAM,wBAAwB;GAClE,MAAM,EAAE,aAAa,aAAa,OAAO,MAAM,MAAM,sBAAsB;GAG3E,IAAI,OAAO,YAAY;IACrB,MAAM,0BAAU,IAAI,IAAY;IAChC,KAAK,MAAM,SAAS,qBAAqB,WAAW,GAAG;KACrD,MAAM,SAAS,MAAM,OAAO,MAAM;KAClC,IAAI,kBAAkB,YACpB,QAAQ,IAAI,OAAO,EAAE;IAEzB;IACA,SAAS,UAAU,OAAO;GAC5B;GAIA,KAAK,MAAM,UAAU,OAAO,aAAa,SAAS,OAAO,GAAG,OAAO,GACjE,IAAI,OAAO,GAAG,kBAAkB,GAC9B,SAAS,QAAQ,OAAO,MAAM,IAAI,eAAe,OAAO,MAAM,GAAG;EAGvE;CACF,CAWA;CACA;CACA;CACA;CACA;AACF;;;;;;;AC1wBA,IAAa,iBAAiB,MAAM,OAAwC,EAC1E,UAAU,aAAa,SACzB,CAAC;;AAGD,IAAa,gBAAgB,OAAoB,QAC/C,MAAM,MAAM,cAAc,CAAC,CAAC,MAAM,UAAU,MAAM,GAAG,CAAC;;AAGxD,IAAa,gBACV,aACA,QACC,QAAQ,MAAM,WAAW,IAAI,WAAW,MAAM,CAAC;;AAGnD,IAAa,cACV,WACA,QAAQ;CACP,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,GAAG;CACtB,QAAQ;EACN,OAAO;CACT;CAEA,IAAI,OAAO,aAAa,WAAW,OAAO,aAAa,UACrD,OAAO;CAET,MAAM,EAAE,aAAa;CACrB,OAAO,MAAM,MAAM,SAAS,aAAa,QAAQ,SAAS,SAAS,IAAI,MAAM,CAAC;AAChF;;AAGF,IAAa,gBACV,aACA,QAAQ;CAEP,QAAQ,YAAY;CACpB,OAAO,QAAQ,KAAK,GAAG;AACzB;;;;;;;;;;AAkCF,IAAa,sBACX,MACA,IACA,OACA,EAAE,UAAU,SAAgC,CAAC,MACpC;CACT,MAAM,UAAU,eAAe,KAAK,OAAO,EAAE,KAAK,CAAC;CAEnD,IADa,OAAO,KAAK,KACrB,CAAA,CAAK,OAAO,SAAS,QAAQ,QAAQ,WAAW,MAAM,IAAI,GAC5D;CAEF,KAAK,SAAS,EACZ,SAAS,CACP,mBAAmB,GAAG;EAAE;EAAI,QAAQ,UAAU;GAAE,GAAG;GAAM,GAAG;EAAM;CAAG,CAAC,GACtE,GAAI,UAAU,CAAC,oBAAoB,GAAG,IAAI,CAAC,IAAI,CAAC,CAClD,EACF,CAAC;AACH;AAyBA,SAAgB,YAAY,EAAE,OAAO,MAAM,OAAO,OAAO,WAA0C;CACjG,MAAM,UAAyB;EAK7B,OAAO;GAAC;GAAQ;GAAS;EAAK;EAC9B,OAAO,MAAM,SAAS,OAAO;EAC7B,QAAQ,MAAM,EAAE,OAAO,SAAS,gBAAgB,UAAU,eAAe;GACvE,MAAM,OAAO,KAAK,KAAK,SAAS;GAEhC,IAAI,SAAS,KAAK,KAAK,QAAQ,SAAS,UAAU,KAAK,KAAK,QAAQ,SAAS,UAC3E;GAGF,MAAM,UAAU,OAAO,KAAK,OAAO,KAAK,KAAK,SAAS,KAAK;GAC3D,MAAM,YAAY,OAAO,CAAC,IAAI,KAAK,KAAK,YAAY,UAAU;GAC9D,IAAI,CAAC,WAAY,CAAC,QAAQ,UAAU,SAAS,GAC3C;GAEF,MAAM,MAAM,MAAM,SAAS,QAAQ,MAAM,QAAQ,EAAE;GACnD,IAAI,CAAC,MAAM,GAAG,GACZ;GAEF,MAAM,UAAU,KAAK,KAAK,SAAS;GACnC,MAAM,MAAM,UAAU,QAAQ;GAC9B,IAAI,CAAC,KACH;GAKF,MAAM,QAAQ,OAAO,MAAM,MAAM,SAAS,UAAU,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC,IAAI;GAC5E,IAAI,CAAC,OACH;GAEF,MAAM,QAAQ;IAAE,MAAM,KAAK,KAAK;IAAM,IAAI,KAAK,KAAK;GAAG;GAIvD,MAAM,aAAa,SAAS,IAAI,GAAG,KAAK;GACxC,SAAS,IAAI,KAAK,aAAa,CAAC;GAChC,MAAM,KAAK,UAAU,IAAI,GAAG;GAC5B,MAAM,YAA6B;IACjC;IACA,MAAM,UAAU,UAAU;IAC1B;IACA;IACA;IACA;IACA,OAAO;IACP,SAAS;IACT,GAAG,eAAe;GACpB;GACA,MAAM,QAAQ,UAAU,QAAQ,SAAS,IAAI;GAC7C,MAAM,YAAY,MAAM,SAAS,MAAM,MAAM,MAAM,EAAE;GAErD,IAAI,UAAU,YAAY;IACxB,MAAM,SAAS,aAAa;KAC1B,KAAK;MAAE,GAAG;MAAK,OAAO;MAAO,iBAAiB,KAAA;KAAU;KACxD;KACA;KACA;KACA;IACF,CAAC;IACD,IAAI,CAAC,QACH;IAEF,OAAO;KACL,MAAM,MAAM;KACZ,IAAI,MAAM;KACV,YAAY,WAAW,OAAO;MAAE;MAAQ,MAAM;MAAG,KAAK,MAAM;KAAK,CAAC;IACpE;GACF;GACA,MAAM,SAAS,aAAa;IAC1B,KAAK;KAAE,GAAG;KAAK,OAAO;KAAS,iBAAiB,UAAU,YAAY,KAAA,IAAY,IAAI;IAAgB;IACtG;IACA;IACA;IACA;GACF,CAAC;GACD,IAAI,CAAC,QACH;GAEF,OAAO;IACL,GAAG;IACH,YAAY,WAAW,QAAQ;KAAE;KAAQ,OAAO;KAAS,QAAQ;KAAM,WAAW;KAAM,KAAK,MAAM;IAAK,CAAC;GAC3G;EACF;CACF;CAEA,OAAO;EAAC;EAAa,oBAAoB,GAAG,OAAO;EAAG,eAAe,GAAG,KAAK;CAAC;AAChF;;;;;;AC7NA,IAAa,sBAAsB;CACjC,OAAO,WAAW,UAChB,MAA6B;EAC3B,OAAO,QAAoB;GACzB,MAAM,OAAO,WAAW,OAAO,KAAK;GACpC,MAAM,cAA4B,CAAC;GAEnC,KAAK,MAAM,MAAM,OAAO,cAEtB,QADc,GAAG,WAAW,YAAY,SAChC,GAAR;IAIE,KAAK,SAAS;KACZ,MAAM,UAAU,GAAG;KACnB,IAAI,QAAQ,OACV;KAGF,QAAQ,aAAa,UAAU;MAE7B,IADa,KAAK,aAAa,OAAO,CAClC,CAAA,EAAM,SAAS,cAAc;OAE/B,MAAM,EAAE,SAAS,OAAO,MAAM,IAAI,OAAO,KAAK;OAC9C,IAAI,KAAK,SAAS,GAAG,GACnB,YAAY,KAAK;QAAE,MAAM;QAAO,IAAI;QAAO,QAAQ;OAAI,CAAC;MAE5D;KACF,CAAC;KAED;IACF;IAKA,KAAK,eAAe;KAClB,MAAM,UAAU,GAAG;KACnB,IAAI,QAAQ,OACV;KAGF,QAAQ,aAAa,OAAO,KAAK,OAAO,KAAK,SAAS;MAEpD,MAAM,MAAM,YAAY,OAAO,KAAK,MAAM,SAAS,OAAO,GAAG,CAAC;MAC9D,IAAI,KAAK;OAEP,MAAM,OAAO,KAAK,aAAa,OAAO,EAAE;OAExC,IAAI,kBAAC,IADwB,IAAI;QAAC;QAAQ;QAAY;QAAc;QAAQ;QAAY;OAAK,CACxF,EAAA,CAAiB,IAAI,MAAM,IAAI,GAAG;QACrC,MAAM,eAAe,GAAG,WAAW,SAAS,OAAO,GAAG;QACtD,YAAY,KAAK;SAAE,MAAM;SAAO,IAAI;SAAK,QAAQ,WAAW,KAAK,YAAY;QAAE,CAAC;OAClF;MACF,OAEE,QADa,KAAK,aAAa,OAAO,CAC9B,CAAA,EAAM,MAAd;OACE,KAAK,QAAQ;QAGX,MAAM,QADM,KAAK,SACH,CAAA,CAAI,MAAM,uBAAuB;QAC/C,IAAI,OAAO;SACT,MAAM,GAAG,eAAe;SACxB,YAAY,KAAK;UAAE,MAAM;UAAO,IAAI;UAAK,QAAQ;SAAY,CAAC;QAChE;QACA;OACF;MACF;KAEJ,CAAC;KAED;IACF;GACF;GAIF,IAAI,YAAY,QACd,iBAAiB;IACf,OAAO,KAAK,SACV,OAAO,KAAK,MAAM,OAAO,EACvB,SAAS,YACX,CAAC,CACH;GACF,CAAC;EAEL;CACF,CACF;AACF;AAMA,IAAa,cAAc,KAAU,UAA0B;CAG7D,MAAM,EAAE,MAAM,aAAa;CAC3B,MAAM,GAAG,aAAa,SAAS,MAAM,GAAG;CAExC,IAAI;EADqB;EAAO;EAAQ;EAAO;EAAO;EAAO;CACzD,CAAA,CAAgB,SAAS,SAAS,GACpC,OAAO,KAAK,SAAS,KAAK,IAAI,IAAI;CAGpC,IAAI,CAAC,OACH,QAAQ,gBAAgB,GAAG;CAG7B,OAAO,IAAI,MAAM,IAAI,IAAI;AAC3B;AAEA,IAAa,mBAAmB,QAAqB;CACnD,IAAI,EAAE,UAAU,MAAM,aAAa;CACnC,IAAI,aAAa,WAAW,aAAa,UACvC,WAAW;CAKb,OAAO,KAAK,QAAQ,UAAU,EAAE;CAEhC,OAAO,CAAC,UAAU,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI,KAAK,aAAa,MAAM,WAAW;AACtF;AAEA,IAAM,eAAe,QAAiC;CACpD,MAAM,iBAAiB;EAAC;EAAS;EAAU;EAAW;CAAM;CAC5D,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,GAAG;EACvB,IAAI,CAAC,eAAe,SAAS,IAAI,QAAQ,GACvC;EAGF,OAAO;CACT,SAAS,MAAM;EACb;CACF;AACF;;;;;;AC3HA,IAAa,SAAS,UAAwB,CAAC,MAAiB;CAC9D,OAAO,CACL,WAAW,OAAsB;EAC/B,SAAS,UAAU;GACjB,OAAO,WAAW,IAAI,mBAAiB,OAAO,GAAG,MAAM,IAAI,QAAQ,OAAO,CAAC;EAC7E;EACA,SAAS,OAAsB,OAAoB;GACjD,IAAI,OAAO,OAAO;GAClB,IAAI,KAAK,OAAO;GAChB,IAAI,GAAG,cAAc,GAAG,WAAW;IAEjC,MAAM,SAAS,GAAG,MAAM,UAAU,KAAK;IACvC,MAAM,YAAY,GAAG,QAAQ,OAAO,GAAG,WAAW,UAAU,KAAK,IAAI;IACrE,OAAO,KAAK,IAAI,QAAQ,SAAS;IACjC,KAAK,KAAK,IAAI,QAAQ,SAAS;IAC/B,GAAG,QAAQ,mBAAmB,QAAQ,MAAM,OAAO,QAAQ;KACzD,OAAO,KAAK,IAAI,MAAM,KAAK;KAC3B,KAAK,KAAK,IAAI,IAAI,GAAG;IACvB,CAAC;GACH;GAIA,MAAM,eAAe,GAAG,QAAQ,OAAO,WAAW,GAAG,UAAU,CAAC,CAAC,MAAM;GACvE,MAAM,cAAc,WAAW,GAAG,KAAK,CAAC,CAAC;GACzC,IAAI,cAAc,cAAc;IAC9B,OAAO,KAAK,IAAI,MAAM,YAAY;IAClC,KAAK,KAAK,IAAI,IAAI,WAAW;GAC/B;GAEA,IAAI,OAAO,IACT,OAAO,MAAM,IAAI,GAAG,OAAO;GAI7B,OAAO,GAAG,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC;GACjC,KAAK,GAAG,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC;GAE7B,OAAO,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,OAAO;IAClC,YAAY;IACZ,UAAU;IACV,cAAc;IACd,KAAK,mBAAiB,GAAG,OAAO,MAAM,IAAI,OAAO;GACnD,CAAC;EACH;EACA,UAAU,UAAU,aAAW,YAAY,KAAK,KAAK;CACvD,CAAC,CACH;AACF;AAEA,IAAM,sBAAoB,OAAoB,MAAc,IAAY,UAAwB,CAAC,MAAM;CACrG,MAAM,cAAmC,CAAC;CAC1C,MAAM,SAAS,MAAM,UAAU,KAAK;CACpC,WAAW,KAAK,CAAC,CAAC,QAAQ;EACxB,QAAQ,SAAS;GACf,IAAI,KAAK,SAAS,SAAS;IACzB,MAAM,UAAU,KAAK,KAAK,SAAS,KAAK;IACxC,IAAI,SAAS;KACX,MAAM,OAAO,MAAM,YAAY,SAAS,KAAK,QAAQ,SAAS,KAAK,MAAM,CAAC,MAAM,MAAM,UAAU;KAEhG,MAAM,MAAM,MAAM,SAAS,QAAQ,MAAM,QAAQ,EAAE;KAEnD,IAAI,IAAI,MAAM,cAAc,MAAM,QAAQ,IAAI,MAAM,aAAa,MAAM,MACrE;KAKF,IAAI,aAAa,OAAO,GAAG,KAAK,QAAQ,OAAO;MAAE,MAAM;MAAS;KAAI,CAAC,GACnE;KAGF,aAAa,GAAG;KAChB,YAAY,KACV,WAAW,QAAQ;MACjB,OAAO;MACP,QAAQ,IAAI,YAAY,GAAG;KAC7B,CAAC,CAAC,CAAC,MAAM,OAAO,KAAK,OAAO,KAAK,IAAI,KAAK,EAAE,CAC9C;IACF;GACF;EACF;EACA;EACA;CACF,CAAC;CAED,OAAO;AACT;AAEA,IAAM,4BAAY,IAAI,IAAY;AAElC,IAAM,gBAAgB,QAAgB;CACpC,IAAI,CAAC,UAAU,IAAI,GAAG,GAAG;EACvB,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,MAAM;EACV,UAAU,IAAI,GAAG;CACnB;AACF;AAEA,IAAM,cAAN,cAA0B,WAAW;CACd;CAArB,YAAY,MAAuB;EACjC,MAAM;EADa,KAAA,OAAA;CAErB;CAEA,GAAY,OAAa;EACvB,OAAO,KAAK,SAAS,MAAM;CAC7B;CAEA,MAAe,MAAkB;EAC/B,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,aAAa,OAAO,KAAK,IAAI;EACjC,IAAI,aAAa,SAAS,UAAU;EAGpC,IAFgB,KAAK,MAAM,MAAM,UAE7B,GACF,IAAI,eAAe;GACjB,IAAI,UAAU,IAAI,iBAAiB;GACnC,wBAAwB,GAAG;EAC7B;OACK;GACL,IAAI,UAAU,IAAI,iBAAiB;GACnC,IAAI,eAAe,wBAAwB,GAAG;EAChD;EAEA,IAAI,gBAAgB,aAAa,GAAG;EAEpC,OAAO;CACT;AACF;;;;;AAMA,IAAM,2BAA2B,QAA0B;CACzD,IAAI,IAAI,gBAAgB,KAAK,IAAI,iBAAiB,GAChD,aAAa,GAAG;AAEpB;AAEA,IAAM,gBAAgB,QAA0B;CAC9C,IAAI,MAAM,UAAU;AACtB;;;;ACzJA,IAAa,mBAAmB,aAAW,MAAM;;;;CAI/C,YAAY;EACV,SAAS;EACT,OAAO;EACP,QAAQ;EACR,eAAe;EACf,WAAW;EACX,SAAS;CACX;;;;CAKA,mBAAmB,CAAC;CACpB,mBAAmB;EAGjB,SAAS;EACT,WAAW;EACX,cAAc;EAGd,YAAY;EACZ,aAAa;EAIb,eAAe;EACf,YAAY;CACd;CACA,0BAA0B;EACxB,OAAO;EAGP,WAAW;EAGX,YAAY;CACd;CACA,2BAA2B,EACzB,OAAO,OACT;;;;CAKA,oBAAoB;EAClB,YAAY;EACZ,YAAY;EACZ,aAAa;EACb,QAAQ;CACV;;;;CAKA,UAAU;EACR,YAAY;EACZ,OAAO;EACP,YAAY;CACd;CACA,cAAc;EACZ,YAAY;EACZ,OAAO;CACT;CAIA,qBAAqB;EACnB,YAAY;EACZ,QAAQ;EACR,YAAY;EACZ,UAAU;EACV,YAAY;EACZ,SAAS;EACT,OAAO;EACP,iBAAiB;CACnB;CACA,mBAAmB;EACjB,YAAY;EACZ,QAAQ;EACR,SAAS;EACT,YAAY;EACZ,UAAU;CACZ;CACA,wBAAwB;EACtB,YAAY;EACZ,aAAa;EACb,cAAc;CAChB;CACA,yBAAyB;EACvB,qBAAqB;EACrB,sBAAsB;CACxB;CACA,uBAAuB;EACrB,wBAAwB;EACxB,yBAAyB;CAC3B;;;;CAKA,cAAc;EACZ,SAAS;EACT,OAAO;EACP,QAAQ;CACV;CACA,uBAAuB;EACrB,SAAS;EACT,QAAQ;EACR,WAAW;CACb;;;;CAKA,aAAa;EACX,gBAAgB;EAChB,eAAe;CACjB;CACA,eAAe,EACb,YAAY,IACd;CACA,sBAAsB,EACpB,YAAY,SACd;CACA,kBAAkB;EAChB,SAAS;EACT,cAAc;EACd,cAAc;EACd,YAAY;EACZ,WAAW;EACX,WAAW;EACX,gBAAgB;EAChB,UAAU;EACV,eAAe;EACf,OAAO;EACP,iBAAiB;CACnB;CACA,kBAAkB;EAChB,SAAS;EACT,cAAc;EACd,YAAY;EACZ,WAAW;EACX,eAAe;EACf,iBAAiB;EACjB,oBAAoB;CACtB;;;;CAKA,aAAa;EACX,SAAS;EACT,QAAQ;CACV;CACA,6BAA6B;EAC3B,QAAQ;EACR,WAAW;EACX,cAAc;CAChB;CACA,yBAAyB;EACvB,SAAS;EACT,SAAS;EACT,oBAAoB;EACpB,oBAAoB;CACtB;CACA,yCAAyC,EACvC,SAAS,EACX;CACA,qBAAqB;EACnB,yBAAyB;EACzB,WAAW;EACX,UAAU;EACV,YAAY;EACZ,sBAAsB;EACtB,sBAAsB;EACtB,SAAS;GACP,kBAAkB;GAClB,qBAAqB;EACvB;CACF;AACF,CAAC;;;;;;;AC7KD,IAAa,SAAS,UAAwB,CAAC,MAAiB;CAC9D,OAAO,WAAW,OAA6B;EAC7C,SAAS,UAAU,OAAO,OAAO,OAAO;EACxC,SAAS,GAAyB,OAAoB,OAAO,GAAG,OAAO,OAAO;EAC9E,UAAU,UAAU,aAAW,YAAY,KAAK,KAAK;CACvD,CAAC;AACH;AASA,IAAM,UAAU,OAAoB,aAA2B;CAC7D,MAAM,UAAU,IAAI,gBAA4B;CAChD,MAAM,SAAS,MAAM,UAAU,KAAK;CAEpC,MAAM,SAAkB,CAAC;CACzB,MAAM,iBAAiB,OAAO,OAAO,SAAS;CAI9C,WAAW,KAAK,CAAC,CAAC,QAAQ,EACxB,QAAQ,SAAS;EACf,QAAQ,KAAK,MAAb;GACE,KAAK;IACH,OAAO,KAAK;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK;IAAG,CAAC;IAC5C;GAEF,KAAK;IACH,SAAS,CAAC,CAAC,SAAS,SAAS,MAAM,SAAS,KAAK,MAAM,KAAK,EAAE,CAAC;IAC/D,OAAO;GAET,KAAK;IACH,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,EAAA,CAAG,KAAK,SAAS,MAAM,SAAS,KAAK,MAAM,KAAK,EAAE,CAAC,CAAC;IAC1E,OAAO;EAEX;CACF,EACF,CAAC;CAGD,OAAO,SAAS,UAAU;EACxB,MAAM,UAAU,KAAK,IAAI,MAAM,QAAQ,UAAU,GAAG,IAAI,MAAM,QAAQ,CAAC,EAAA,CAAG,KAAK,QAAQ,IAAI,MAAM,CAAC;EAClG,MAAM,OAAO,QAAkB,CAAC,GAAG,KAAK,GAAG,MAAc,KAAK,IAAI,GAAG,UAAU,IAAI,MAAM,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;EACpG,MAAM,SAAS,MAAM,UAAU,IAAI,MAAM,MAAM;EAC/C,MAAM,OAAO,MAAM,MAAM,IAAI,GAAG;CAClC,CAAC;CAED,OAAO,SAAS,UAAU;EAExB,IADgB,MAAM,YAAY,SAAS,MAAM,QAAQ,SAAS,MAAM,IAGtE,QAAQ,IACN,MAAM,MACN,MAAM,IACN,WAAW,QAAQ;GACjB,OAAO;GACP,QAAQ,IAAI,YAAY,KAAK;EAC/B,CAAC,CACH;OAEA,QAAQ,IACN,MAAM,MACN,MAAM,IACN,WAAW,KAAK,EACd,OAAO,kBACT,CAAC,CACH;CAEJ,CAAC;CAED,OAAO,QAAQ,OAAO;AACxB;;AAGA,IAAM,YAAY,SAA2B;CAK3C,OAJgB,KACb,KAAK,CAAA,CACL,QAAQ,OAAO,EAAE,CAAA,CACjB,QAAQ,cAAc,EAClB,CAAA,CAAQ,MAAM,WAAW,CAAC,CAAC,KAAK,SAAS,KAAK,KAAK,CAAC,CAAC,QAAQ,SAAS,GAAG,CAAC;AACnF;;AAGA,IAAM,qBAAqB,IAAiB,SAAuB;CACjE,MAAM,QAAQ,KAAK,MAAM,+DAA+D;CACxF,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK,SAAS,KAAK,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAAG;EACjE,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,cAAc,KAAK,MAAM,GAAG,EAAE;EACnC,GAAG,YAAY,IAAI;CACrB,OAAO,IAAK,KAAK,WAAW,IAAI,KAAK,KAAK,SAAS,IAAI,KAAO,KAAK,WAAW,IAAI,KAAK,KAAK,SAAS,IAAI,GAAI;EAC3G,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,cAAc,KAAK,MAAM,GAAG,EAAE;EACrC,GAAG,YAAY,MAAM;CACvB,OAAO,IAAK,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,KAAO,KAAK,WAAW,GAAG,KAAK,KAAK,SAAS,GAAG,GAAI;EACvG,MAAM,KAAK,SAAS,cAAc,IAAI;EACtC,GAAG,cAAc,KAAK,MAAM,GAAG,EAAE;EACjC,GAAG,YAAY,EAAE;CACnB,OACE,GAAG,YAAY,SAAS,eAAe,IAAI,CAAC;AAGlD;AAEA,IAAM,cAAN,cAA0B,WAAW;CACd;CAArB,YAAY,QAAwB;EAClC,MAAM;EADa,KAAA,SAAA;CAErB;CAEA,GAAY,OAAa;EACvB,OACE,KAAK,UAAU,KAAK,OAAO,MAAM,MAAM,KAAK,UAAU,MAAM,OAAO,MAAM,KACzE,KAAK,UAAU,KAAK,OAAO,IAAI,MAAM,KAAK,UAAU,MAAM,OAAO,IAAI;CAEzE;CAEA,YAAqB,GAAmB;EACtC,OAAO,CAAC,SAAS,KAAK,EAAE,IAAI;CAC9B;CAEA,MAAe,OAAmB;EAChC,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,MAAM,QAAQ,IAAI,YAAY,SAAS,cAAc,OAAO,CAAC;EAC7D,MAAM,aAAa,SAAS,UAAU;EAGtC,MAAM,KADS,MAAM,YAAY,SAAS,cAAc,OAAO,CACpD,CAAA,CAAO,YAAY,SAAS,cAAc,IAAI,CAAC;EAC1D,KAAK,OAAO,QAAQ,SAAS,SAAS;GACpC,MAAM,KAAK,SAAS,cAAc,IAAI;GACtC,GAAG,aAAa,SAAS,eAAe;GACxC,kBAAkB,GAAG,YAAY,EAAE,GAAG,IAAI;EAC5C,CAAC;EAED,MAAM,OAAO,MAAM,YAAY,SAAS,cAAc,OAAO,CAAC;EAC9D,KAAK,OAAO,MAAM,SAAS,QAAQ;GACjC,MAAM,KAAK,KAAK,YAAY,SAAS,cAAc,IAAI,CAAC;GACxD,IAAI,SAAS,SAAS;IACpB,MAAM,KAAK,SAAS,cAAc,IAAI;IACtC,GAAG,aAAa,SAAS,eAAe;IACxC,kBAAkB,GAAG,YAAY,EAAE,GAAG,IAAI;GAC5C,CAAC;EACH,CAAC;EAED,OAAO;CACT;AACF;;;;;AC1JA,IAAM,YAAY,KAAa,aAAa,UAC1C,WAAW,KAAK;CACd,SAAS;CACT,YAAY;EACV,OAAO,aAAa,gCAAgC;EACpD,MAAM;EACN,KAAK;EACL,QAAQ;EAGR,UAAU;CACZ;AACF,CAAC;;;;;AAiCH,IAAa,oBAAoB,UAA2B,CAAC,MAAM;CACjE,OAAO;EACL,WAAW,UACT,MAAM;GACJ;GACA;GACA;GAEA,YAAY,MAAkB;IAC5B,CAAC,CAAE,MAAM,KAAK,MAAM,YAAY,KAAK,cAAe,iBAAiB,MAAM,SAAS,KAAK,QAAQ;GACnG;GAEA,OAAO,QAAoB;IACzB,IACE,OAAO,cACP,OAAO,mBACP,OAAO,gBACP,OAAO,aAAa,MAAM,OAAO,GAAG,QAAQ,MAAM,WAAW,OAAO,GAAG,WAAW,CAAC,CAAC,KACnF,OAAO,gBAAgB,CAAC,QAAQ,sBACjC;KACA,CAAC,CAAE,MAAM,KAAK,MAAM,YAAY,KAAK,cAAe,iBAClD,OAAO,MACP,SACA,OAAO,KAAK,QACd;KAEA,KAAK,YAAY;IACnB,OAAO,IAAI,OAAO,cAChB,KAAK,eAAe,OAAO,IAAI;GAEnC;GAGA,eAAe,MAAkB;IAC/B,KAAK,YAAY;IACjB,KAAK,gBAAgB,iBAAiB;KACpC,KAAK,SAAS,EAAE,SAAS,YAAY,GAAG,IAAI,EAAE,CAAC;IACjD,GAAG,QAAQ,oBAAoB;GACjC;GAEA,cAAc;IACZ,IAAI,KAAK,eAAe;KACtB,aAAa,KAAK,aAAa;KAC/B,KAAK,gBAAgB,KAAA;IACvB;GACF;GAEA,UAAU;IACR,KAAK,YAAY;GACnB;EACF,GACA,EACE,UAAU,WAAW;GACnB,KAAK,IAAI,aAAW,YAAY,IAAI,SAAS,KAAK,OAAO,MAAM,CAAC,EAAE,QAAQ,WAAW,IAAI,CAAC;GAC1F,aAAW,YAAY,IAAI,SAAS,KAAK,OAAO,MAAM,CAAC,EAAE,cAAc,WAAW,IAAI;GACtF,aAAW,aAAa,IAAI,SAAS,KAAK,OAAO,MAAM,CAAC,EAAE,cAAc,WAAW,IAAI;EACzF,EACF,CACF;EACA,MAAM,EAAE,MAAM,QAAQ,QAAQ,SAAS,CAAC,CAAC,QAAQ,OAAO,IAAI,IAAI,KAAA,EAAU,CAAC;EAC3E,MAAM;EACN,cAAc;EACd;CACF;AACF;;;;;;;AAQA,IAAM,UAAU;CACd,QAAQ;CACR,QAAQ;CACR,aAAa;CACb,cAAc;AAChB;AAMA,IAAM,uBAAN,cAAmC,WAAW;CAC5C,QAAkC;EAChC,MAAM,KAAK,SAAS,cAAc,MAAM;EACxC,GAAG,YAAY;EACf,OAAO;CACT;AACF;AAEA,IAAM,aAAN,cAAyB,WAAW;CAEf;CACA;CAFnB,YACE,KACA,QACA;EACA,MAAM;EAHW,KAAA,MAAA;EACA,KAAA,SAAA;CAGnB;CAEA,GAAY,OAAa;EACvB,OAAO,KAAK,QAAQ,MAAM;CAC5B;CAEA,MAAe,MAAkB;EAC/B,MAAM,KAAK,SAAS,cAAc,MAAM;EACxC,KAAK,OAAO,IAAI,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;EACvC,OAAO;CACT;AACF;AAEA,IAAM,iBAAN,cAA6B,WAAW;CAClB;CAApB,YAAY,UAA2B;EACrC,MAAM;EADY,KAAA,WAAA;CAEpB;CAEA,GAAY,OAAa;EACvB,OAAO,KAAK,aAAa,MAAM;CACjC;CAEA,cAAuB;EACrB,OAAO;CACT;CAEA,MAAe,MAAkB;EAC/B,MAAM,QAAQ,SAAS,cAAc,OAAO;EAC5C,MAAM,YAAY;EAClB,MAAM,OAAO;EACb,MAAM,WAAW;EACjB,MAAM,UAAU,KAAK;EACrB,IAAI,KAAK,MAAM,UACb,MAAM,aAAa,YAAY,MAAM;OAErC,MAAM,eAAe,UAAiB;GAEpC,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC;GACtD,MAAM,OAAO,KAAK,MAAM,SAAS,KAAK,MAAM,KAAK,EAAE;GACnD,MAAM,QAAQ,KAAK,MAAM,oBAAoB;GAC7C,IAAI,OAAO;IACT,MAAM,GAAG,WAAW;IACpB,MAAM,MAAM,KAAK,OAAO,KAAK,QAAQ,OAAO;IAC5C,KAAK,WAAW,YAAY;IAC5B,KAAK,SAAS,EACZ,SAAS;KAAE,MAAM,MAAM;KAAG,IAAI,MAAM;KAAG,QAAQ,KAAK,WAAW,MAAM;IAAI,EAC3E,CAAC;IAED,MAAM,eAAe;GACvB;EACF;EAGF,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,YAAY;EACjB,KAAK,YAAY,KAAK;EACtB,OAAO;CACT;AACF;AAEA,IAAM,aAAN,cAAyB,WAAW;CAEf;CACA;CAFnB,YACE,MACA,WACA;EACA,MAAM;EAHW,KAAA,OAAA;EACA,KAAA,YAAA;CAGnB;CAEA,QAAkC;EAChC,MAAM,KAAK,SAAS,cAAc,MAAM;EACxC,IAAI,KAAK,WACP,GAAG,YAAY,KAAK;EAEtB,GAAG,YAAY,KAAK;EACpB,OAAO;CACT;AACF;AAEA,IAAM,OAAO,WAAW,QAAQ,CAAC,CAAC;AAClC,IAAM,aAAa,WAAW,KAAK,EAAE,OAAO,gBAAgB,CAAC;AAC7D,IAAM,iBAAiB,WAAW,KAAK,EAAE,OAAO,4BAA4B,CAAC;AAC7E,IAAM,sBAAsB,WAAW,KAAK,EAAE,OAAO,+CAA+C,CAAC;AACrG,IAAM,qBAAqB,WAAW,KAAK,EAAE,OAAO,6CAA6C,CAAC;AAClG,IAAM,mBAAmB;AACzB,IAAM,wBAAwB;AAC9B,IAAM,uBAAuB;AAC7B,IAAM,iBAAiB,WAAW,QAAQ,EAAE,QAAQ,IAAI,qBAAqB,EAAE,CAAC;AAChF,IAAM,cAAc,WAAW,QAAQ,EAAE,QAAQ,IAAI,eAAe,IAAI,EAAE,CAAC;AAC3E,IAAM,gBAAgB,WAAW,QAAQ,EAAE,QAAQ,IAAI,eAAe,KAAK,EAAE,CAAC;;;;;AAM9E,IAAM,gBAAgB,SAAqC;CACzD,IAAI,uBAAuB,KAAK,IAAI,GAClC,OAAO;CAET,IAAI,UAAU,KAAK,IAAI,GACrB,OAAO,WAAW;CAEpB,IAAI,KAAK,SAAS,GAAG,GACnB,OAAO,UAAU;AAIrB;AAEA,IAAM,gBAAgB,OAAoB,OAAqC,UAAmB;CAChG,MAAM,EACJ,UACA,WAAW,EACT,MAAM,EAAE,aAER;CACJ,OAAO,SAAS,CAAC,YAAY,QAAQ,MAAM,QAAQ,QAAQ,MAAM;AACnE;AAEA,IAAM,+BAAe,IAAI,IAAI;CAC3B;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAOD,IAAM,oBAAoB,MAAkB,SAA0B,UAAmB;CACvF,MAAM,aAA+D,CAAC;CACtE,MAAM,mBAAqE,CAAC;CAC5E,MAAM,EAAE,UAAU;CAIlB,MAAM,eAA0C,CAAC;CACjD,MAAM,mBAAmB,MAAqB,UAA6C;EACzF,UAAU,QAAQ,GAAA,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,KAAA;GAAA,GAAA,CAAA,aAAA,EAAA;EAAA,CAAC;EACnB,IAAI,QAAQ,aAAa,QAAQ;GAC/B,MAAM,MAAM,aAAa;GACzB,aAAa,SAAS;GACtB,aAAa,KAAK,MAAM,GAAG;GAC3B,aAAa,QAAQ,KAAK;IAAE,MAAM,KAAK;IAAM,MAAM,KAAK;IAAM,IAAI,KAAK;IAAI;IAAO,QAAQ;GAAE;EAC9F,OACE,aAAa,OAAO,KAAK;EAG3B,OAAO,aAAa,MAAM,GAAG,KAAK;CACpC;CAGA,MAAM,aAA+B,CAAC;CACtC,MAAM,aAAa,SAAwB;EACzC,WAAW,KAAK;GAAE,MAAM,KAAK;GAAM,MAAM,KAAK;GAAM,IAAI,KAAK;GAAI,OAAO,WAAW;GAAQ,QAAQ;EAAE,CAAC;CACxG;CACA,MAAM,kBAAkB;EACtB,WAAW,IAAI;CACjB;CACA,MAAM,4BAA4C;EAChD,UAAU,WAAW,QAAK,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,KAAA;GAAA,GAAA,CAAA,qBAAA,EAAA;EAAA,CAAC;EAC3B,OAAO,WAAW,WAAW,SAAS;CACxC;CAGA,MAAM,aAAa,SAAwB;EAEzC,QAAQ,KAAK,MAAb;GAIE,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK;GACL,KAAK,eAAe;IAClB,MAAM,QAAQ,SAAS,KAAK,KAAK,GAAoB;IACrD,MAAM,UAAU,gBAAgB,MAAM,KAAK;IAC3C,IAAI,QAAQ,kBAAkB,SAAS,KAAA,GAAW;KAChD,MAAM,SAAS,QAAQ,QAAQ;KAE/B,IAAI,QACF,OAAO;IAEX;IAGA,IADgB,aAAa,OAAO,MAAM,KACtC,GACF;IAGF,MAAM,OAAO,KAAK,KAAK;IACvB,IAAI,MAAM,SAAS,cAAc;KAC/B,MAAM,EAAE,MAAM,KAAK,MAAM,QAAQ,oBAAoB,CAAC;KAEtD,MAAM,MADO,MAAM,SAAS,KAAK,MAAM,KAAK,EAChC,CAAA,CAAK,MAAM,QAAQ,CAAC,CAAE,EAAE,CAAC;KACrC,IAAI,CAAC,QAAQ,QAAQ,QAAQ,QAAQ,IACnC,iBAAiB,KAAK;MAAE,MAAM,KAAK;MAAM,IAAI,KAAK,OAAO;MAAK,MAAM;KAAK,CAAC;UACrE;MAEL,MAAM,MACJ,QACG,MAAM,OAAO,CAAC,CAAA,CACd,KAAK,UAAU,OAAO,UAAU,CAAC,CAAA,CACjC,KAAK,GAAG,IAAI;MAEjB,IAAI,IAAI,QACN,iBAAiB,KAAK;OACpB,MAAM,KAAK;OACX,IAAI,KAAK,OAAO;OAChB,MAAM,WAAW,QAAQ,EACvB,QAAQ,IAAI,WAAW,KAAK,cAAc,QAAQ,KAAK,CAAC,CAAC,SAAS,EACpE,CAAC;MACH,CAAC;KAEL;IACF;IAEA,OAAO;GACT;GAOA,KAAK;GACL,KAAK;IACH,UAAU,IAAI;IACd;GAGF,KAAK,YAAY;IACf,MAAM,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI;IAGvC,MAAM,OAAO,oBAAoB;IACjC,MAAM,QAAQ,KAAK,SAAS,gBAAA,KAAA;IAC5B,MAAM,UAAU,SAAS,mBAAmB,OAAO,KAAK,SAAS,KAAK,KAAK;IAC3E,IAAI,KAAK,SAAS,KAAK,KAAK,GAE1B,OAAO;IAKT,WAAW,KAAK;KACd,MAAM,KAAK;KACX,IAAI,KAAK;KACT,MAAM,WAAW,KAAK;MACpB,OAAO;MACP,YAAY,EACV,OAAO,iBAAiB,OAAO,oBAAoB,MAAM,KAC3D;KACF,CAAC;IACH,CAAC;IAED;GACF;GAEA,KAAK,YAAY;IACf,MAAM,OAAO,oBAAoB;IAKjC,IADa,KAAK,QAAQ,KAAK,KAAK,GAAG,CACnC,CAAA,EAAM,SAAS,cACjB;IAIF,MAAM,QAAQ,KAAK,SAAS,gBAAgB,GAAG,EAAE,KAAK,OAAO,KAAK,QAAQ;IAC1E,MAAM,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI;IACvC,MAAM,KAAK,MAAM,IAAI,YAAY,KAAK,IAAI,KAAK,KAAK,CAAC,MAAM,MAAM,KAAK,KAAK,IAAI,KAAK;IACpF,iBAAiB,KAAK;KACpB,MAAM,KAAK;KACX;KACA,MAAM,WAAW,QAAQ,EACvB,QAAQ,IAAI,WACV,OACA,KAAK,SAAS,gBAAgB,sCAAsC,kCACtE,EACF,CAAC;IACH,CAAC;IACD;GACF;GAEA,KAAK,cAAc;IACjB,MAAM,UAAU,MAAM,IAAI,YAAY,KAAK,OAAO,GAAG,KAAK,KAAK,CAAC,MAAM;IAEtE,MAAM,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI;IACvC,MAAM,KAAK,MAAM,IAAI,YAAY,KAAK,IAAI,KAAK,KAAK,CAAC,MAAM,MAAM,KAAK,KAAK,IAAI,KAAK;IACpF,iBAAiB,KAAK;KAAE,MAAM,KAAK;KAAM;KAAI,MAAM,UAAU,cAAc;IAAc,CAAC;IAC1F;GACF;GAMA,KAAK,cAAc;IAGjB,MAAM,YAAY,MAAM,IAAI,OAAO,KAAK,IAAI;IAC5C,IAAI,gBAAgB,KAAK,UAAU,IAAI,GACrC;IAGF,MAAM,UAAU,aAAa,OAAO,MAAM,KAAK;IAC/C,MAAM,YAAY,KAAK,KAAK,SAAS,WAAW;IAChD,MAAM,YAAY,KAAK,KAAK,SAAS,WAAW;IAChD,IAAI,CAAC,WAAW,aAAa,WAC3B,iBAAiB,KAAK;KAAE,MAAM,UAAU;KAAM,IAAI,UAAU;KAAM,MAAM;IAAK,CAAC;IAGhF,KAAK,MAAM,SAAS,KAAK,oBAAoB;KAC3C,IAAI,MAAM,KAAK,KAAK,MAClB;KAEF,IAAI,MAAM,OAAO,KAAK,IACpB;KAGF,WAAW,KAAK;MAAE,MAAM,MAAM;MAAM,IAAI,MAAM;MAAM,MAAM;KAAW,CAAC;IACxE;IAEA;GACF;GAMA,KAAK,gBAAgB;IACnB,MAAM,UAAU,aAAa,OAAO,MAAM,KAAK;IAC/C,KAAK,MAAM,SAAS,KAAK,oBAAoB;KAC3C,IAAI,MAAM,KAAK,KAAK,MAClB;KAEF,IAAI,MAAM,OAAO,KAAK,IACpB;KAGF,MAAM,UAAU,MAAM,QAAQ,KAAK;KACnC,MAAM,SAAS,MAAM,MAAM,KAAK,MAAM,cAAc,KAAK,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,EAAE,CAAC;KAEpG,WAAW,KAAK;MACd,MAAM,MAAM;MACZ,IAAI,MAAM;MACV,MAAM,UAAU,wBAAwB,SAAS,uBAAuB;KAC1E,CAAC;KAED,IAAI,CAAC,YAAY,WAAW,SAC1B,iBAAiB,KAAK;MAAE,MAAM,MAAM;MAAM,IAAI,MAAM;MAAI,MAAM;KAAK,CAAC;IAExE;IACA;GACF;GAMA,KAAK;IACH,KAAK,MAAM,SAAS,KAAK,oBAAoB;KAC3C,IAAI,MAAM,KAAK,KAAK,MAClB;KAEF,IAAI,MAAM,OAAO,KAAK,IACpB;KAGF,MAAM,QAAQ,MAAM,QAAQ,KAAK;KACjC,MAAM,OAAO,MAAM,MAAM,KAAK,MAAM,OAAO,KAAK,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,EAAE,CAAC;KAC3F,WAAW,KAAK;MACd,MAAM,MAAM;MACZ,IAAI,MAAM;MACV,MAAM,QAAQ,sBAAsB,OAAO,qBAAqB;KAClE,CAAC;KAGD,IAAI,CADY,aAAa,OAAO,MAAM,KACrC,MAAY,SAAS,OACxB,iBAAiB,KAAK;MAAE,MAAM,MAAM;MAAM,IAAI,MAAM;MAAI,MAAM;KAAK,CAAC;IAExE;IACA,OAAO;GAOT,KAAK,QAAQ;IACX,MAAM,QAAQ,KAAK,KAAK,YAAY,UAAU;IAC9C,MAAM,UAAU,KAAK,KAAK,SAAS,KAAK;IACxC,MAAM,UAAU,aAAa,OAAO,MAAM,KAAK;IAC/C,IAAI,WAAW,MAAM,UAAU,GAAG;KAChC,MAAM,MAAM,MAAM,SAAS,QAAQ,MAAM,QAAQ,EAAE;KACnD,IAAI,aAAa,OAAO,GAAG,KAAK,QAAQ,OAAO;MAAE,MAAM;MAAQ;KAAI,CAAC,GAClE;KAEF,IAAI,CAAC,SACH,iBAAiB,KAAK;MAAE,MAAM,KAAK;MAAM,IAAI,MAAM,EAAE,CAAC;MAAI,MAAM;KAAK,CAAC;KAGxE,WAAW,KAAK;MACd,MAAM,MAAM,EAAE,CAAC;MACf,IAAI,CAAC,WAAW,QAAQ,mBAAmB,KAAK,KAAK,MAAM,EAAE,CAAC;MAC9D,MAAM,SAAS,KAAK,CAAC,CAAC,QAAQ,gBAAgB;KAChD,CAAC;KAED,IAAI,CAAC,SACH,iBAAiB,KAAK;MACpB,MAAM,MAAM,EAAE,CAAC;MACf,IAAI,KAAK;MACT,MAAM,QAAQ,mBACV,WAAW,QAAQ,EACjB,QAAQ,IAAI,WAAW,KAAK,QAAQ,gBAAgB,EACtD,CAAC,IACD;KACN,CAAC;IAEL;IACA;GACF;GAOA,KAAK,OAAO;IAEV,MAAM,SAAS,KAAK,KAAK;IACzB,IAAI,QAAQ,SAAS,UAAU,QAAQ,SAAS,SAC9C;IAMF,MAAM,MAAM,aADC,MAAM,SAAS,KAAK,MAAM,KAAK,EACnB,CAAI;IAC7B,IAAI,CAAC,OAAO,QAAQ,OAAO;KAAE,MAAM;KAAQ;IAAI,CAAC,GAC9C;IAGF,WAAW,KAAK;KACd,MAAM,KAAK;KACX,IAAI,KAAK;KACT,MAAM,WAAW,KAAK;MACpB,SAAS;MACT,YAAY;OACV,OAAO;OACP,MAAM;OACN,KAAK;OACL,QAAQ;OAER,UAAU;MACZ;KACF,CAAC;IACH,CAAC;IAGD,IAAI,QAAQ,SAAS,cAAc,CAAC,aAAa,OAAO,QAAQ,KAAK,GACnE,KAAK,MAAM,QAAQ,OAAO,YAAY,UAAU,GAC9C,iBAAiB,KAAK;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK;KAAI,MAAM;IAAK,CAAC;IAGtE;GACF;GAMA,KAAK;IACH,IAAI,CAAC,aAAa,OAAO,MAAM,KAAK,GAClC,WAAW,KAAK;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK;KAAI,MAAM;IAAe,CAAC;IAExE;GAGF,SACE,IAAI,aAAa,IAAI,KAAK,IAAI,GACxB;QAAA,CAAC,aAAa,OAAO,KAAK,KAAK,QAAS,KAAK,GAC/C,iBAAiB,KAAK;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK;KAAI,MAAM;IAAK,CAAC;GAAA;EAI1E;CACF;CAEA,MAAM,aAAa,SAAwB;EACzC,QAAQ,KAAK,MAAb;GACE,KAAK;GACL,KAAK,eACH,UAAU;EAGd;CACF;CAEA,MAAM,OAAO,WAAW,KAAK;CAC7B,IAAI,QAAQ,kBAAkB,SAAS,KAAA,GACrC,KAAK,MAAM,EAAE,MAAM,QAAQ,KAAK,eAC9B,KAAK,QAAQ;EACX;EACA;EACA,OAAO,cAAc,WAAW,gBAAgB;EAChD,OAAO,cAAc,WAAW,gBAAgB;CAClD,CAAC;MAIH,KAAK,QAAQ;EACX,OAAO,cAAc,WAAW,gBAAgB;EAChD,OAAO,cAAc,WAAW,gBAAgB;CAClD,CAAC;CAIH,MAAM,cAAc,GAAiC,MACnD,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE;CAE9B,WAAW,KAAK,UAAU;CAC1B,iBAAiB,KAAK,UAAU;CAEhC,MAAM,OAAO,IAAI,gBAA4B;CAC7C,KAAK,MAAM,EAAE,MAAM,IAAI,MAAM,OAAO,YAClC,KAAK,IAAI,MAAM,IAAI,CAAC;CAGtB,MAAM,aAAa,IAAI,gBAA4B;CACnD,KAAK,MAAM,EAAE,MAAM,IAAI,UAAU,kBAAkB;EAEjD,IAAI,OAAO,MAAM,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC,WAAW,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC,QACtE;EAEF,WAAW,IAAI,MAAM,IAAI,IAAI;CAC/B;CAEA,OAAO;EACL,MAAM,KAAK,OAAO;EAClB,YAAY,WAAW,OAAO;CAChC;AACF;AAEA,IAAM,cAAc,YAAY,OAAa;;;ACvrB7C,IAAM,mBAAmB,CAAC,QAAQ,OAAO;;;;AAKzC,IAAa,eAAe,EAAE,aAA+B;CAC3D,OAAO,cAAc,MAAM,KAAK,SAAS;EACvC,MAAM,SAAS,WAAW,KAAK,KAAK,CAAC,CAAC,aAAa,KAAK,IAAI;EAC5D,IAAI,OAAO;EACX,KAAK,IAAI,IAAI,GAAG,OAA0B,QAAQ,CAAC,QAAQ,QAAQ,IAAI,GAAG,OAAO,KAAK,QAAQ,KAC5F,OAAO,KAAK,SAAS,SAAS,OAAO;EAGvC,MAAM,MAAM,QAAQ,KAAK,SAAS,KAAK;EACvC,IAAI,CAAC,OAAO,CAAC,MACX,OAAO;EAGT,MAAM,UAAU,KAAK,MAAM,SAAS,IAAI,MAAM,IAAI,EAAE;EACpD,IAAI,iBAAiB,MAAM,WAAW,QAAQ,WAAW,MAAM,CAAC,GAC9D,OAAO;EAGT,OAAO;GACL,KAAK,KAAK;GACV,KAAK,KAAK;GACV,OAAO;GACP,cAAc;IACZ,MAAM,KAAK,SAAS,cAAc,KAAK;IACvC,GAAG,YAAY;IACf,OAAO,IAAI,EAAE,KAAK,QAAQ,GAAG,IAAI;IACjC,OAAO;KAAE,KAAK;KAAI,QAAQ;MAAE,GAAG;MAAG,GAAG;KAAE;IAAE;GAC3C;EACF;CACF,CAAC;AACH;;AAGA,IAAa,mBAAmB,UAA0B,MAAM,QAAQ,UAAU,EAAE,CAAC,CAAC,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;;;;;;;;;AAUhH,IAAa,kBAAkB,MAAkB,YAA0D;CACzG,MAAM,EAAE,UAAU;CAClB,MAAM,UAAwB,CAAC;CAC/B,WAAW,KAAK,CAAC,CAAC,QAAQ,EACxB,QAAQ,SAAS;EACf,IAAI,KAAK,SAAS,QAChB;EAGF,MAAM,QAAQ,KAAK,KAAK,YAAY,UAAU;EAC9C,MAAM,UAAU,KAAK,KAAK,SAAS,KAAK;EACxC,IAAI,CAAC,WAAW,MAAM,SAAS,GAC7B;EAGF,MAAM,QAAQ,QAAQ,MAAM,SAAS,QAAQ,MAAM,QAAQ,EAAE,CAAC;EAC9D,IAAI,UAAU,KAAA,GACZ;EAGF,MAAM,CAAC,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,MAAM,EAAE,CAAC,IAAI;EAC9C,MAAM,UAAU,gBAAgB,KAAK;EAGrC,IAAI,QAAQ,SAAS,KAAK,MAAM,SAAS,MAAM,EAAE,MAAM,SACrD,QAAQ,KAAK;GAAE;GAAM;GAAI,QAAQ;EAAQ,CAAC;CAE9C,EACF,CAAC;CAED,IAAI,QAAQ,WAAW,GACrB,OAAO;CAGT,KAAK,SAAS;EAAE;EAAS,WAAW;CAAY,CAAC;CACjD,OAAO;AACT;AAEA,IAAM,mBAAmB,GACvB,+FACA,cAAc,EAAE,WAAW,aAAa,CAAC,CAC3C;;;;;;ACrFA,IAAa,iBAAiB,EAC5B,gBAAgB,uBAChB,WAAW,qBACY,CAAC,MAAiB;CAEzC,MAAM,SAAS,OAAO,QAAQ,aAAa,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,MAAM;CAEnF,OAAO,CACL,aAAW,aAAa,IAAI,MAAM,MAAM,KAAK,WAAW;EACtD,IAAI,OAAO,WAAW,GACpB,OAAO;EAGT,MAAM,EAAE,UAAU;EAClB,MAAM,YAAY,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC;EACzC,MAAM,iBAAiB,MAAM,IAAI,YAAY,WAAW,IAAI,IAAI;EAEhE,KAAK,MAAM,CAAC,OAAO,WAAW,QAAQ;GACpC,IAAI,CAAC,eAAe,SAAS,KAAK,GAChC;GAGF,MAAM,YAAY,OAAO,MAAM,SAAS;GACxC,IAAI,YAAY,WACd;GAGF,KAAK,SACH,MAAM,OAAO;IACX,SAAS;KAAE,MAAM;KAAW,IAAI;KAAM,QAAQ;IAAO;IACrD,WAAW,EAAE,QAAQ,YAAY,OAAO,OAAO;GACjD,CAAC,CACH;GACA,OAAO;EACT;EAEA,OAAO;CACT,CAAC,GACD,SAAO,GACL,OAAO,QAAQ,QAAQ,CAAC,CAAC,KAAK,CAAC,KAAK,aAAa;EAC/C;EACA,gBAAgB;EAChB,MAAM,SAAS;GACb,KAAK,SAAS,KAAK,MAAM,iBAAiB,MAAM,CAAC;GACjD,OAAO;EACT;CACF,EAAE,CACJ,CACF;AACF;;;;AAKA,IAAa,wBAAsC;CACjD,OAAO;CACP,MAAM;CACN,MAAM;CACN,MAAM;CACN,OAAO;CACP,MAAM;CACN,MAAM;CACN,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;CACP,OAAO;AACT;;;;AAKA,IAAa,mBAAgC,EAC3C,SAAS,IACX;;;;;;AC/EA,IAAa,oBAAoB,EAAE,aAAsC,CAAC,MAAiB;CACzF,OAAO,CACL,yBAAyB,EACvB,YAAY,CACV;EACE,MAAM,YAAY,QAAQ;EAC1B,YAAY,CAEV;GACE,MAAM;GACN,aAAa;EACf,GAGA,GAAG,gBAAgB,QAAQ,CAC7B;CACF,CACF,EACF,CAAC,CACH;AACF;;;;;;;;;;AAWA,IAAM,mBAAmB,aAAgD;CACvE,MAAM,aAAa,OAAO,KAAK,YAAY,CAAC,CAAC;CAC7C,IAAI,WAAW,WAAW,GACxB,OAAO,CAAC;CAGV,MAAM,aAAa,WAAW,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;CAC9D,MAAM,mBAAmB,IAAI,OAAO,UAAU,WAAW,sBAAsB;CAC/E,MAAM,cAAc,IAAI,OAAO,UAAU,WAAW,kBAAkB;CAEtE,OAAO,CACL;EACE,MAAM;EACN,QAAQ;EACR,QAAQ,IAAI,SAAS;GACnB,MAAM,QAAQ,YAAY,KAAK,KAAK,IAAI;GACxC,IAAI,CAAC,OACH,OAAO;GAIT,IAAI,iBAAiB,KAAK,KAAK,IAAI,GAAG;IACpC,MAAM,MAAM,GAAG,YAAY,KAAK,KAAK;IACrC,GAAG,WAAW,GAAG,IAAI,aAAa,GAAG,WAAW,GAAG,CAAC;IACpD,GAAG,SAAS;IACZ,OAAO;GACT;GAIA,IAAI,MAAM,EAAE,CAAC,QAAQ,CAAC,CAAC,SAAS,IAAI,GAClC,OAAO;GAIT,MAAM,WAAW,KADD,MAAM,GACQ;GAC9B,MAAM,QAAQ,GAAG;GAGjB,IAAI,KAAK,KAAK,SAAS,QAAQ,GAAG;IAChC,GAAG,WAAW,GAAG,IAAI,aAAa,OAAO,QAAQ,KAAK,KAAK,MAAM,CAAC;IAClE,GAAG,SAAS;IACZ,OAAO;GACT;GAGA,IAAI,MAAM,GAAG,YAAY,KAAK,KAAK;GACnC,OAAO,GAAG,SAAS,GAAG;IACpB,MAAM,GAAG,YAAY,KAAK,KAAK;IAC/B,IAAI,KAAK,KAAK,SAAS,QAAQ,GAAG;KAChC,GAAG,WAAW,GAAG,IAAI,aAAa,OAAO,GAAG,CAAC;KAC7C,GAAG,SAAS;KACZ,OAAO;IACT;GACF;GAGA,GAAG,WAAW,GAAG,IAAI,aAAa,OAAO,GAAG,CAAC;GAC7C,OAAO;EACT;CACF,CACF;AACF;;;;AAKA,IAAM,eAAe,aAA+C;CAClE,MAAM,aAAa,OAAO,KAAK,YAAY,CAAC,CAAC;CAC7C,MAAM,aAAa,IAAI,OAAO,KAAK,WAAW,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG,EAAE,EAAE;CAElF,OAAO,YAAY,MAAM,UAAU;EACjC,QAAQ,KAAK,MAAb;GAEE,KAAK;GACL,KAAK,cACH,OAAO;GAKT,KAAK,aACH,OAAO,EACL,QAAQ,YAAY,OACtB;GAKF,KAAK,aAAa;IAChB,MAAM,UAAU,MAAM,KAAK,KAAK,MAAM,KAAK,EAAE;IAC7C,IAAI,WAAW,KAAK,OAAO,GACzB,OAAO,EACL,QAAQ,YAAY,OACtB;IAGF,OAAO;GACT;EACF;EAEA,OAAO;CACT,CAAC;AACH;;;;;;;;;;;;;;;;;AC7FA,IAAa,sBAAsB,EACjC,KACA,WACA,gBACA,eACA,cACA,eAC0C;CAC1C,MAAM,oBAAoB,eAAe,WAAW,KAAK,EAAE,OAAO,aAAa,CAAC,IAAI,KAAA;CACpF,MAAM,iBAAiB,WAAW,WAAW,QAAQ,CAAC,CAAC,IAAI,KAAA;CAK3D,MAAM,aAAa,mBAAmB,GAAG;CACzC,MAAM,gBAAgB,IAAI,OAAO,IAAI,WAAW,iBAAiB,GAAG;CACpE,MAAM,cAAc,KAAK,IAAI;CAE7B,MAAM,oBAAoB,SAAoC;EAC5D,MAAM,YAAY,KAAK,MAAM,IAAI;EACjC,MAAM,OAAO,KAAK,MAAM,SAAS,GAAG,SAAS;EAC7C,IAAI,CAAC,KAAK,SAAS,IAAI,KAAK,GAC1B,OAAO,WAAW;EAGpB,MAAM,SAA8B,CAAC;EACrC,cAAc,YAAY;EAC1B,IAAI;EACJ,OAAQ,QAAQ,cAAc,KAAK,IAAI,GAAI;GACzC,MAAM,WAAW,MAAM;GACvB,MAAM,SAAS,WAAW,MAAM,EAAE,CAAC;GACnC,MAAM,YAAY,KAAK,QAAQ,aAAa,MAAM;GAGlD,IAAI,cAAc,IAChB;GAEF,MAAM,UAAU,YAAY,YAAY;GACxC,MAAM,cAAc;GACpB,MAAM,YAAY;GAElB,IAAI,gBAAgB;IAClB,OAAO,KAAK,eAAe,MAAM,UAAU,MAAM,CAAC;IAClD,OAAO,KAAK,eAAe,MAAM,WAAW,OAAO,CAAC;GACtD;GAEA,IAAI,qBAAqB,cAAc,WACrC,OAAO,KAAK,kBAAkB,MAAM,aAAa,SAAS,CAAC;GAG7D,IAAI,aAAa,kBAAkB,eAAe;IAGhD,MAAM,QAAQ,CAAC;IACf,IAAI,MAAM;IACV,OAAO,MAAM,aAAa,OAAO,WAAW;KAC1C,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,GAAG;KAGtC,IAAI,KAAK,KAAK,eAAe,KAAK,OAAO,WACvC,MAAM,KAAK,IAAI;KAEjB,IAAI,KAAK,MAAM,WACb;KAEF,MAAM,KAAK,KAAK;IAClB;IAEA,MAAM,SAAS,MAAM,UAAU;KAC7B,MAAM,MAAM;MAAC;MAAW,UAAU,KAAK;MAAgB,UAAU,MAAM,SAAS,KAAK;KAAa,CAAA,CAC/F,OAAO,OAAO,CAAA,CACd,KAAK,GAAG;KACX,IAAI,KACF,OAAO,KAAK,WAAW,KAAK,EAAE,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC;IAEhE,CAAC;GACH;GAGA,cAAc,YAAY;EAC5B;EAEA,OAAO,WAAW,IAAI,QAAQ,IAAI;CACpC;CAEA,OAAO,WAAW,UAChB,MAAM;EACJ;EAEA,YAAY,MAAkB;GAC5B,KAAK,cAAc,iBAAiB,IAAI;EAC1C;EAEA,OAAO,QAAoB;GACzB,IAAI,OAAO,YACT,KAAK,cAAc,iBAAiB,OAAO,IAAI;EAEnD;CACF,GACA,EACE,cAAc,aAAa,SAAS,YACtC,CACF;AACF;;;;;;;;;;;;ACtIA,IAAa,iBAAiB,EAAE,SAA+B,CAAC,MAAiB;CAC/E,MAAM,UAAU,QAAQ,KAAK,SAAS,IAAI,IAAI,IAAI,IAAI,IAAI,KAAA;CAE1D,MAAM,oBAAoB,SAAoC;EAC5D,MAAM,OAAO,KAAK,MAAM,SAAS,GAAG,KAAK,MAAM,IAAI,MAAM;EACzD,IAAI,CAAC,KAAK,SAAS,GAAG,GACpB,OAAO,WAAW;EAGpB,MAAM,aAAa,SAAuC,KAAK,MAAM,KAAK,MAAM,KAAK,EAAE;EAIvF,MAAM,aAAa,YAAiF;GAClG,MAAM,cAAc,QAAQ,SAAS,SAAS;GAC9C,IAAI,CAAC,aACH;GAEF,MAAM,QAAQ,UAAU,WAAW;GACnC,OAAO,eAAe,KAAK,KAAK,IAAI,QAAQ,KAAA;EAC9C;EAEA,MAAM,OAAO,YAAY,OAAO,MAAM,IAAI;EAC1C,MAAM,SAA8B,CAAC;EACrC,KAAK,QAAQ,EACX,QAAQ,SAAS;GACf,MAAM,OAAO,KAAK,KAAK;GACvB,IAAI,SAAS,oBAAoB,KAAK,OAAO,KAAK,IAAI;IACpD,MAAM,UAAU,UAAU,KAAK,IAAI;IACnC,IAAI,CAAC,SACH;IAEF,IAAI,SAAS,IAAI,OAAO,GACtB,OAAO;IAGT,OAAO,KAAK,eAAe,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC;IACpD,OAAO,KAAK,WAAW,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC;IAChD;GACF;GACA,IAAI,SAAS,aAAa,KAAK,OAAO,KAAK,IAAI;IAC7C,MAAM,UAAU,KAAK,KAAK,SAAS,SAAS;IAI5C,MAAM,WAAW,KAAK,KAAK,SAAS,UAAU;IAC9C,MAAM,UAAU,UAAU,UAAU,OAAO,IAAI,KAAA;IAC/C,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,SAC5B;IAEF,IAAI,SAAS,IAAI,OAAO,GAEtB,OAAO;IAET,OAAO,KAAK,eAAe,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC;IACpD,OAAO,KAAK,WAAW,MAAM,QAAQ,MAAM,QAAQ,EAAE,CAAC;IACtD,OAAO,KAAK,WAAW,MAAM,SAAS,MAAM,SAAS,EAAE,CAAC;IACxD,IAAI,QAAQ,KAAK,SAAS,MACxB,OAAO,KAAK,eAAe,MAAM,QAAQ,IAAI,SAAS,IAAI,CAAC;GAE/D;EACF,EACF,CAAC;EACD,OAAO,WAAW,IAAI,QAAQ,IAAI;CACpC;CAEA,OAAO,CACL,WAAW,UACT,MAAM;EACJ;EAEA,YAAY,MAAkB;GAC5B,KAAK,cAAc,iBAAiB,IAAI;EAC1C;EAEA,OAAO,QAAoB;GACzB,IAAI,OAAO,YACT,KAAK,cAAc,iBAAiB,OAAO,IAAI;EAEnD;CACF,GACA,EACE,cAAc,aAAa,SAAS,YACtC,CACF,GAEA,QACF;AACF;;;;AAKA,IAAM,iBAAiB;AAEvB,IAAM,iBAAiB,WAAW,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAClE,IAAM,aAAa,WAAW,KAAK,EAAE,OAAO,aAAa,CAAC;AAC1D,IAAM,iBAAiB,WAAW,KAAK,EAAE,OAAO,iBAAiB,CAAC;AAElE,IAAM,WAAS,aAAW,UAAU;CAClC,mBAAmB;EACjB,iBAAiB;EACjB,cAAc;EACd,SAAS;CACX;CACA,eAAe;EACb,OAAO;EACP,YAAY;CACd;CACA,mBAAmB,CAAC;AACtB,CAAC;;;;;;;AChID,IAAa,eAAb,cAAkC,WAAW;CAEhC;CACA;CAEA;CAEA;CAEA;CARX,YACE,QACA,MAEA,UAEA,eAEA,OACA;EACA,MAAM;EATG,KAAA,SAAA;EACA,KAAA,OAAA;EAEA,KAAA,WAAA;EAEA,KAAA,gBAAA;EAEA,KAAA,QAAA;CAGX;CAEA,GAAY,OAAa;EAGvB,OACE,KAAK,SAAS,MAAM,QACpB,KAAK,WAAW,MAAM,UACtB,KAAK,aAAa,MAAM,YACxB,CAAC,CAAC,KAAK,kBAAkB,CAAC,CAAC,MAAM,iBACjC,KAAK,OAAO,SAAS,MAAM,OAAO,QAClC,KAAK,OAAO,eAAe,MAAM,OAAO;CAE5C;CAEA,MAAe,OAAmB;EAChC,MAAM,OAAO,SAAS,cAAc,WAAW;EAC/C,KAAK,UAAU,IAAI,eAAe;EAClC,KAAK,aAAa,OAAO,KAAK,IAAI;EAClC,IAAI,KAAK,UACP,KAAK,aAAa,WAAW,KAAK,QAAQ;EAE5C,IAAI,KAAK,OAAO;GAEd,MAAM,OAAO,KAAK,YAAY,SAAS,cAAc,SAAS,CAAC;GAC/D,KAAK,aAAa,QAAQ,KAAK,MAAM,IAAI;GACzC,KAAK,YAAY;IAAC;IAAsC,QAAQ,CAAC;IAAG,KAAK,MAAM;GAAU,CAAA,CACtF,OAAO,OAAO,CAAA,CACd,KAAK,GAAG;EACb;EAEA,MAAM,QAAQ,KAAK,YAAY,SAAS,eAAe,KAAK,MAAM,CAAC;EACnE,IAAI,KAAK,eACP,KAAU,cAAc,CAAC,CAAC,MAAM,aAAa;GAE3C,IAAI,YAAY,KAAK,aACnB,MAAM,cAAc;EAExB,CAAC;EAEH,OAAO;CACT;AACF;;;;ACvDA,IAAa,qBAAqB,CAAC,QAAQ,OAAO;;;;;AAoBlD,IAAa,eAAe,EAC1B,UAAU,oBACV,SACA,OAAO,EACL,UAAU,EAAE,OAAO,UAAU,IAAI,aAAa,OAAO,KAAK,OAAO,EACnE,GACA,UACsB,CAAC,MACvB,YAA6B;CAC3B,OAAO,aAAa,OAAO;CAC3B;CACA;CAEA,QAAQ,WAAW;EAAE,GAAG;EAAO,KAAK,MAAM;CAAI;AAChD,CAAC;;;;;;;AC7BH,IAAa,cAAc,OAAoB,SAAsC;CACnF,UAAU,KAAK,KAAK,SAAS,WAAW,0BAAuB;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA,KAAA;EAAA,GAAA,CAAA,gCAAA,0BAAA;CAAA,CAAC;CAGhE,MAAM,UAAU,KAAK,KAAK,SAAS,SAAS,KAAK,KAAK,KAAK,SAAS,gBAAgB;CACpF,IAAI,SAAS;EAEX,MAAM,UAAU,QAAQ,SAAS,SAAS;EAC1C,IAAI,CAAC,SACH;EAGF,MAAM,MAAW,EACf,MAAM,MAAM,IAAI,YAAY,QAAQ,MAAM,QAAQ,EAAE,EACtD;EAGA,IAAI,gBAAgB,QAAQ,SAAS,WAAW;EAChD,OAAO,eAAe;GACpB,MAAM,WAAW,cAAc,SAAS,eAAe;GACvD,IAAI,UAAU;IACZ,MAAM,OAAO,MAAM,IAAI,YAAY,SAAS,MAAM,SAAS,EAAE;IAG7D,IAAI,QAAa,KAAA;IACjB,MAAM,YAAY,cAAc,SAAS,gBAAgB;IACzD,IAAI,WAAW;KACb,MAAM,WAAW,MAAM,IAAI,YAAY,UAAU,MAAM,UAAU,EAAE;KACnE,IACG,SAAS,WAAW,IAAG,KAAK,SAAS,SAAS,IAAG,KACjD,SAAS,WAAW,GAAG,KAAK,SAAS,SAAS,GAAG,GAGlD,QAAQ,SAAS,MAAM,GAAG,EAAE;UAE5B,QAAQ;IAEZ;IAEA,IAAI,QAAQ;GACd;GAGA,gBAAgB,cAAc;EAChC;EAGA,IAAI,KAAK,KAAK,SAAS,aAAa,QAAQ,KAAK,SAAS,kBAAkB;GAC1E,MAAM,WAAkB,CAAC;GACzB,IAAI,QAAQ,KAAK,KAAK;GAEtB,MAAM,cAAc,QAAgB;IAClC,IAAI,IAAI,WAAW,GACjB;IAEF,MAAM,OAAO,SAAS,SAAS,SAAS;IACxC,IAAI,OAAO,SAAS,UAClB,SAAS,SAAS,SAAS,KAAK,OAAO;SAEvC,SAAS,KAAK,GAAG;GAErB;GAEA,OAAO,OAAO;IAEZ,IAAI,MAAM,KAAK,SAAS,aAAa,MAAM,KAAK,SAAS,YAAY;KACnE,IAAI,MAAM,KAAK,SAAS,QACtB,WAAW,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,EAAE,CAAC;UACjD,IAAI,MAAM,KAAK,SAAS,qBAAqB,MAAM,KAAK,SAAS,sBACtE,WAAW,gBAAgB,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,EAAE,CAAC,CAAC;UAClE,IAAI,MAAM,KAAK,SAAS,WAAW;MACxC,MAAM,OAAO,WAAW,OAAO,KAAK;MACpC,IAAI,MACF,SAAS,KAAK,IAAI;KAEtB;IACF;IACA,QAAQ,MAAM;GAChB;GAKA,IAAI,SAAS,SAAS,KAAK,OAAO,SAAS,OAAO,UAChD,SAAS,KAAK,SAAS,EAAE,CAAC,UAAU;GAEtC,IAAI,SAAS,SAAS,GAAG;IACvB,MAAM,YAAY,SAAS,SAAS;IACpC,MAAM,OAAO,SAAS;IACtB,IAAI,OAAO,SAAS,UAClB,SAAS,aAAa,KAAK,QAAQ;GAEvC;GACA,MAAM,UAAU,SAAS,QAAQ,UAAU,OAAO,UAAU,YAAY,MAAM,SAAS,CAAC;GAExF,IAAI,QAAQ,SAAS,GACnB,IAAI,WAAW;EAEnB;EAEA,OAAO;CACT;AACF;;;;;AAMA,IAAM,qBAA6C;CACjD,QAAQ;CACR,QAAQ;CACR,SAAS;CACT,UAAU;CACV,UAAU;AACZ;AAEA,IAAM,mBAAmB,QAAwB;CAC/C,MAAM,QAAQ,mBAAmB;CACjC,IAAI,UAAU,KAAA,GACZ,OAAO;CAET,MAAM,UAAU,0BAA0B,KAAK,GAAG;CAClD,IAAI,SAAS;EACX,MAAM,OAAO,SAAS,QAAQ,IAAI,QAAQ,KAAK,KAAK,EAAE;EACtD,IAAI,OAAO,SAAS,IAAI,GACtB,IAAI;GACF,OAAO,OAAO,cAAc,IAAI;EAClC,QAAQ,CAER;CAEJ;CACA,OAAO;AACT;;;;;;;;;;ACnGA,IAAa,mBAAmB,aAAqD;CACnF,MAAM,QAAQ,WAAW;CACzB,OAAO,OAAO,UAAU,WAAW,QAAQ;AAC7C;;;;;;;;AASA,IAAa,mBAAmB,aAAmC;CACjE,MAAM,QAAQ,YAAY,CAAC,EAAA,CACxB,KAAK,UAAW,OAAO,UAAU,WAAW,QAAS,gBAAgB,OAAO,YAAY,CAAC,CAAC,KAAK,EAAI,CAAA,CACnG,KAAK,EAAE;CACV,OAAO,KAAK,SAAS,IAAI,OAAO;AAClC;;;;;;;;;AAeA,IAAa,WAAW,EAAE,WAAW,CAAC,MAAsB,CAAC,MAAiB,CAC5E,aACA,oBAAoB,GAAG,iBAAiB,QAAQ,CAAC,CACnD;AAEA,IAAM,oBAAoB,aAA+C;CAEvE,MAAM,oBAAoB,OAAO,QAAQ,QAAQ,CAAA,CAC9C,QAAQ,GAAG,SAAS,IAAI,SAAS,CAAA,CACjC,KAAK,CAAC,UAAU,IAAI,CAAA,CACpB,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM;CAErC,OAAO;EACL,OAAO,CAAC,SAAS;EACjB,OAAO,OAAO,OAAO,QAAQ,CAAC,CAAC,MAAM,QAAQ,IAAI,KAAK;EACtD,QAAQ,MAAM,EAAE,OAAO,SAAS,gBAAgB,eAAe;GAC7D,MAAM,OAAO,WAAW,OAAO,KAAK,IAAI;GACxC,IAAI,CAAC,MACH;GAEF,MAAM,MAAM,SAAS,KAAK;GAC1B,IAAI,CAAC,KACH;GAGF,IAAI,IAAI,aAAa,CAAC,KAAK,KAAK,SAAS,UAAU,GACjD;GAGF,MAAM,QAAQ;IAAE,MAAM,KAAK,KAAK;IAAM,IAAI,KAAK,KAAK;GAAG;GACvD,MAAM,KAAK,SAAS,KAAK,IAAI,IAAI,YAAY,UAAU,MAAM,SAAS,UAAU,MAAM,KAAK,GAAG,MAAM,IAAI;GAGxG,MAAM,SAAS,aAAa;IAC1B;IACA;IACA;KAJc;KAAI;KAAO;KAAS,GAAG;KAAM,GAAG,eAAe;IAI7D;IACA;IACA,WAAW,MAAM,SAAS,MAAM,MAAM,MAAM,EAAE;GAChD,CAAC;GACD,IAAI,CAAC,QACH;GAEF,OAAO;IACL,GAAG;IACH,YAAY,WAAW,QAAQ;KAAE;KAAQ,OAAO,IAAI;KAAO,QAAQ;KAAM,WAAW;KAAM,KAAK,KAAK;IAAK,CAAC;GAC5G;EACF;EACA,OAAO,EAAE,OAAO,OAAO,SAAS,gBAAgB,eAC9C,kBAAkB,SAAS,IACvB,mBAAmB,OAAO,OAAO,UAAU,mBAAmB;GAAE;GAAS;GAAgB;EAAS,CAAC,IACnG,KAAA;CACR;AACF;;;;;AAMA,IAAM,sBACJ,OACA,OACA,UACA,mBACA,EAAE,SAAS,gBAAgB,eACC;CAC5B,MAAM,WAAW,MAAM,SAAS,MAAM,MAAM,MAAM,EAAE;CACpD,MAAM,mBAAmB,kBAAkB,IAAI,kBAAkB,CAAC,CAAC,KAAK,GAAG;CAC3E,MAAM,aAAa,IAAI,OAAO,KAAK,iBAAiB,gBAAgB,GAAG;CACvE,IAAI;CAEJ,QAAQ,QAAQ,WAAW,KAAK,QAAQ,OAAO,MAAM;EACnD,MAAM,UAAU,MAAM;EACtB,MAAM,WAAW,KAAK,QAAQ;EAC9B,MAAM,YAAY,MAAM,QAAQ,MAAM,EAAE,CAAC;EAGzC,IAAI,SAAS,QAAQ,UAAU,SAAS,MAAM,IAC5C;EAGF,MAAM,eAAe,MAAM,OAAO,MAAM;EACxC,MAAM,cAAc,MAAM,OAAO;EACjC,MAAM,YAAY,MAAM,SAAS,aAAa,MAAM,EAAE,CAAC,CAAC,KAAK;EAE7D,MAAM,MAAM,SAAS;EACrB,MAAM,QAAqB;GACzB,MAAM;GACN;GACA,OAAO;IAAE,MAAM;IAAc,IAAI,MAAM;GAAG;GAC1C,UAAU,YAAY,CAAC,SAAS,IAAI,KAAA;EACtC;EAGA,MAAM,cAAc;EACpB,IAAI;EACJ,QAAQ,YAAY,YAAY,KAAK,MAAM,EAAE,OAAO,MAClD,MAAM,UAAU,MAAM,UAAU;EAGlC,MAAM,KAAK,SAAS,MAAM,IAAI,UAAU,cAAc;EAEtD,MAAM,SAAiC,aAAa;GAAE;GAAK;GAAI,OAAO;IADhD,GAAG;IAAO;IAAI,GAAG,eAAe;GACgB;GAAa;GAAU,WAAW;EAAK,CAAC;EAO9G,OAAO;GACL,MAAM;GACN,IAAI,MAAM;GACV,eAAe;GACf,YAAY,WAAW,QAAQ;IAC7B,GAAI,SAAS;KAAE;KAAQ,OAAO,IAAI;IAAM,IAAI,CAAC;IAC7C,QAAQ;IACR,WAAW;IACX,KAAK;IACL,WAAW;IACX;GACF,CAAC;EACH;CACF;CAMA,MAAM,UAAU,oBAAoB,UAAU,iBAAiB;CAC/D,IAAI,YAAY,KAAA,GAAW;EACzB,MAAM,eAAe,MAAM,OAAO,QAAQ;EAC1C,OAAO;GACL,MAAM;GACN,IAAI,MAAM;GACV,eAAe;GACf,YAAY,WAAW,QAAQ;IAC7B,QAAQ;IACR,WAAW;IACX,WAAW;IACX,aAAa,MAAM;IAGnB,KAAK,QAAQ;GACf,CAAC;EACH;CACF;AAGF;;;;;;;;AASA,IAAM,uBAAuB,MAAc,aAAuE;CAChH,MAAM,QAAQ,KAAK,YAAY,GAAG;CAClC,IAAI,UAAU,IACZ;CAGF,MAAM,WAAW,KAAK,MAAM,QAAQ,CAAC;CAErC,IAAI,SAAS,SAAS,GAAG,GACvB;CAKF,OAAO,SAAS,SAAS,KAAK,SAAS,MAAM,SAAS,KAAK,WAAW,QAAQ,CAAC,IAC3E;EAAE,MAAM;EAAO;CAAS,IACxB,KAAA;AACN;;;;;;;;;ACnPA,IAAa,kBAAb,cAAqC,aAAa;CAChD;CACA;CACA;CAEA,YAAY,QAAgB,MAAe,YAAqB;EAC9D,MAAM;EACN,KAAK,UAAU;EACf,KAAK,QAAQ;EACb,KAAK,cAAc;CACrB;CAEA,GAAY,OAAiC;EAC3C,OAAO,MAAM,YAAY,KAAK,WAAW,MAAM,UAAU,KAAK,SAAS,MAAM,gBAAgB,KAAK;CACpG;CAEA,QAA8B;EAC5B,MAAM,MAAM,SAAS,cAAc,KAAK;EACxC,IAAI,YAAY;EAEhB,IAAI,MAAM,aAAa,KAAK;EAC5B,IAAI,KAAK,UAAU,KAAA,GACjB,IAAI,MAAM,SAAS,QAAQ,KAAK,MAAM;EAGxC,IAAI,KAAK,aACP,IAAI,MAAM,YAAY,QAAQ,KAAK,YAAY;EAEjD,OAAO;CACT;AACF;AAQA,IAAM,iBAAiB,aAAW,UAAU;CAC1C,2BAA2B;EACzB,OAAO;EACP,eAAe;CACjB;CACA,oBAAoB;EAClB,OAAO;EACP,QAAQ;EACR,cAAc;CAChB;AACF,CAAC;;;;;;;AAQD,IAAM,qBAAqB,MAAM,OAA0B,EACzD,UAAU,SAAS,CACjB,OAAO;CACL,OAAO;CAGP,UAAU,SAAS;EACjB,MAAM,OAAO,KAAK,MAAM,MAAM,IAAI,CAAC,CAAC,KAAK,aAAa,SAAS,KAAK,KAAK,CAAC;EAC1E,OAAO,KAAK,SAAS,SAAS,KAAK,IAAI,IAAI,SAAS;CACtD;AACF,CAAC,GACD,cACF,EACF,CAAC;;AAGD,IAAa,cAAc,aAA2C,mBAAmB,GAAG,QAAQ;;;;;;;AAQpG,IAAa,kBAA6B,mBAAmB,SAAS,SAAS,KAAK;;;;;;;ACFpF,IAAa,iBAAiB,MAAgB,WAA2B,GAAG,OAAO,GAAG,KAAK,QAAQ,GAAG,KAAK;;AAG3G,IAAM,gBAAgB,YAAY,OAAe;;;;;;;;;;;AAqBjD,IAAa,eAAe,EAC1B,SACA,MACA,OACA,gBAAgB,MAChB,UACA,UACA,eACmC;CACnC,MAAM,UAAU,UAAqC;EACnD,MAAM,MAAM,MAAM,IAAI,SAAS;EAI/B,MAAM,+BAAe,IAAI,IAAoB;EAC7C,MAAM,MAAoB,CAAC;EAC3B,KAAK,MAAM,UAAU,SAAS;GAC5B,MAAM,aAAa,OAAO,QAAQ;GAClC,MAAM,SAAS,cAAc;GAC7B,IAAI;GACJ,IAAI,eAAe,KAAA,GAAW;IAC5B,YAAY,aAAa,IAAI,UAAU,KAAK,iBAAiB,YAAY,GAAG;IAC5E,aAAa,IAAI,YAAY,SAAS;GACxC;GAIA,MAAM,UAAU,eAAe,KAAA,IAAY,KAAA,IAAY,IAAI,IAAI,UAAU,YAAY,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC;GACtG,MAAM,WAAW,UAAU,QAAQ,OAAO,OAAO;GACjD,MAAM,UAAU,YAAY,KAAA,IAAY,WAAW,SAAS,QAAQ,SAAS,CAAC,QAAQ,IAAI,QAAQ,IAAI,CAAC,CAAC;GAGxG,MAAM,SAAS,gBAAgB,SAAS,MAAM;GAC9C,MAAM,UAAU,QAAQ,WAAW,QAAQ,QAAQ,KAAK,IAAI;GAC5D,MAAM,QAAQ,cAAc,KAAA,IAAY,UAAU,gBAAgB,WAAW,OAAO;GACpF,KAAK,MAAM,QAAQ,OACjB,IAAI,KAAK;IAAE,GAAG;IAAM,QAAQ,OAAO;IAAQ,QAAQ,OAAO;GAAO,CAAC;EAEtE;EAEA,OAAO,IAAI,MACR,GAAG,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,KAAK,EAAE,SAAS,EAAE,SAAS,IAAI,EACpG;CACF;;;;;;;CAQA,MAAM,WAAW,SAA2B,GAAG,KAAK,QAAQ,QAAQ,KAAK;CAEzE,MAAM,SAAS,OAAoB,cAAoD;EACrF,MAAM,SAAS,CAAC;EAChB,MAAM,UAAwB,CAAC;EAE/B,MAAM,6BAAa,IAAI,IAAoE;EAC3F,KAAK,MAAM,QAAQ,OAAO,KAAK,GAAG;GAChC,IAAI,UAAU,IAAI,cAAc,MAAM,KAAK,MAAM,CAAC,GAChD;GAEF,QAAQ,KAAK,IAAI;GACjB,IAAI,KAAK,KAAK,KAAK,MACjB,OAAO,KAAK,WAAW,MAAM,KAAK,MAAM,KAAK,EAAE,CAAC;GAGlD,IAAI,KAAK,UAAU;IAIjB,MAAM,OAAO,KAAK,OAAO,MAAM,IAAI,SAAS,KAAK;IACjD,OAAO,KAAK,WAAW,OAAO;KAAE,QAAQ,IAAI,iBAAiB,IAAI;KAAG;IAAK,CAAC,CAAC,CAAC,MAAM,KAAK,EAAE,CAAC;GAC5F;GAGA,IAAI,KAAK,QAAQ,KAAK,CAAC,CAAC,WAAW,KAAK,KAAK,SAAS,KAAK,CAAC,CAAC,WAAW,GACtE;GAKF,IAAI,KAAK,YAAY,MAAM,KAAK,SAAS,SAAS,IAAI,GAAG;IACvD,MAAM,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC;IACzC,IAAI,CAAC,WAAW,IAAI,IAAI,GAAG;KACzB,MAAM,UAAU,KAAK,SAAS,SAAS,KAAK,SAAS,QAAQ,QAAQ,EAAE,CAAC,CAAC;KACzE,MAAM,OAAO,KAAK,SAAS,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;KACjE,IAAI,KAAK,SAAS,GAChB,WAAW,IAAI,MAAM;MACnB,QAAQ,KAAK;MACb,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC;MACvB,YAAY,UAAU,IAAI,UAAU,KAAA;KACtC,CAAC;IAEL;IACA;GACF;GAEA,IAAI,MAAM,KAAK;GACf,OAAO,MAAM,KAAK,QAAQ,MAAM,IAAI,YAAY,MAAM,GAAG,GAAG,MAAM,MAChE;GAEF,KAAK,IAAI,OAAO,MAAM,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC,QAAQ,QAAQ,MAAM,IAAI,OAAO,GAAG,CAAC,CAAC,QAAQ,QAAQ;IAChG,MAAM,OAAO,MAAM,IAAI,KAAK,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,WAAW,IAAI,IAAI,GACtB,WAAW,IAAI,MAAM,EAAE,QAAQ,KAAK,OAAO,CAAC;GAEhD;EACF;EACA,MAAM,eAAe,SAAS,GAC5B,CAAC,GAAG,WAAW,QAAQ,CAAC,CAAA,CACrB,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAA,CACxB,KAAK,CAAC,MAAM,EAAE,QAAQ,MAAM,kBAAkB,IAAI,gBAAgB,QAAQ,MAAM,UAAU,CAAC,CAAC,MAAM,IAAI,CAAC,CAC5G;EAEA,OAAO;GAAE;GAAW,aAAa,WAAW,IAAI,QAAQ,IAAI;GAAG,OAAO;GAAS,YAAY;EAAa;CAC1G;CAEA,MAAM,QAAQ,WAAW,OAAwB;EAC/C,SAAS,UAAU,MAAM,uBAAO,IAAI,IAAI,CAAC;EACzC,SAAS,OAAO,gBAAgB;GAC9B,MAAM,YAAY,IAAI,IAAI,MAAM,SAAS;GACzC,IAAI,UAAU;GACd,KAAK,MAAM,UAAU,YAAY,SAC/B,IAAI,OAAO,GAAG,aAAa,GAAG;IAC5B,UAAU,IAAI,OAAO,KAAK;IAC1B,UAAU;GACZ;GAEF,IAAI,CAAC,YAAY,cAAc,CAAC,SAC9B,OAAO;GAET,OAAO,MAAM,YAAY,OAAO,SAAS;EAC3C;EACA,UAAU,SAAS,aAAW,YAAY,KAAK,OAAO,UAAU,MAAM,WAAW;CACnF,CAAC;CAED,OAAO;EACL;EACA,cAAc,OAAO,QAAQ;EAC7B,gBAAgB,eAAe,OAAO,UAAU,QAAQ,IAAI,CAAC;EAC7D,YAAY,UAAU,MAAM,MAAM,KAAK,CAAC,CAAC,UAAU;EACnD;CACF;AACF;;;;;;AAOA,IAAM,iBACJ,OACA,aAEA,aAAW,iBAAiB,EAC1B,YAAY,OAAO,SAAS;CAC1B,IAAI,CAAC,UACH,OAAO;CAET,MAAM,MAAM,KAAK,YAAY;EAAE,GAAG,MAAM;EAAS,GAAG,MAAM;CAAQ,CAAC;CACnE,IAAI,QAAQ,MACV,OAAO;CAET,MAAM,OAAO,KAAK,MACf,MAAM,KAAK,CAAA,CACX,MAAM,MAAM,cACX,UAAU,SAAS,UAAU,KAAK,QAAQ,UAAU,OAAO,OAAO,UAAU,QAAQ,OAAO,UAAU,EACvG;CACF,IAAI,MACF,SAAS,MAAM,KAAK,MAAM;CAE5B,OAAO;AACT,EACF,CAAC;;;;;;;AAQH,IAAM,kBACJ,OACA,UACA,aACc;CAId,MAAM,2BAAW,IAAI,IAAqB;CAC1C,OAAO,cACJ,MAAM,QAAwB;EAG7B,MAAM,OAAO,KAAK,MACf,MAAM,KAAK,CAAA,CACX,MAAM,MAAM,cACX,UAAU,SAAS,UAAU,KAAK,QAAQ,UAAU,OAAO,OAAO,UAAU,QAAQ,OAAO,UAAU,EACvG;EACF,IAAI,CAAC,MACH,OAAO;EAGT,MAAM,MAAM,GAAG,cAAc,MAAM,KAAK,MAAM,EAAE,GAAG,KAAK,KAAK,GAAG,KAAK;EACrE,MAAM,SAAS,SAAS,IAAI,GAAG;EAC/B,IAAI,QACF,OAAO;EAIT,SAAS,MAAM;EACf,MAAM,UAAmB;GACvB,KAAK,KAAK;GACV,KAAK,KAAK;GACV,OAAO;GACP,eAAe,EAAE,KAAK,eAAe,MAAM,MAAM,UAAU,QAAQ,EAAE;EACvE;EACA,SAAS,IAAI,KAAK,OAAO;EACzB,OAAO;CACT,GACA;EACE,WAAW;EAGX,SAAS,gBAAgB,YAAY,cAAc,YAAY,QAAQ,MAAM,WAAW,OAAO,GAAG,aAAa,CAAC;CAClH,CACF;AACF;;AAGA,IAAM,kBACJ,MACA,MACA,UACA,aAEA,OAAO,GAAG,KAAK,CAAA,CACZ,WAAW,qBAAqB,CAAA,CAChC,OAEC,OAAO,GAAG,KAAK,CAAA,CACZ,WAAW,yDAAyD,CAAA,CACpE,WAAW;CAAE,QAAQ;CAAU,aAAa;CAAM,SAAS;AAAgB,CAAC,CAAA,CAC5E,OAAO,OAAO,IAAI,oBAAoB,CAAC,CAAA,CACvC,GAAG,cAAc,UAAU;CAC1B,MAAM,eAAe;CACrB,IAAI,UAAU;EAIZ,KAAK,SAAS,EAAE,SAAS,cAAc,GAAG,cAAc,MAAM,KAAK,MAAM,CAAC,EAAE,CAAC;EAC7E,SAAS,MAAM,KAAK,MAAM;CAC5B,OACE,KAAK,SAAS,EAAE,SAAS;EAAE,MAAM,KAAK;EAAM,IAAI,KAAK;EAAI,QAAQ,KAAK;CAAS,EAAE,CAAC;AAEtF,CAAC,GACH,OAAO,GAAG,KAAK,CAAA,CACZ,WAAW,yDAAyD,CAAA,CACpE,WAAW;CAAE,QAAQ;CAAU,aAAa;CAAM,SAAS;AAAgB,CAAC,CAAA,CAC5E,OAAO,OAAO,IAAI,gBAAgB,CAAC,CAAA,CACnC,GAAG,cAAc,UAAU;CAC1B,MAAM,eAAe;CACrB,KAAK,SAAS,EAAE,SAAS,cAAc,GAAG,cAAc,MAAM,KAAK,MAAM,CAAC,EAAE,CAAC;CAC7E,WAAW,MAAM,KAAK,MAAM;AAC9B,CAAC,CACL,CAAC,CAAC;;;;;AAmBN,IAAa,kBAAkB,EAC7B,UACA,SAAS,6BACT,UACA,eAEA,YAAY;CACV,SAAS,CAAC;EAAE,QAAQ;EAAI;EAAQ,SAAS;CAAS,CAAC;CACnD,UAAU,cAAc,SAAS,SAAS,IAAI;CAC9C,UAAU,cAAc,SAAS,SAAS,IAAI;AAChD,CAAC;;AAGH,IAAM,aAAa,WAAW,KAAK,EAAE,OAAO,oBAAoB,CAAC;;AAGjE,IAAM,mBAAN,cAA+B,WAAW;CACxC;CAEA,YAAY,MAAkB;EAC5B,MAAM;EACN,KAAK,QAAQ;CACf;CAEA,GAAY,OAAkC;EAG5C,OACE,MAAM,MAAM,aAAa,KAAK,MAAM,YACpC,MAAM,MAAM,WAAW,KAAK,MAAM,UAClC,MAAM,MAAM,WAAW,KAAK,MAAM;CAEtC;CAEA,QAA8B;EAK5B,MAAM,WAAW,KAAK,MAAM;EAC5B,MAAM,OAAO,SAAS,QAAQ,QAAQ,EAAE,CAAC,CAAC,QAAQ,QAAQ,EAAE;EAC5D,MAAM,UAAU,SAAS,MAAM,GAAG,SAAS,QAAQ,IAAI,CAAC;EACxD,MAAM,WAAW,KAAK,WAAW,IAAI,KAAK,SAAS,MAAM,QAAQ,SAAS,KAAK,MAAM;EACrF,MAAM,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,WAAW,oBAAoB;EAC9D,IAAI,KAAK,WAAW,GAClB,OAAO,KAAK,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC;EAEvD,IAAI,SACF,KAAK,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,OAAO,CAAC;EAE7C,KAAK,OACH,OAAO,GAAG,MAAM,CAAA,CACb,WAAW,mBAAmB,CAAA,CAE9B,MAAM;GAAE,OAAO,KAAK,MAAM;GAAQ,mBAAmB,KAAK,MAAM;EAAO,CAAC,CAAA,CACxE,KAAK,IAAI,CACd;EACA,IAAI,UACF,KAAK,OAAO,OAAO,GAAG,MAAM,CAAC,CAAC,KAAK,QAAQ,CAAC;EAE9C,OAAO,KAAK;CACd;CAKA,YAAqB,OAAuB;EAC1C,OAAO,EAAE,iBAAiB,cAAc,iBAAiB;CAC3D;AACF;AAEA,IAAM,eAAe,aAAW,UAAU;CAGxC,wBAAwB;EACtB,gBAAgB;EAChB,qBAAqB;EACrB,SAAS;CACX;CAGA,wBAAwB,EACtB,cAAc,wBAChB;CACA,yBAAyB,EACvB,mBAAmB,MACrB;CAGA,0BAA0B;EACxB,SAAS;EACT,KAAK;EACL,SAAS;EACT,cAAc;EACd,YAAY;EACZ,QAAQ;EACR,WAAW;EACX,YAAY;CACd;CAEA,8CAA8C;EAC5C,WAAW;EACX,YAAY;EACZ,cAAc;CAChB;CACA,wBAAwB,EACtB,OAAO,4BACT;CACA,wBAAwB,EACtB,OAAO,0BACT;AACF,CAAC;;;;;;;;;;;ACjeD,IAAa,sBACX,UACA,UACA,aACuB;CACvB,MAAM,cAAc,IAAI,YAAY;CACpC,OAAO;EACL,WAAW,YAAY,GAAG,CAAC,CAAC;EAC5B,cAAc,MAAkB,SAA6B,SAAkB,SAAkB;GAC/F,KAAK,SAAS,EACZ,SAAS,YAAY,YAEnB,WAAW,QAAQ,SAAS,IACxB,YAAY;IAAE;IAAS;IAAM,eAAe;IAAO;IAAU;IAAU;GAAS,CAAC,IACjF,CAAC,CACP,EACF,CAAC;EACH;CACF;AACF;;;;;;;;ACrBA,IAAa,UAAU,YAAY,OAAe;;;;;;;;;AAUlD,IAAa,gBAAgB,EAAE,MAAM,aAA6C;CAGhF,MAAM,aAAa,WAAW,KAAK;EACjC,OAAO;EACP,YAAY,EAAE,OAAO,SAAS,OAAO,yBAAyB,SAAS;CACzE,CAAC;CAKD,IAAI;CAEJ,MAAM,SAAS,MAAc,UAAmC;EAC9D,MAAM,MAAM,MAAM,IAAI,SAAS;EAC/B,IAAI,SAAS,MAAM,SAAS,QAAQ,MAAM,QAAQ,KAChD,OAAO,MAAM;EAEf,MAAM,cAAc,CAAC;EACrB,MAAM,gBAAgB,CAAC;EACvB,MAAM,mBAA6B,CAAC;EAEpC,MAAM,+BAAe,IAAI,IAAY;EACrC,MAAM,aAAa,MAAc,OAAe;GAG9C,IAAI,MAAM;GACV,OAAO,MAAM,QAAQ,MAAM,IAAI,YAAY,MAAM,GAAG,GAAG,MAAM,MAC3D;GAEF,KAAK,IAAI,OAAO,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC,QAAQ,QAAQ,MAAM,IAAI,OAAO,GAAG,CAAC,CAAC,QAAQ,QACnF,aAAa,IAAI,MAAM,IAAI,KAAK,IAAI,CAAC,CAAC,IAAI;EAE9C;EACA,KAAK,MAAM,QAAQ,iBAAiB,MAAM,GAAG,GAAG;GAG9C,IAAI,KAAK,MAAM,KAAK,OAAO;IACzB,MAAM,SAAS,WAAW,OAAO;KAC/B,QAAQ,IAAI,oBAAoB,KAAK,MAAM,KAAK,OAAO,KAAK,GAAG,GAAG,MAAM;KACxE,MAAM;IACR,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK;IACnB,YAAY,KAAK,MAAM;IACvB,cAAc,KAAK,MAAM;IACzB,iBAAiB,KAAK,KAAK,KAAK;IAChC,UAAU,KAAK,OAAO,KAAK,KAAK;GAClC;GAEA,IAAI,KAAK,MAAM,KAAK,OAAO;IACzB,YAAY,KAAK,WAAW,MAAM,KAAK,OAAO,KAAK,GAAG,CAAC;IACvD,UAAU,KAAK,OAAO,KAAK,GAAG;GAChC;EACF;EACA,MAAM,SAAqB;GACzB;GAEA,aAAa,WAAW,IAAI,aAAa,IAAI;GAC7C,QAAQ,WAAW,IAAI,eAAe,IAAI;GAC1C,UAAU;GACV,YAAY,SAAS,GACnB,CAAC,GAAG,YAAY,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,gBAAgB,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,CAC/F;EACF;EACA,QAAQ;GAAE;GAAM;GAAK,OAAO;EAAO;EACnC,OAAO;CACT;CAEA,MAAM,QAAQ,WAAW,OAAmB;EAC1C,SAAS,UAAU,MAAM,MAAM,KAAK;EACpC,SAAS,OAAO,gBAAgB;GAC9B,IAAI,OAAO,MAAM;GACjB,IAAI,cAAc;GAClB,KAAK,MAAM,UAAU,YAAY,SAC/B,IAAI,OAAO,GAAG,OAAO,GAAG;IACtB,OAAO,OAAO;IACd,cAAc;GAChB;GAEF,IAAI,CAAC,YAAY,cAAc,CAAC,aAC9B,OAAO;GAET,OAAO,MAAM,MAAM,YAAY,KAAK;EACtC;EACA,UAAU,SAAS,aAAW,YAAY,KAAK,OAAO,UAAU,MAAM,WAAW;CACnF,CAAC;CAED,OAAO;EACL;EACA,mBAAmB,KAAK;EACxB,mBAAmB,KAAK;EACxB,YAAY,UAAU,MAAM,MAAM,KAAK,CAAC,CAAC,UAAU;EACnD;CACF;AACF;;;;;AAiBA,IAAM,sBAAN,cAAkC,WAAW;CAC3C;CACA;CAEA,YAAY,MAAc,QAAgB;EACxC,MAAM;EACN,KAAK,QAAQ;EACb,KAAK,UAAU;CACjB;CAEA,GAAY,OAAqC;EAC/C,OAAO,MAAM,UAAU,KAAK,SAAS,MAAM,YAAY,KAAK;CAC9D;CAEA,QAA8B;EAC5B,OACE,OAAO,GAAG,MAAM,CAAA,CACb,WAAW,iBAAiB,CAAA,CAC5B,WAAW,EAAE,iBAAiB,QAAQ,CAAC,CAAA,CAEvC,MAAM;GAAE,OAAO,KAAK;GAAS,qBAAqB,KAAK;EAAQ,CAAC,CAAA,CAChE,KAAK,KAAK,KAAK,CAAC,CAAC;CAExB;CAEA,cAAgC;EAC9B,OAAO;CACT;AACF;;;;;;;AAQA,IAAM,sBAAsB,UAC1B,SAAO,GAAG,CACR;CACE,KAAK;CACL,MAAM,SAAS;EACb,MAAM,YAAY,KAAK,MAAM,UAAU;EACvC,OAAO,UAAU,SAAS,KAAK,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,SAAS,UAAU,IAAI;CACpF;AACF,CACF,CAAC;;AAGH,IAAM,sBAAsB,UAC1B,aAAW,aAAa,IAAI,SAAS,KAAK,MAAM,MAAM,OAAO,KAAK,CAAC,EAAE,UAAU,WAAW,IAAI;AAEhG,IAAM,oBAAoB,aAAW,UAAU;CAE7C,sBAAsB;EACpB,gBAAgB;EAChB,yBAAyB;EACzB,qBAAqB;CACvB;CAIA,sBAAsB;EACpB,gBAAgB;EAChB,SAAS;EACT,YAAY;CACd;AACF,CAAC;;;AChND,IAAa,mBAAmB,YAAY,OAAgB;;;;AAK5D,IAAa,kBAAkB,WAAW,OAAgB;CACxD,cAAc;CACd,SAAS,OAAO,OAAO;EACrB,IAAI,WAAW;EACf,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,gBAAgB,GAC5B,WAAW,OAAO;EAItB,OAAO;CACT;AACF,CAAC;;;ACbD,IAAM,gBAAc;AACpB,IAAM,gBAAc;;AA6BpB,IAAa,oBAAoB,YAAY,OAA0B;;AAGvE,IAAa,uBAAuB,YAAY,OAAe;AAE/D,IAAM,sBAAsB,YAC1B,QAAQ,MAAM,WAAW,OAAO,GAAG,iBAAiB,KAAK,OAAO,GAAG,oBAAoB,CAAC;AAE1F,IAAa,sBAAsB,WAAW,OAA0B;CACtE,cAAc,CAAC;CACf,SAAS,OAAO,OAAO;EAErB,IAAI,mBAAmB,GAAG,OAAO,GAAG;GAClC,IAAI,OAAO;GACX,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,iBAAiB,GAC7B,OAAO,CAAC,GAAG,OAAO,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;QACxC,IAAI,OAAO,GAAG,oBAAoB,GAAG;IAC1C,MAAM,MAAM,IAAI,IAAI,IAAI;IACxB,IAAI,IAAI,OAAO,KAAK,IAAI,IAAI,OAAO,OAAO,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK;IACvE,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;GACtC;GAEF,OAAO;EACT;EAEA,IAAI,MAAM,WAAW,GACnB,OAAO;EAKT,IAAI,GAAG,YACL,OAAO,MAAM,KAAK,QAAQ,GAAG,QAAQ,OAAO,KAAK,EAAE,CAAC;EAGtD,OAAO;CACT;AACF,CAAC;;AAGD,IAAa,qBACX,OACA,cACsC;CACtC,MAAM,UAAU,MAAM,MAAM,qBAAqB,KAAK;CACtD,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO,CAAC;CAEV,MAAM,SAAS,IAAI,IAAI,UAAU,KAAK,CAAC,CAAC,KAAK,OAAO,UAAU,CAAC,MAAM,MAAM;EAAE;EAAO;CAAM,CAAC,CAAC,CAAC;CAC7F,MAAM,SAA4C,CAAC;CACnD,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,QAAQ,OAAO,IAAI,MAAM;EAC/B,IAAI,OACF,OAAO,KAAK,KAAK;CAErB;CACA,OAAO;AACT;AAMA,IAAM,yBACJ,MACA,WACA,cACsB;CACtB,MAAM,WAAW,kBAAkB,KAAK,OAAO,SAAS;CACxD,IAAI,SAAS,WAAW,GACtB,OAAO,CAAC;CAGV,MAAM,cAAc,KAAK,WAAW,sBAAsB;CAC1D,MAAM,eAAe,YAAY,OAAO,KAAK,UAAU,sBAAsB,CAAC,CAAC;CAC/E,MAAM,UAA6B,CAAC;CACpC,KAAK,MAAM,EAAE,WAAW,UAAU;EAEhC,MAAM,SAAS,YAAY,UAAU,KAAK,OAAO,KAAK,IAAI;EAC1D,MAAM,OAAO,KAAK,IAAI,OAAO,MAAM,KAAK,SAAS,IAAI;EACrD,MAAM,KAAK,KAAK,IAAI,OAAO,IAAI,KAAK,SAAS,EAAE;EAC/C,IAAI,OAAO,IACT;EAEF,MAAM,YAAY,KAAK,YAAY,IAAI;EACvC,MAAM,WAAW,KAAK,YAAY,EAAE;EACpC,QAAQ,KACN,IAAI,gBACF,oBACA,eAAe,eACf,UAAU,MAAM,eAChB,YAAY,QAAQ,eACpB,SAAS,SAAS,UAAU,MAAM,CACpC,CACF;CACF;CACA,OAAO;AACT;AAEA,IAAM,oBAAoB,GAAgB,MACxC,EAAE,MAAM,qBAAqB,KAAK,MAAM,EAAE,MAAM,qBAAqB,KAAK;AAE5E,IAAM,iBAAiB,aAAW,MAAM;CACtC,0BAA0B,EACxB,eAAe,OACjB;CACA,qBAAqB;EACnB,WAAW;EACX,iBAAiB;CACnB;AACF,CAAC;AAOD,IAAM,qBAAqB,QACzB,aAAW,iBAAiB;CAC1B,OAAO,OAAO,SAAS;EACrB,MAAM,WAAW,kBAAkB,KAAK,OAAO,IAAI,SAAS;EAC5D,IAAI,SAAS,WAAW,GACtB,OAAO;EAET,MAAM,eAAe,QACnB,cACA,IAAI,UACF,KAAK,OACL,SAAS,KAAK,UAAU,MAAM,KAAK,CACrC,CACF;EACA,MAAM,eAAe;EACrB,OAAO;CACT;CACA,MAAM,OAAO,SAAS;EACpB,MAAM,WAAW,kBAAkB,KAAK,OAAO,IAAI,SAAS;EAC5D,IAAI,SAAS,WAAW,GACtB,OAAO;EAET,MAAM,eAAe,QACnB,cACA,IAAI,UACF,KAAK,OACL,SAAS,KAAK,UAAU,MAAM,KAAK,CACrC,CACF;EACA,MAAM,eAAe;EAErB,IAAI,cACF,MACA,SAAS,KAAK,UAAU,MAAM,KAAK,GACnC,IACF;EACA,OAAO;CACT;CACA,QAAQ,OAAO,SAAS;EACtB,MAAM,WAAW,kBAAkB,KAAK,OAAO,IAAI,SAAS;EAC5D,IAAI,SAAS,WAAW,GACtB,OAAO;EAET,MAAM,OAAO,MAAM,eAAe,QAAQ,YAAY,KAAK;EAC3D,MAAM,eAAe;EAErB,IAAI,cACF,MACA,SAAS,KAAK,UAAU,MAAM,KAAK,GACnC,IACF;EACA,OAAO;CACT;AACF,CAAC;AAIH,IAAM,mBAAmB,aAAW,iBAAiB,EACnD,YAAY,OAAO,SAAS;CAC1B,IAAI,MAAM,UACR,OAAO;CAET,MAAM,SAAS,MAAM;CACrB,MAAM,UAAU,KAAK,MAAM,MAAM,qBAAqB,KAAK;CAC3D,IAAI,WAAW,QAAQ,SAAS,KAAK,kBAAkB,QAAQ,KAAK,WAAW,SAAS,MAAM,GAC5F,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,CAAC,CAAC,EAAE,CAAC;CAErD,OAAO;AACT,EACF,CAAC;AAID,IAAM,gBAAgB,KAAK,KACzB,SAAO,GAAG,CACR;CACE,KAAK;CACL,MAAM,SAAS;EACb,MAAM,UAAU,KAAK,MAAM,MAAM,qBAAqB,KAAK;EAC3D,IAAI,CAAC,WAAW,QAAQ,WAAW,GACjC,OAAO;EAET,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,CAAC,CAAC,EAAE,CAAC;EACnD,OAAO;CACT;AACF,CACF,CAAC,CACH;;;;;;;AAQA,IAAa,iCAAiC,WAAkC,cAAuC;CACrH;CACA;CACA,MAAM;EACJ,OAAO;EACP,OAAO;EACP,SAAS,WACP,OAAO,cACP,OAAO,mBACP,OAAO,mBACP,iBAAiB,OAAO,YAAY,OAAO,KAAK;EAClD,UAAU,SAAS,sBAAsB,MAAM,WAAW,SAAS;CACrE,CAAC;AACH;;;;;;AAOA,IAAa,wBAAwB,QAA6B;CAChE;CACA,kBAAkB,GAAG;CACrB;CACA;AACF;;;ACzNA,IAAM,iBAAe,WAA2E;CAC9F,MAAM,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;CACzD,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,OAAO,GAAG,EAAE;EACzB,IAAI,QAAQ,MAAM,QAAQ,KAAK,IAC7B,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,MAAM,EAAE;OAEpC,OAAO,KAAK,EAAE,GAAG,MAAM,CAAC;CAE5B;CACA,OAAO;AACT;AAIA,IAAM,cAAc;AAIpB,IAAM,cAAc;AAGpB,IAAM,iBAAiB;CAAE,GAAG;CAAG,GAAG;AAAE;AAGpC,IAAM,kBAAkB;AAGxB,IAAM,uBAAuB;AAG7B,IAAM,iBAAiB;AAQvB,IAAa,eAAe;AAC5B,IAAa,gBAAgB;AAE7B,IAAM,YAAY;AASlB,IAAM,kBAAkB,YAAY,OAAsB;AAC1D,IAAM,oBAAoB,WAAW,OAAsB;CACzD,cAAc;CACd,SAAS,OAAO,OAAO;EACrB,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,eAAe,GAC3B,OAAO,OAAO;EAGlB,OAAO,SAAS,QAAQ,GAAG,aAAa,GAAG,QAAQ,OAAO,OAAO,EAAE,IAAI;CACzE;AACF,CAAC;AAMD,IAAM,qBAAqB,WAA0C,eACnE,WAAW,UACT,MAAM;CACiB;CAArB,YAAY,MAA2B;EAAlB,KAAA,OAAA;EACnB,KAAK,UAAU,iBAAiB,aAAa,KAAK,OAAO;EACzD,KAAK,UAAU,iBAAiB,cAAc,KAAK,QAAQ;CAC7D;CAEA,UAAU;EACR,KAAK,KAAK,UAAU,oBAAoB,aAAa,KAAK,OAAO;EACjE,KAAK,KAAK,UAAU,oBAAoB,cAAc,KAAK,QAAQ;CACrE;CAEA,KAAK,QAAuB;EAC1B,IAAI,YAAY,KAAK,KAAK,MAAM,MAAM,mBAAmB,KAAK,KAAK,OACjE,KAAK,KAAK,SAAS,EAAE,SAAS,gBAAgB,GAAG,MAAM,EAAE,CAAC;CAE9D;CAEA,WAAW,UAAsB;EAG/B,IAAI,KAAK,KAAK,OAAO,UAAU,CAAC,EAAE,UAChC;EAGF,MAAM,cAAc,KAAK,KAAK,WAAW,sBAAsB;EAC/D,MAAM,MAAM,KAAK,KAAK,YAAY;GAAE,GAAG,YAAY,OAAO,YAAY,QAAQ;GAAG,GAAG,MAAM;EAAQ,CAAC;EACnG,KAAK,KACH,OAAO,OACH,OACC,UAAU,KAAK,KAAK,KAAK,CAAC,CAAC,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO,MAAM,EAAE,CAAC,EAAE,QAAQ,IACjG;CACF;CAEA,iBAAiB,KAAK,KAAK,IAAI;AACjC,CACF;AAIF,IAAM,oBAAoB,OAAoB,cAA4D;CACxG,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,UAAU,MAAM,MAAM,mBAAmB,KAAK;CACpD,IAAI,WAAW,MAAM;EACnB,MAAM,aAAa,OAAO,WAAW,UAAU,MAAM,SAAS,OAAO;EACrE,IAAI,cAAc,GAChB,OAAO;CAEX;CACA,MAAM,OAAO,MAAM,UAAU,KAAK;CAClC,MAAM,cAAc,OAAO,WAAW,UAAU,QAAQ,MAAM,QAAQ,QAAQ,MAAM,EAAE;CACtF,OAAO,eAAe,IAAI,cAAc;AAC1C;AAKA,IAAM,0BACJ,OAAO,GAAG,KAAK,CAAA,CACZ,WAAW,oBAAoB,CAAA,CAC/B,OACC,OAAO,GAAG,KAAK,CAAA,CACZ,WAAW,kEAAkE,CAAA,CAC7E,OAAO,OAAO,GAAG,KAAK,CAAC,CAAC,WAAW,wBAAwB,CAAC,CAAC,OAAO,OAAO,IAAI,SAAS,CAAC,CAAC,CAC/F,CAAC,CAAC;AAmBN,IAAM,cAAc,YAAY,OAAsB;;;;;;AAOtD,IAAM,sBAAN,cAAgC,WAAW;CAE9B;CACA;CACA;CAHX,YACE,aACA,aACA,cACA;EACA,MAAM;EAJG,KAAA,cAAA;EACA,KAAA,cAAA;EACA,KAAA,eAAA;CAGX;CAEA,GAAY,OAAmC;EAC7C,OACE,MAAM,gBAAgB,KAAK,eAC3B,MAAM,gBAAgB,KAAK,eAC3B,MAAM,iBAAiB,KAAK;CAEhC;CAIA,IAAa,kBAA0B;EACrC,OAAO,KAAK;CACd;CAEA,QAA8B;EAC5B,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,YAAY;EACpB,QAAQ,MAAM,SAAS,GAAG,KAAK,YAAY;EAC3C,MAAM,MAAM,QAAQ,YAAY,SAAS,cAAc,KAAK,CAAC;EAC7D,IAAI,YAAY;EAChB,IAAI,MAAM,SAAS,GAAG,KAAK,YAAY;EAGvC,MAAM,QAAQ,KAAK,eAAe,eAAe;EACjD,IAAI,MAAM,aAAa,GAAG,MAAM;EAChC,IAAI,MAAM,QAAQ,eAAe,MAAM;EACvC,OAAO;CACT;AACF;AAEA,IAAM,gBAAgB,WAAW,OAAsB;CACrD,cAAc,WAAW;CACzB,SAAS,OAAO,OAAO;EACrB,IAAI,OAAO,MAAM,IAAI,GAAG,OAAO;EAC/B,KAAK,MAAM,UAAU,GAAG,SACtB,IAAI,OAAO,GAAG,WAAW,GAAG;GAC1B,IAAI,OAAO,SAAS,MAClB,OAAO,WAAW;QACb;IACL,MAAM,EAAE,WAAW,gBAAgB,aAAa,aAAa,iBAAiB,OAAO;IACrF,OAAO,WAAW,IAChB,CAEE,GAAG,UAAU,KAAK,UAAU,WAAW,QAAQ,EAAE,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,MAAM,MAAM,MAAM,EAAE,CAAC,GAE3F,WAAW,OAAO;KAChB,QAAQ,IAAI,oBAAkB,aAAa,aAAa,YAAY;KACpE,OAAO;KACP,MAAM,kBAAkB,GAAG,MAAM,IAAI,SAAS,IAAI;IACpD,CAAC,CAAC,CAAC,MAAM,cAAc,CACzB,GAEA,IACF;GACF;EACF;EAEF,OAAO;CACT;CACA,UAAU,UAAU,aAAW,YAAY,KAAK,KAAK;AACvD,CAAC;;AAGD,IAAM,oBACJ,WACA,YACA,QACA,WACA,oBAAoB,OACpB,kBAEA,WAAW,UACT,MAAM;CA2BiB;CAzBrB,SAA4E;CAG5E,iBAAkC;CAClC,aAA4B;CAG5B,cAAyD;CACzD,mBAA6B,CAAC;CAC9B,kBAAiC;CACjC,WAA+B;CAE/B,kBAAsC;CACtC,iBAAuD;CAGvD,eAAmC;CACnC,cAAoD;CAEpD,aAAwG;CACxG,SAAS;CACT,SAAS;CACT,eAA4D;CAC5D,eAA8B;CAE9B,YAAY,MAA2B;EAAlB,KAAA,OAAA;CAAmB;CAGxC,IAAI,WAAoB;EACtB,OAAO,KAAK,kBAAkB;CAChC;CAGA,IAAI,gBAAiC;EACnC,OAAO,KAAK;CACd;CAIA,OAAO,QAAoB;EACzB,IAAI,KAAK,kBAAkB,OAAO,YAChC,KAAK,MAAM;CAEf;CAEA,UAAU;EAER,KAAK,MAAM,KAAK;CAClB;CAGA,IAAI,OAAe,OAAmB;EACpC,KAAK,SAAS;GAAE;GAAO,UAAU,MAAM;GAAU,GAAG,MAAM;GAAS,GAAG,MAAM;EAAQ;EACpF,OAAO,iBAAiB,aAAa,KAAK,YAAY;EACtD,OAAO,iBAAiB,WAAW,KAAK,UAAU;CACpD;CAEA,UAAU;EACR,OAAO,oBAAoB,aAAa,KAAK,YAAY;EACzD,OAAO,oBAAoB,WAAW,KAAK,UAAU;EACrD,KAAK,SAAS;CAChB;CAEA,gBAAgB,UAAsB;EACpC,IAAI,CAAC,KAAK,QACR;EAEF,IAAI,KAAK,IAAI,MAAM,UAAU,KAAK,OAAO,CAAC,IAAI,KAAK,IAAI,MAAM,UAAU,KAAK,OAAO,CAAC,IAAI,gBAAgB;GACtG,MAAM,EAAE,UAAU,KAAK;GACvB,KAAK,QAAQ;GACb,KAAK,WAAW,OAAO,KAAK;EAC9B;CACF;CAEA,mBAAmB;EACjB,IAAI,KAAK,QAAQ;GACf,MAAM,EAAE,OAAO,aAAa,KAAK;GACjC,KAAK,QAAQ;GACb,KAAK,OAAO,OAAO,QAAQ;EAC7B;CACF;CAOA,OAAO,OAAe,UAAmB;EACvC,MAAM,QAAQ,UAAU,KAAK,KAAK,KAAK,CAAC,CAAC;EACzC,IAAI,CAAC,OACH;EAEF,MAAM,SAAS,MAAM;EACrB,IAAI;EACJ,IAAI,UACF,SAAS,qBAAqB,GAAG,MAAM;OAClC;GACL,MAAM,UAAU,KAAK,KAAK,MAAM,MAAM,qBAAqB,KAAK,KAAK,CAAC;GACtE,MAAM,kBAAkB,QAAQ,WAAW,KAAK,QAAQ,OAAO;GAC/D,SAAS,kBAAkB,GAAG,kBAAkB,CAAC,IAAI,CAAC,MAAM,CAAC;EAC/D;EACA,KAAK,KAAK,SAAS;GAAE,WAAW,EAAE,QAAQ,MAAM,GAAG;GAAG,SAAS;EAAO,CAAC;CACzE;CAKA,WAAW,OAAe,OAAmB;EAC3C,MAAM,WAAW,kBAAkB,KAAK,KAAK,OAAO,SAAS,CAAC,CAAC,KAAK,UAAU,MAAM,KAAK;EACzF,MAAM,UAAU,SAAS,SAAS,KAAK,SAAS,SAAS,KAAK,IAAI,WAAW,CAAC,KAAK;EACnF,IAAI,CAAC,SAAS,SAAS,KAAK,GAAG;GAC7B,MAAM,QAAQ,UAAU,KAAK,KAAK,KAAK,CAAC,CAAC;GACzC,IAAI,OACF,KAAK,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;EAEtE;EACA,KAAK,iBAAiB;EACtB,KAAK,aAAa,QAAQ;EAC1B,KAAK,SAAS,MAAM;EACpB,KAAK,SAAS,MAAM;EACpB,KAAK,eAAe;GAAE,SAAS,MAAM;GAAS,SAAS,MAAM;EAAQ;EACrE,KAAK,aAAa,KAAK,cAAc,QAAQ,EAAE;EAC/C,KAAK,mBAAmB,QAAQ,KAAK,gBAAgB,KAAK,uBAAuB,WAAW,CAAC;EAE7F,KAAK,WAAW,KAAK,cAAc,OAAO;EAC1C,IAAI,KAAK,UAAU;GACjB,KAAK,SAAS,MAAM,YAAY;GAChC,MAAM,OAAO,KAAK,SAAS,sBAAsB;GACjD,KAAK,iBAAiB;IAAE,MAAM,KAAK;IAAM,KAAK,KAAK;GAAI;EACzD;EAGA,KAAK,cAAc,KAAK,mBAAmB,QAAQ,EAAE;EACrD,IAAI,KAAK,aAAa;GACpB,KAAK,eAAe,kBAAkB;GACtC,KAAK,aAAa,UAAU,IAAI,2BAA2B;GAC3D,KAAK,KAAK,IAAI,YAAY,KAAK,YAAY;EAC7C;EACA,KAAK,iBAAiB,MAAM,SAAS,MAAM,OAAO;EAClD,KAAK,gBAAgB,KAAK,UAAU;EACpC,KAAK,KAAK,UAAU,UAAU,IAAI,kBAAkB;EACpD,OAAO,iBAAiB,aAAa,KAAK,OAAO;EACjD,OAAO,iBAAiB,WAAW,KAAK,KAAK;EAC7C,KAAK,eAAe,sBAAsB,KAAK,eAAe;CAChE;CAEA,MAAM,mBAAmB,MAAM;EAC7B,KAAK,QAAQ;EACb,OAAO,oBAAoB,aAAa,KAAK,OAAO;EACpD,OAAO,oBAAoB,WAAW,KAAK,KAAK;EAChD,IAAI,KAAK,gBAAgB,MAAM;GAC7B,qBAAqB,KAAK,YAAY;GACtC,KAAK,eAAe;EACtB;EACA,KAAK,KAAK,UAAU,UAAU,OAAO,kBAAkB;EACvD,IAAI,oBAAoB,KAAK,mBAAmB,MAC9C,KAAK,KAAK,SAAS,EAAE,SAAS,YAAY,GAAG,IAAI,EAAE,CAAC;EAEtD,KAAK,kBAAkB;EACvB,KAAK,mBAAmB,CAAC;EACzB,KAAK,UAAU,OAAO;EACtB,KAAK,WAAW;EAChB,KAAK,kBAAkB;EACvB,KAAK,iBAAiB;EACtB,KAAK,cAAc,OAAO;EAC1B,KAAK,eAAe;EACpB,KAAK,cAAc;EACnB,KAAK,aAAa;EAClB,KAAK,iBAAiB;EACtB,KAAK,aAAa;EAClB,KAAK,cAAc;EACnB,KAAK,eAAe;CACtB;CAIA,UAAU,aAAmC;EAC3C,MAAM,QAAQ,UAAU,KAAK,KAAK,KAAK,CAAC,CAAC;EACzC,IAAI,CAAC,OACH,OAAO;EAET,OAAO,YAAY,UAAU,KAAK,KAAK,OAAO,KAAK,IAAI;CACzD;CAIA,gBAAgB,KAAqB;EACnC,MAAM,OAAO,UAAU,KAAK,KAAK,KAAK,CAAC,CAAC,MAAM,UAAU,MAAM,OAAO,GAAG;EACxE,OAAO,OAAO,KAAK,OAAO,KAAK,KAAK,MAAM,IAAI;CAChD;CAIA,uBAAuB,aAA6B;EAClD,MAAM,SAAS,KAAK,UAAU,WAAW;EACzC,MAAM,YAAY,UAAU,KAAK,UAAU,OAAO,IAAI;EACtD,IAAI,CAAC,UAAU,CAAC,WACd,OAAO;EAET,MAAM,YAAY,KAAK,gBAAgB,OAAO,EAAE;EAChD,IAAI,aAAa,KAAK,KAAK,MAAM,IAAI,QAAQ;GAC3C,MAAM,WAAW,KAAK,UAAU,OAAO,EAAE;GACzC,OAAO,WAAW,SAAS,SAAS,UAAU,MAAM;EACtD;EACA,MAAM,UAAU,KAAK,UAAU,SAAS,CAAC,EAAE;EAC3C,OAAO,WAAW,OAAO,UAAU,UAAU,MAAM;CACrD;CAGA,UAAU,KAA6B;EACrC,MAAM,EAAE,SAAS,KAAK,KAAK,SAAS,GAAG;EAEvC,MAAM,eADU,gBAAgB,UAAU,OAAO,KAAK,cAAA,EACzB,QAAQ,UAAU;EAC/C,OAAO,cAAc,YAAY,sBAAsB,IAAI;CAC7D;CAIA,cAAc,aAML;EACP,MAAM,SAAS,KAAK,UAAU,WAAW;EACzC,IAAI,CAAC,QACH,OAAO;EAGT,MAAM,cAAc,KAAK,KAAK,WAAW,sBAAsB;EAC/D,MAAM,YAAY,KAAK,UAAU,OAAO,IAAI;EAC5C,MAAM,WAAW,KAAK,UAAU,OAAO,EAAE;EACzC,IAAI,aAAa,UAAU;GACzB,MAAM,eAAe,SAAS,SAAS,UAAU;GACjD,OAAO;IACL,MAAM,YAAY,OAAO;IACzB,KAAK,UAAU,MAAM;IACrB,OAAO,YAAY,QAAQ;IAC3B,QAAQ,eAAe;IACvB;GACF;EACF;EAEA,MAAM,YAAY,KAAK,KAAK,YAAY,OAAO,IAAI;EAEnD,MAAM,eADW,KAAK,KAAK,YAAY,OAAO,EACzB,CAAA,CAAS,SAAS,UAAU;EACjD,OAAO;GACL,MAAM,YAAY,OAAO;GACzB,KAAK,YAAY,MAAM,UAAU,MAAM;GACvC,OAAO,YAAY,QAAQ;GAC3B,QAAQ,eAAe;GACvB;EACF;CACF;CAIA,cAAc,eAA6C;EACzD,MAAM,EAAE,UAAU,KAAK;EACvB,IAAI,CAAC,KAAK,YACR,OAAO;EAGT,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,YAAY;EACpB,QAAQ,MAAM,QAAQ,GAAG,KAAK,WAAW,MAAM;EAC/C,IAAI,cAAc,WAAW,GAC3B,QAAQ,MAAM,SAAS,GAAG,KAAK,WAAW,OAAO;EAEnD,QAAQ,MAAM,cAAc,GAAG,YAAY;EAE3C,MAAM,UAAU,SAAS,cAAc,KAAK;EAC5C,QAAQ,YAAY;EACpB,QAAQ,MAAM,QAAQ,GAAG,KAAK,KAAK,WAAW,YAAY;EAC1D,QAAQ,MAAM,YAAY,aAAa,eAAe,EAAE,MAAM,eAAe,EAAE;EAE/E,MAAM,uBAAO,IAAI,IAAa;EAC9B,cAAc,SAAS,aAAa,UAAU;GAC5C,MAAM,SAAS,KAAK,UAAU,WAAW;GACzC,IAAI,CAAC,QACH;GAEF,IAAI,QAAQ,GAAG;IACb,MAAM,SAAS,QAAQ,YAAY,SAAS,cAAc,KAAK,CAAC;IAChE,OAAO,MAAM,SAAS,GAAG,KAAK,KAAK,kBAAkB;GACvD;GACA,MAAM,YAAY,MAAM,IAAI,OAAO,OAAO,IAAI,CAAC,CAAC;GAChD,MAAM,UAAU,MAAM,IAAI,OAAO,OAAO,EAAE,CAAC,CAAC;GAC5C,KAAK,IAAI,aAAa,WAAW,cAAc,SAAS,cAAc;IACpE,MAAM,EAAE,SAAS,KAAK,KAAK,SAAS,MAAM,IAAI,KAAK,UAAU,CAAC,CAAC,IAAI;IAEnE,MAAM,eADU,gBAAgB,UAAU,OAAO,KAAK,cAAA,EACzB,QAAQ,UAAU;IAC/C,IAAI,eAAe,CAAC,KAAK,IAAI,WAAW,GAAG;KACzC,KAAK,IAAI,WAAW;KACpB,QAAQ,YAAY,YAAY,UAAU,IAAI,CAAC;IACjD;GACF;EACF,CAAC;EAED,QAAQ,YAAY,OAAO;EAE3B,KAAK,kBAAkB;EACvB,OAAO,KAAK,KAAK,IAAI,YAAY,OAAO;CAC1C;CAIA,mBAAmB,aAA2D;EAC5E,MAAM,QAAQ,UAAU,KAAK,KAAK,KAAK,CAAC,CAAC;EACzC,MAAM,SAAS,SAAS,KAAK,KAAK,YAAY,MAAM,IAAI;EACxD,IAAI,CAAC,QACH,OAAO;EAGT,OAAO;GACL,MAFkB,KAAK,KAAK,WAAW,sBAEjC,CAAA,CAAY,OAAA;GAClB,MAAM,OAAO,MAAM,OAAO,UAAU;EACtC;CACF;CAEA,iBAAiB,SAAiB,SAAiB;EACjD,MAAM,KAAK,SAAS,IAAI,UAAU,KAAK;EACvC,MAAM,KAAK,UAAU,KAAK;EAC1B,IAAI,KAAK,YAAY,KAAK,kBAAkB,KAAK,YAAY;GAC3D,MAAM,OAAO,KAAK,WAAW,OAAO,KAAK,KAAK,eAAe;GAC7D,MAAM,MAAM,KAAK,WAAW,MAAM,KAAK,KAAK,eAAe;GAC3D,KAAK,SAAS,MAAM,YAAY,aAAa,KAAK,MAAM,IAAI;EAC9D;EAGA,IAAI,KAAK,iBAAiB;GACxB,MAAM,eAAe,KAAK,aAAa,UAAU;GACjD,KAAK,gBAAgB,MAAM,YAAY,aAAa,eAAe,IAAI,aAAa,MAAM,eAAe,EAAE;EAC7G;EACA,IAAI,KAAK,gBAAgB,KAAK,aAAa;GACzC,KAAK,aAAa,MAAM,OAAO,GAAG,KAAK,YAAY,OAAO,GAAG;GAC7D,KAAK,aAAa,MAAM,MAAM,GAAG,KAAK,YAAY,MAAM,GAAG;EAC7D;CACF;CAOA,aAAa,SAAyB;EACpC,MAAM,SAAS,UAAU,KAAK,KAAK,KAAK;EACxC,MAAM,UAAU,IAAI,IAAI,KAAK,kBAAkB,CAAC,CAAC;EACjD,MAAM,UAAU,CAAC,GAAG,OAAO,CAAA,CACxB,KAAK,gBAAgB,KAAK,UAAU,WAAW,CAAC,CAAA,CAChD,QAAQ,WAA4B,UAAU,IAAI;EACrD,MAAM,aAAa,UACjB,QAAQ,MAAM,WAAW,MAAM,QAAQ,OAAO,QAAQ,MAAM,QAAQ,OAAO,EAAE;EAC/E,KAAK,IAAI,QAAQ,GAAG,QAAQ,OAAO,QAAQ,SAAS;GAClD,IAAI,QAAQ,IAAI,KAAK,KAAK,UAAU,OAAO,MAAM,GAC/C;GAEF,MAAM,MAAM,KAAK,KAAK,YAAY,OAAO,MAAM,CAAC,IAAI;GACpD,MAAM,SAAS,KAAK,KAAK,YAAY,OAAO,MAAM,CAAC,EAAE;GACrD,IAAI,CAAC,OAAO,CAAC,QACX;GAEF,IAAI,WAAW,IAAI,MAAM,OAAO,UAAU,GACxC,OAAO;EAEX;EACA,OAAO,OAAO;CAChB;CAEA,WAAW,UAAsB;EAC/B,KAAK,eAAe;GAAE,SAAS,MAAM;GAAS,SAAS,MAAM;EAAQ;EACrE,KAAK,mBAAmB;CAC1B;CAEA,qBAAqB;EACnB,IAAI,CAAC,KAAK,gBAAgB,KAAK,kBAAkB,MAC/C;EAEF,KAAK,aAAa,KAAK,aAAa,KAAK,aAAa,OAAO;EAC7D,KAAK,cACH,gBAAgB,KAAK,MAAM,KAAK,gBAAgB,KAAK,YAAY,KAAK,aAAa,OAAO,KAAK;EACjG,KAAK,gBAAgB,KAAK,UAAU;EACpC,KAAK,iBAAiB,KAAK,aAAa,SAAS,KAAK,aAAa,OAAO;CAC5E;CAEA,iBAAiB,SAAyB;EACxC,MAAM,OAAO,KAAK,KAAK,UAAU,sBAAsB;EACvD,MAAM,UAAU,UAAU,KAAK;EAC/B,MAAM,aAAa,KAAK,SAAS;EACjC,IAAI,UAAU,iBAAiB;GAC7B,MAAM,YAAY,KAAK,IAAI,IAAI,kBAAkB,WAAW,eAAe;GAC3E,OAAO,CAAC,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,oBAAoB,CAAC;EAClE;EACA,IAAI,aAAa,iBAAiB;GAChC,MAAM,YAAY,KAAK,IAAI,IAAI,kBAAkB,cAAc,eAAe;GAC9E,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,YAAY,oBAAoB,CAAC;EACjE;EACA,OAAO;CACT;CAEA,wBAAwB;EACtB,KAAK,eAAe;EACpB,IAAI,KAAK,kBAAkB,QAAQ,CAAC,KAAK,cACvC;EAEF,MAAM,QAAQ,KAAK,iBAAiB,KAAK,aAAa,OAAO;EAC7D,IAAI,UAAU,GAAG;GACf,MAAM,WAAW,KAAK,KAAK;GAC3B,MAAM,SAAS,SAAS;GACxB,SAAS,YAAY,SAAS;GAC9B,IAAI,SAAS,cAAc,QACzB,KAAK,mBAAmB;EAE5B;EACA,KAAK,eAAe,sBAAsB,KAAK,eAAe;CAChE;CAEA,cAAc;EACZ,MAAM,UAAU,KAAK;EACrB,MAAM,OAAO,KAAK;EAClB,MAAM,SAAS,KAAK,aAAa;EACjC,KAAK,MAAM;EACX,IAAI,WAAW,QAAQ,QAAQ,MAC7B,WAAW,KAAK,MAAM,SAAS,MAAM,MAAM;CAE/C;CAIA,gBAAgB,WAAmB;EACjC,MAAM,UAAU,KAAK;EACrB,IAAI,WAAW,MACb;EAEF,MAAM,SAAS,UAAU,KAAK,KAAK,KAAK;EAGxC,MAAM,YAAY,cAChB,QACG,KAAK,gBAAgB;GACpB,MAAM,SAAS,KAAK,UAAU,WAAW;GAGzC,OAAO,SACH;IAAE,MAAM,OAAO;IAAM,IAAI,oBAAoB,OAAO,KAAK,KAAK,gBAAgB,OAAO,EAAE;GAAE,IACzF;EACN,CAAC,CAAA,CACA,QAAQ,UAAiD,SAAS,IAAI,CAC3E;EACA,MAAM,cAAc,KAAK,iBAAiB,QAAQ,KAAK,WAAW,MAAM,QAAQ,CAAC;EACjF,MAAM,cACJ,eAAe,KAAK,aAAa,KAAK,iBAAiB,KAAK,KAAK,WAAW,eAAe;EAC7F,IAAI,eAAe,GACjB;EAOF,IAAI,iBAAiB,YAAY,OAAO,SAAS,OAAO,UAAU,CAAC,OAAO,KAAK,KAAK,MAAM,IAAI;EAC9F,MAAM,aAAa,UAAU,MAAM,UAAU,iBAAiB,MAAM,QAAQ,kBAAkB,MAAM,EAAE;EACtG,IAAI,YACF,iBAAiB,WAAW;EAG9B,MAAM,eAAe,KAAK,aAAa,UAAU;EACjD,MAAM,MAAM,GAAG,eAAe,GAAG,aAAa,GAAG,UAAU,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,KAAK,GAAG;EAC9F,IAAI,QAAQ,KAAK,iBACf;EAGF,KAAK,kBAAkB;EACvB,KAAK,KAAK,SAAS,EACjB,SAAS,YAAY,GAAG;GAAE;GAAW;GAAgB;GAAa;GAAa;EAAa,CAAC,EAC/F,CAAC;CACH;AACF,CACF;AAEF,IAAM,YAAY,aAAW,MAAM;CAKjC,uBAAuB;EACrB,UAAU;EACV,QAAQ;EACR,QAAQ;EAER,WAAW;EAEX,SAAS;EACT,YAAY;EACZ,OAAO;EACP,QAAQ;CACV;CACA,2BAA2B;EACzB,SAAS;EACT,cAAc;EAEd,UAAU;EACV,OAAO;EACP,SAAS;EACT,YAAY;CACd;CACA,qDAAqD,EACnD,SAAS,IACX;CAEA,8BAA8B;EAC5B,QAAQ;EACR,QAAQ;EACR,eAAe;CACjB;CACA,sDAAsD,EACpD,SAAS,IACX;CACA,iCAAiC,EAC/B,QAAQ,WACV;CAIA,qDAAqD,EACnD,SAAS,OACX;CACA,6CAA6C,EAC3C,YAAY,OACd;CAGA,4BAA4B,EAC1B,eAAe,OACjB;CAGA,+BAA+B,EAC7B,WAAW,aACb;CACA,wBAAwB;EACtB,UAAU;EACV,KAAK;EACL,MAAM;EACN,QAAQ;EACR,eAAe;EACf,WAAW;EACX,UAAU;EACV,SAAS;EAET,SAAS,GAAG,YAAY;EACxB,iBAAiB;EACjB,WAAW;CACb;AACF,CAAC;;;;;;;;;AAUD,IAAa,mBAAmB,EAC9B,WACA,SACA,YACA,SAAS,MACT,WACA,oBAAoB,OACpB,oBACiC;CACjC,MAAM,aAAa,iBAAiB,WAAW,YAAY,QAAQ,WAAW,mBAAmB,aAAa;CAC9G,OAAO;EACL;EACA;EACA;EACA,kBAAkB,WAAW,UAAU;EACvC;EACA,kBAAkB,WAAW,YAAY,OAAO;CAClD;AACF;;;;;;;;AASA,IAAM,qBACJ,WACA,YACA,YAEA,WAAW,UACT,MAAM;CAGiB;CADrB,yBAAS,IAAI,IAAyB;CACtC,YAAY,MAA2B;EAAlB,KAAA,OAAA;EACnB,KAAK,UAAU,iBAAiB,UAAU,KAAK,SAAS;EACxD,KAAK,UAAU;CACjB;CAEA,OAAO,QAAoB;EACzB,MAAM,mBACJ,OAAO,WAAW,MAAM,qBAAqB,KAAK,MAAM,OAAO,MAAM,MAAM,qBAAqB,KAAK;EACvG,MAAM,eACJ,OAAO,WAAW,MAAM,mBAAmB,KAAK,MAAM,OAAO,MAAM,MAAM,mBAAmB,KAAK;EACnG,IACE,OAAO,mBACP,OAAO,cACP,OAAO,mBACP,OAAO,gBACP,oBACA,cAEA,KAAK,UAAU;CAEnB;CAEA,UAAU;EACR,KAAK,KAAK,UAAU,oBAAoB,UAAU,KAAK,SAAS;EAChE,KAAK,MAAM,QAAQ,KAAK,OAAO,OAAO,GACpC,KAAK,OAAO;EAEd,KAAK,OAAO,MAAM;CACpB;CAEA,kBAAkB,KAAK,UAAU;CAIjC,WAAW;EACT,KAAK;EACL,OAAO,SAAqC,KAAK,MAAM,IAAI;EAC3D,QAAQ,cAA8B,KAAK,OAAO,SAAS;CAC7D;CAEA,YAAY;EACV,KAAK,KAAK,eAAe,KAAK,QAAQ;CACxC;CAEA,MAAM,MAAkC;EAEtC,IAAI,KAAK,OAAO,UAAU,CAAC,EAAE,UAC3B,OAAO,CAAC;EAEV,MAAM,QAAQ,iBAAiB,KAAK,OAAO,SAAS;EACpD,IAAI,SAAS,MACX,OAAO,CAAC;EAEV,MAAM,QAAQ,UAAU,KAAK,KAAK,CAAC,CAAC;EACpC,IAAI,CAAC,SAAU,WAAW,CAAC,QAAQ,KAAK,OAAO,KAAK,GAClD,OAAO,CAAC;EAEV,MAAM,SAAS,KAAK,YAAY,MAAM,IAAI;EAC1C,MAAM,aAAa,KAAK,UAAU,sBAAsB;EAExD,IAAI,CAAC,UAAU,OAAO,UAAU,WAAW,OAAO,OAAO,OAAO,WAAW,QACzE,OAAO,CAAC;EAIV,MAAM,OADc,KAAK,WAAW,sBACvB,CAAA,CAAY,OAAA;EACzB,OAAO,CAAC;GAAE;GAAO,QAAQ,MAAM;GAAM;GAAM,MAAM,OAAO,MAAM,OAAO,UAAU;EAAE,CAAC;CACpF;CAEA,OAAO,WAA2B;EAChC,MAAM,yBAAS,IAAI,IAAY;EAC/B,KAAK,MAAM,EAAE,OAAO,QAAQ,MAAM,SAAS,WAAW;GACpD,OAAO,IAAI,KAAK;GAChB,IAAI,OAAO,KAAK,OAAO,IAAI,KAAK;GAChC,IAAI,CAAC,MAAM;IACT,OAAO,KAAK,YAAY;IACxB,KAAK,OAAO,IAAI,OAAO,IAAI;GAC7B;GACA,KAAK,QAAQ,SAAS,OAAO,MAAM;GACnC,KAAK,MAAM,OAAO,GAAG,KAAK;GAC1B,KAAK,MAAM,MAAM,GAAG,IAAI;EAC1B;EACA,KAAK,MAAM,CAAC,OAAO,SAAS,KAAK,QAC/B,IAAI,CAAC,OAAO,IAAI,KAAK,GAAG;GACtB,KAAK,OAAO;GACZ,KAAK,OAAO,OAAO,KAAK;EAC1B;CAEJ;CAEA,cAA2B;EACzB,MAAM,OAAO,kBAAkB;EAC/B,KAAK,iBAAiB,cAAc,UAAU;GAC5C,IAAI,MAAM,WAAW,GACnB;GAGF,MAAM,SAAS,OAAO,KAAK,QAAQ,MAAM;GACzC,MAAM,cAAc,UAAU,KAAK,KAAK,KAAK,CAAC,CAAC,WAAW,UAAU,MAAM,SAAS,MAAM;GACzF,IAAI,cAAc,GAChB;GAEF,KAAK,KAAK,OAAO,UAAU,CAAC,EAAE,IAAI,aAAa,KAAK;GACpD,MAAM,eAAe;EACvB,CAAC;EACD,KAAK,KAAK,UAAU,YAAY,IAAI;EACpC,OAAO;CACT;AACF,CACF;;;;;;;;;;AC39BF,IAAa,UAAU,EAAE,WAAyB,CAAC,MAAiB;CAClE,wBAAwB;CACxB,eAAe;CACf,UAAU,EAAE,OAAO,CAAC;AACtB;;;;;AAMA,IAAa,gCAA2C,8BAA8B,UAAU;;;;;;;AAQhG,IAAa,uBAAkC,qBAAqB,gBAAgB;;;;;AAMpF,IAAa,aAAa,EAAE,WAAyC,CAAC,MACpE,gBAAgB;CAAE,WAAW;CAAY,YAAA;CAAY;AAAO,CAAC;;;;;;AAO/D,IAAM,6BAAa,IAAI,QAA8B;AAErD,IAAa,cAAc,UAAgC;CACzD,MAAM,SAAS,WAAW,IAAI,KAAK;CACnC,IAAI,QACF,OAAO;CAGT,MAAM,SAAkB,CAAC;CACzB,MAAM,SAAS,WAAW,KAAK,CAAC,CAAC,QAAQ,OAAO;CAChD,IAAI,OAAO,WAAW,GACpB;EACE,IAAI,OAAO,KAAK,OAAO,MACrB,OAAO,KAAK;GAAE,MAAM,OAAO;GAAM,IAAI,OAAO;EAAG,CAAC;QAE3C,OAAO,YAAY;CAG9B,WAAW,IAAI,OAAO,MAAM;CAC5B,OAAO;AACT;AAGA,IAAM,iBAAe,WAA2E;CAC9F,MAAM,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;CACzD,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,OAAO,GAAG,EAAE;EACzB,IAAI,QAAQ,MAAM,QAAQ,KAAK,IAC7B,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,MAAM,EAAE;OAEpC,OAAO,KAAK,EAAE,GAAG,MAAM,CAAC;CAE5B;CACA,OAAO;AACT;AAKA,IAAM,kBAAgB,OAAoB,QAAiB,YAAsD;CAC/G,MAAM,QAAQ,OAAO;CACrB,OAAO,cACL,QAAQ,KAAK,WAAW;EACtB,MAAM,UAAU,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,EAAE,CAAC,KAAK,OAAO,MAAM,CAAC;EAC9E,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,EAAE,CAAC,OAAO,MAAM,IAAI;CAC7D,EAAE,CACJ;AACF;;;;;;;;AASA,IAAa,kBACX,OACA,eACA,cAC2B;CAC3B,MAAM,SAAS,WAAW,KAAK;CAC/B,MAAM,QAAQ,OAAO;CACrB,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC,QAAQ,UAAU,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAE/G,IAAI,QAAQ,WAAW,KAAK,QAAQ,SAAS,SAAS,GACpD,OAAO;CAGT,MAAM,OAAO,QAAQ,KAAK,UAAU,MAAM,IAAI,YAAY,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM;CAC5G,MAAM,UAAU,eAAa,OAAO,QAAQ,OAAO;CACnD,MAAM,WAAW,YAAY,QAAQ,OAAO,UAAU,CAAC,OAAO,MAAM,IAAI;CAExE,IAAI,QAAQ,MAAM,UAAU,WAAW,MAAM,QAAQ,WAAW,MAAM,EAAE,GACtE,OAAO;CAGT,MAAM,SAAS,YAAY,QAAQ,GAAG,KAAK,QAAQ,OAAO;CAC1D,MAAM,UAAU,CAAC,GAAG,QAAQ,KAAK,WAAW;EAAE,MAAM,MAAM;EAAM,IAAI,MAAM;CAAG,EAAE,GAAG;EAAE,MAAM;EAAU;CAAO,CAAC;CAG5G,MAAM,YAAY,MAAM,QAAQ,OAAO;CACvC,MAAM,YAAY,UAAU,OAAO,UAAU,EAAE,KAAK,YAAY,QAAQ,IAAI;CAG5E,MAAM,UAAoB,CAAC;CAC3B,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,SAAS;EAC3B,QAAQ,KAAK,MAAM;EACnB,UAAU,OAAO,MAAM,CAAC,KAAK,OAAO,MAAM,CAAC,OAAO;CACpD;CACA,MAAM,QAAQ,YAAY,KAAK;CAC/B,OAAO;EACL;EACA,WAAW,EAAE,QAAQ,KAAK,IAAI,OAAO,UAAU,SAAS,EAAE;EAC1D,SAAS,kBAAkB,GAAG,OAAO;EACrC,WAAW;CACb;AACF;AAEA,IAAM,gBAAc,MAAkB,eAAyB,cAA4B;CACzF,MAAM,OAAO,eAAe,KAAK,OAAO,eAAe,SAAS;CAChE,IAAI,MACF,KAAK,SAAS,IAAI;AAEtB;;;;;;AAOA,IAAa,qBACX,OACA,SACA,SAC2B;CAC3B,MAAM,SAAS,WAAW,KAAK;CAC/B,MAAM,QAAQ,OAAO;CACrB,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,QAAQ,UAAU,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CACzG,IAAI,QAAQ,WAAW,GACrB,OAAO;CAGT,MAAM,UAAU,eAAa,OAAO,QAAQ,OAAO;CACnD,MAAM,UAA2D,QAAQ,KAAK,WAAW;EACvF,MAAM,MAAM;EACZ,IAAI,MAAM;CACZ,EAAE;CACF,IAAI,SAAS,QAAQ,EAAE,CAAC;CACxB,IAAI,QAAQ,QAAQ,KAAK,SAAS,GAAG;EAEnC,MAAM,SAAS,QAAQ,EAAE,CAAC,MAAM,MAAM,IAAI,SAAS,OAAO,GAAG,KAAK;EAClE,QAAQ,KAAK;GAAE,MAAM,QAAQ,EAAE,CAAC;GAAM,IAAI,QAAQ,EAAE,CAAC;GAAI;EAAO;EAChE,SAAS,QAAQ,EAAE,CAAC,OAAO,KAAK;CAClC;CAEA,MAAM,YAAY,MAAM,QAAQ,OAAO;CACvC,OAAO;EACL;EACA,WAAW,EAAE,QAAQ,KAAK,IAAI,QAAQ,UAAU,SAAS,EAAE;EAC3D,SAAS,kBAAkB,GAAG,CAAC,CAAC;EAChC,WAAW,QAAQ,OAAO,iBAAiB;CAC7C;AACF;AAEA,IAAM,mBAAiB,MAAkB,SAAmB,SAA8B;CACxF,MAAM,OAAO,kBAAkB,KAAK,OAAO,SAAS,IAAI;CACxD,IAAI,MACF,KAAK,SAAS,IAAI;AAEtB;AAEA,IAAa,mBAA6B;CACxC,WAAW;CACX,YAAA;CACA,YAAY,OAAO,WAAW,OAAO,KAAK,UAAU,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,MAAM;CAC5G,eAAA;AACF;;;;;;AC7MA,IAAa,gBAA2B;CACtC,OAAO;EACL,YAAY,EACV,sBAAsB,OAAO,GAAG,MAAM,CAAC,CAAC,KAC1C,CAAC;EACD,WAAW,EAET,YAAY,SAAS;GACnB,OAAO,OAAO,GAAG,KAAK,CAAA,CACnB,WAAW,yCAAyC,CAAA,CACpD,OACC,OAAO,GAAG,OAAO,OAAO,GAAG,CAAA,CACxB,WAAW,KAAG,0BAA0B,QAAQ,WAAW,CAAC,CAAA,CAC5D,OACC,OAAO,GAAG,OAAO,OAAO,GAAG,CAAC,CAAC,WAAW,EACtC,MAAM,OAAO,KAAK,0BAA0B,EAC9C,CAAC,CACH,CACJ,CAAC,CAAC;EACN,EACF,CAAC;EACD;CACF;AACF;AAEA,IAAM,WAAS,aAAW,MAAM;CAC9B,kBAAkB;EAChB,SAAS;EACT,YAAY;EACZ,OAAO;CACT;CACA,wBAAwB,EACtB,SAAS,EACX;AACF,CAAC;;;;ACFD,IAAa,eAAe,EAAE,aAA4C;CAExE,MAAM,wBAAQ,IAAI,QAA6B;CAC/C,MAAM,WAAW,UAA+B;EAC9C,IAAI,QAAQ,MAAM,IAAI,KAAK;EAC3B,IAAI,CAAC,OAAO;GACV,QAAQ,OAAO,KAAK;GACpB,MAAM,IAAI,OAAO,KAAK;EACxB;EACA,OAAO;CACT;CAEA,OAAO;EAEL,YAAY,EACV,sBAAsB,OAAO,GAAG,MAAM,CAAC,CAAC,WAAW,QAAQ,CAAC,CAAC,KAC/D,CAAC;EACD,OAAO;GACL,OAAO;GACP,UAAU,SAAS;IACjB,MAAM,UAAU,IAAI,gBAA8B;IAClD,KAAK,MAAM,QAAQ,QAAQ,KAAK,KAAK,GACnC,QAAQ,IAAI,KAAK,cAAc,KAAK,cAAc,IAAI,WAAW,SAAS,KAAK,OAAO,IAAI,CAAC,CAAC;IAE9F,OAAO,QAAQ,OAAO;GACxB;GAGA,mBAAmB,WACjB,OAAO,aAAa,MAAM,OAAO,GAAG,QAAQ,MAAM,WAAW,OAAO,GAAG,UAAU,KAAK,OAAO,GAAG,YAAY,CAAC,CAAC;GAChH,kBAAkB,EAChB,YAAY,MAAM,SAAS;IACzB,MAAM,OAAO,QAAQ,KAAK,KAAK,CAAC,CAAC,MAAM,cAAc,UAAU,iBAAiB,KAAK,IAAI;IACzF,IAAI,CAAC,MACH,OAAO;IAET,MAAM,SAAS,SAAS,KAAK,OAAO,IAAI,IAAI,eAAe;IAC3D,KAAK,SAAS,EAAE,SAAS,OAAO,GAAG;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK;IAAG,CAAC,EAAE,CAAC;IACtE,OAAO;GACT,EACF;EACF,CAAC;EACD;CACF;AACF;;AAGA,IAAM,YAAY,OAAoB,SAAwB;CAC5D,IAAI,SAAS;CACb,aAAa,KAAK,CAAC,CAAC,QAAQ,KAAK,MAAM,KAAK,KAAK,MAAM,OAAO;EAC5D,IAAI,QAAQ,KAAK,QAAQ,MAAM,KAAK,IAClC,SAAS;CAEb,CAAC;CACD,OAAO;AACT;;AAGA,IAAa,aAAa,MAAkB,WAA6B;CACvE,MAAM,UAAU,OAAO,KAAK,KAAK,CAAA,CAC9B,QAAQ,SAAS,KAAK,KAAK,KAAK,IAAI,CAAA,CACpC,KAAK,SAAS,WAAW,GAAG;EAAE,MAAM,KAAK;EAAM,IAAI,KAAK;CAAG,CAAC,CAAC;CAChE,IAAI,QAAQ,SAAS,GACnB,KAAK,SAAS,EAAE,QAAQ,CAAC;AAE7B;;AAGA,IAAa,eAAe,MAAkB,WAA6B;CACzE,MAAM,UAAU,OAAO,KAAK,KAAK,CAAC,CAAC,KAAK,SAAS,aAAa,GAAG;EAAE,MAAM,KAAK;EAAM,IAAI,KAAK;CAAG,CAAC,CAAC;CAClG,IAAI,QAAQ,SAAS,GACnB,KAAK,SAAS,EAAE,QAAQ,CAAC;AAE7B;;AAGA,IAAM,aAAN,cAAyB,aAAa;CACpC;CAEA,YAAY,QAAiB;EAC3B,MAAM;EACN,KAAK,UAAU;CACjB;CAEA,GAAY,OAA4B;EACtC,OAAO,KAAK,YAAY,MAAM;CAChC;CAEA,QAA8B;EAC5B,OAAO,OAAO,GAAG,KAAK,CAAA,CACnB,WAAW,wDAAwD,CAAA,CACnE,OACC,OAAO,GAAG,OAAO,OAAO,GAAG,CAAA,CACxB,WAAW,SAAS,CAAA,CACpB,OACC,OAAO,GAAG,OAAO,OAAO,GAAG,CAAC,CAAC,WAAW,EACtC,MAAM,OAAO,KAAK,KAAK,UAAU,6BAA6B,yBAAyB,EACzF,CAAC,CACH,CACJ,CAAC,CAAC;CACN;AACF;;;;;;;;;AAUA,IAAa,iBAAiB;;;;;;;AAQ9B,IAAa,oBAAoB,YAAgC;CAE/D,MAAM,UAAU,IAAI,OAAO,QAAQ,QAAQ,QAAQ,MAAM,SAAS,GAAG,IAAI,QAAQ,QAAQ,GAAG,QAAQ,MAAM,EAAE;CAC5G,QAAQ,UAAU;EAChB,MAAM,MAAM,MAAM;EAClB,MAAM,OAAO,IAAI,SAAS;EAC1B,MAAM,QAAkB,CAAC;EACzB,IAAI;EACJ,QAAQ,YAAY;EACpB,QAAQ,QAAQ,QAAQ,KAAK,IAAI,OAAO,MACtC,MAAM,KAAK,MAAM,OAAO,MAAM,QAAQ,MAAM,EAAE,CAAC,MAAM;EAGvD,MAAM,QAAgB,CAAC;EACvB,KAAK,IAAI,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;GACpD,MAAM,YAAY,MAAM;GACxB,MAAM,UAAU,MAAM,QAAQ;GAG9B,MAAM,OAAO,IAAI,OAAO,OAAO,CAAC,CAAC;GACjC,MAAM,eAAe,IAAI,OAAO,SAAS,CAAC,CAAC;GAE3C,IAAI,KADkB,QAAQ,IAAI,MAAM,SAAS,MAAM,QAAQ,KAAK,IAAI;GAExE,OAAO,KAAK,QAAQ,KAAK,KAAK,KAAK,KAAK,EAAE,GACxC;GAEF,IAAI,KAAK,MACP,MAAM,KAAK;IAAE;IAAM;IAAI;GAAa,CAAC;EAEzC;EACA,OAAO;CACT;AACF;AAEA,IAAM,WAAS,aAAW,MAAM;CAC9B,mBAAmB;EACjB,OAAO;EACP,OAAO;EACP,SAAS;EACT,YAAY;CACd;CACA,yBAAyB,EACvB,SAAS,IACX;AACF,CAAC;;;;AC1KD,IAAa,cAAc,EAAE,MAAM,OAAO,OAAO,WAAW,cAAc,eAA0B;CAClG,OAAO;EAAE;EAAM;EAAO;EAAO;EAAW;EAAc,UAAU,SAAS,IAAI,UAAU;CAAE;AAC3F;;;;AAKA,IAAa,OAAb,MAAkC;CAChC,OAAqB;CACrB,QAAQ;CACR,QAAQ;CACR;CACA;CACA;CACA,WAA6B,CAAC;CAE9B,YAAY,MAAkB;EAC5B,KAAK,OAAO;EACZ,KAAK,YAAY;GAAE,MAAM,KAAK;GAAM,IAAI,KAAK;EAAG;EAChD,KAAK,eAAe,KAAK;CAC3B;CAEA,SAAS;EACP,OAAO,WAAW,IAAI;CACxB;CAEA,IAAI,OAAa;EACf,OAAO;CACT;CAIA,SACE,UACA,SACe;EACf,IAAI,OAAO,aAAa,YACtB,OAAO,SAAY,MAAM,QAAQ;OAEjC,OAAO,SAAY,UAAU,OAAQ;CAEzC;;;;CAKA,KAAK,KAA+B;EAClC,OAAO,KAAK,UAAgB,SAAU,KAAK,UAAU,QAAQ,OAAO,KAAK,UAAU,MAAM,MAAM,OAAO,KAAA,CAAU;CAClH;;;;CAKA,KAAK,MAAY,QAAQ,MAAwB;EAC/C,IAAI,SAAS,KAAK,SAAS,SAAS,GAClC,OAAO,KAAK,SAAS;EAGvB,IAAI,KAAK,aACP,OAAO,KAAK;EAGd,IAAI,KAAK,QACP,OAAO,KAAK,KAAK,KAAK,QAAQ,KAAK;CAIvC;;;;CAKA,KAAK,MAA8B;EACjC,IAAI,KAAK,aACP,OAAO,KAAK,eAAe,KAAK,WAAW;EAG7C,OAAO,KAAK,QAAQ,SAAS,SAAS,KAAA,IAAY,KAAK;CACzD;;;;CAKA,eAAe,MAAkB;EAC/B,OAAO,KAAK,SAAS,SAAS,IAAI,KAAK,eAAe,KAAK,SAAS,GAAG,EAAE,CAAE,IAAI;CACjF;AACF;AAEA,IAAa,YAAY,MAAY,SAAiC;CACpE,MAAM,iBAAiB,KAAK,eAAe,IAAI;CAC/C,OAAO,CAAC,KAAK,UAAU,MAAM,eAAe,UAAU,EAAE;AAC1D;;;;;AAMA,IAAa,YAAqB,MAAY,OAA+D;CAC3G,MAAM,KAAK,MAAY,UAAiC;EACtD,IAAI,KAAK,SAAS,QAAQ;GACxB,MAAM,QAAQ,GAAG,MAAM,KAAK;GAC5B,IAAI,SAAS,MACX,OAAO;EAEX;EAEA,KAAK,MAAM,SAAS,KAAK,UAAU;GACjC,MAAM,QAAQ,EAAE,OAAO,QAAQ,CAAC;GAChC,IAAI,SAAS,MACX,OAAO;EAEX;CAGF;CAEA,OAAO,EAAE,MAAM,KAAK,SAAS,SAAS,KAAK,CAAC;AAC9C;AAEA,IAAa,sBAAsB,OAAoB,SAAuB;CAC5E,OAAO,MAAM,IAAI,YAAY,KAAK,aAAa,MAAM,KAAK,aAAa,EAAE;AAC3E;AAEA,IAAa,oBAAoB,MAAY,QAAQ,MAAM;CACzD,MAAM,SAAS,KAAK,OAAO,KAAK;CAChC,MAAM,OAAO;EACX,GAAG,KAAK;EACR,GAAG,KAAK,aAAa,SAAS;EAC9B,GAAG,KAAK,aAAa,SAAS;EAC9B,OAAO,KAAK;EACZ,MAAM,OAAO,CAAC,KAAK,KAAK,MAAM,KAAK,KAAK,EAAE,CAAC;EAC3C,MAAM,OAAO,CAAC,KAAK,UAAU,MAAM,KAAK,UAAU,EAAE,CAAC;EACrD,SAAS,OAAO,CAAC,KAAK,aAAa,MAAM,KAAK,aAAa,EAAE,CAAC;CAChE;CAEA,OAAO,GAAG,SAAS,KAAK,KAAK,EAAE,CAAC,YAAY,EAAE,GAAG,OAAO,QAAQ,IAAI,CAAA,CACjE,KAAK,CAAC,GAAG,OAAO,GAAG,EAAE,GAAG,GAAG,CAAA,CAC3B,KAAK,IAAI,EAAE;AAChB;AAEA,IAAM,UAAU,UACd,KAAK,UAAU,QAAQ,KAAa,UAAe;CACjD,IAAI,OAAO,UAAU,UACnB,OAAO,MAAM,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG;CAEzC,OAAO;AACT,CAAC,CAAC,CAAC,WAAW,MAAK,EAAE;AAEvB,IAAa,YAAY,MAAM,OAAmB,EAChD,UAAU,WAAW,OAAO,GAC9B,CAAC;;;;;;AASD,IAAa,gBAAgB,WAAwB,CAAC,MAAiB;CAIrE,MAAM,kBAAkB;CAExB,MAAM,aAAa,UAA6B;EAC9C,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI;EACJ,IAAI,QAAQ;EACZ,IAAI,QAAQ;EAGZ,MAAM,eAAqC,CAAC;EAE5C,CAAC,iBAAiB,OAAO,MAAM,IAAI,QAAQ,eAAe,KAAK,WAAW,KAAK,EAAA,CAAG,QAAQ;GACxF,QAAQ,SAAS;IACf,QAAQ,KAAK,MAAb;KACE,KAAK;MACH,OAAO,IAAI,KAAK,KAAK,IAAI;MACzB,UAAU;MACV;KAEF,KAAK;MACH,UAAU,SAAM,KAAA,GAAA;OAAA,YAAA;OAAA,GAAA;OAAA,GAAA;OAAA,GAAA,KAAA;OAAA,GAAA,CAAA,WAAA,EAAA;MAAA,CAAC;MACjB,UAAU,MAAG,KAAA,GAAA;OAAA,YAAA;OAAA,GAAA;OAAA,GAAA;OAAA,GAAA,KAAA;OAAA,GAAA,CAAA,QAAA,EAAA;MAAA,CAAC;MAGd,SAAS,KAAK,KAAK,QAAQ,SAAS,aAAa,OAAO;MACxD,IAAI,WAAW,MACb,KAAK,UAAU,KAAK,KAAK,KAAK;WAE9B,QAAQ,UAAU,KAAK,QAAQ,KAAK;MAEtC,aAAa,EAAE,SAAS,KAAA;MACxB;KAEF,KAAK,YAAY;MACf,UAAU,QAAK,KAAA,GAAA;OAAA,YAAA;OAAA,GAAA;OAAA,GAAA;OAAA,GAAA,KAAA;OAAA,GAAA,CAAA,UAAA,EAAA;MAAA,CAAC;MAIhB,MAAM,cAAc,KAAK,KAAK,eAAe,KAAK,KAAK,QAAQ;MAC/D,MAAM,UAAU,KAAK,KAAK,QAAQ,MAAM,MAAM,IAAI;MAClD,MAAM,WAAkB;OACtB,MAAM,MAAM,IAAI,OAAO,KAAK,IAAI,CAAC,CAAC;OAClC,IAAI,cAAc,KAAK,IAAI,YAAY,OAAO,GAAG,OAAO,IAAI;MAC9D;MAEA,UAAU;OACR,MAAM;OACN,OAAO,EAAE;OACT;OACA,MAAM,KAAK;OACX,WAAW;OACX,cAAc,EAAE,GAAG,SAAS;OAC5B;OACA,aAAa,aAAa;OAC1B,UAAU,CAAC;MACb;MAGA,IAAI,QAAQ,aACV,QAAQ,YAAY,cAAc;MAIpC,aAAa,SAAS;MAGtB,IAAI,MACF,KAAK,UAAU,KAAK,KAAK,aAAa,KAAK,KAAK,IAAI,KAAK,UAAU,IAAI,QAAQ,UAAU,OAAO,CAAC;MAEnG,OAAO;MAGP,OAAO,SAAS,KAAK,OAAO;MAC5B,IAAI,OAAO,UAAU,OAAO,OAAO,KAAK,MACtC,OAAO,UAAU,KAAK,OAAO,aAAa,KAAK,QAAQ,UAAU,OAAO;MAG1E;KACF;KACA,KAAK;MACH,UAAU,SAAM,KAAA,GAAA;OAAA,YAAA;OAAA,GAAA;OAAA,GAAA;OAAA,GAAA,KAAA;OAAA,GAAA,CAAA,WAAA,EAAA;MAAA,CAAC;MACjB,QAAQ,OAAO;MAGf,QAAQ,aAAa,OAAO,KAAK,IAAI,KAAK,KAAK,GAAG,QAAQ,aAAa,EAAE;MACzE;KAEF,KAAK;MACH,UAAU,SAAM,KAAA,GAAA;OAAA,YAAA;OAAA,GAAA;OAAA,GAAA;OAAA,GAAA,KAAA;OAAA,GAAA,CAAA,WAAA,EAAA;MAAA,CAAC;MACjB,QAAQ,OAAO;MACf;KAEF,KAAK;MACH,UAAU,SAAM,KAAA,GAAA;OAAA,YAAA;OAAA,GAAA;OAAA,GAAA;OAAA,GAAA,KAAA;OAAA,GAAA,CAAA,WAAA,EAAA;MAAA,CAAC;MACjB,QAAQ,aAAa,OAAO,KAAK,IAAI,KAAK,KAAK,GAAG,QAAQ,aAAa,EAAE;IAG7E;GACF;GACA,QAAQ,SAAS;IACf,IAAI,KAAK,SAAS,cAAc;KAC9B,UAAU,QAAK,KAAA,GAAA;MAAA,YAAA;MAAA,GAAA;MAAA,GAAA;MAAA,GAAA,KAAA;MAAA,GAAA,CAAA,UAAA,EAAA;KAAA,CAAC;KAChB,aAAa,WAAW,KAAA;KACxB,SAAS,OAAO;IAClB;GACF;EACF,CAAC;EAED,UAAU,MAAG,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,KAAA;GAAA,GAAA,CAAA,QAAA,EAAA;EAAA,CAAC;EACd,OAAO;CACT;CAEA,OAAO,CACL,WAAW,OAAyB;EAClC,SAAS,UAAU;GACjB,OAAO,UAAU,KAAK;EACxB;EACA,SAAS,OAAyB,OAAoB;GAEpD,IAAI,CAAC,GAAG,cAAc,WAAW,GAAG,KAAK,MAAM,WAAW,GAAG,UAAU,GACrE,OAAO;GAGT,OAAO,UAAU,GAAG,KAAK;EAC3B;EACA,UAAU,UAAU,UAAU,KAAK,KAAK;CAC1C,CAAC,CACH;AACF;;;;;;;;ACnTA,IAAM,4BAAY,IAAI,QAA6B;AACnD,IAAM,gBAAgB,UAA+B;CACnD,MAAM,SAAS,UAAU,IAAI,KAAK;CAClC,IAAI,QACF,OAAO;CAGT,MAAM,OAAO,MAAM,MAAM,SAAS;CAClC,MAAM,QAAgB,CAAC;CACvB,KAAK,UAAU,SAAS;EACtB,MAAM,KAAK,IAAI;CACjB,CAAC;CAED,UAAU,IAAI,OAAO,KAAK;CAC1B,OAAO;AACT;AAEA,IAAM,aAAa,UACjB,aAAa,KAAK,CAAC,CAAC,KAAK,UAAU;CACjC,MAAM,KAAK,UAAU;CAErB,IAAI,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,EAAE,CAAC,UAAU,OAAO,IAAI,KAAK,UAAU;AACtF,EAAE;AAKJ,IAAa,aAAa,OAAoB,UAAwB;CACpE,MAAM,OAAO,MAAM,MAAM,SAAS;CAClC,MAAM,OAAO,KAAK,KAAK,MAAM,IAAI;CACjC,IAAI,CAAC,MACH,OAAO;CAET,MAAM,CAAC,MAAM,MAAM,SAAS,MAAM,IAAI;CACtC,OAAO;EAAE;EAAM;CAAG;AACpB;;AAGA,IAAM,YAAY,OAAoB,QAAwB;CAC5D,MAAM,OAAO,MAAM,IAAI,OAAO,GAAG,CAAC,CAAC;CACnC,OAAO,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC;AACxC;;;;;;;AAQA,IAAa,iBACX,MACA,eACA,WACA,aAC8C;CAE9C,IAAI,cAAc,WAAW,GAC3B,OAAO;CAGT,MAAM,EAAE,UAAU;CAClB,MAAM,QAAQ,aAAa,KAAK;CAChC,MAAM,QAAQ,MAAM;CACpB,MAAM,OAAO,cAAc,KAAK;CAChC,MAAM,aAAa,MAAM,cAAc;CACvC,MAAM,eAAe,SAAS,OAAO,WAAW,UAAU,IAAI;CAI9D,MAAM,CAAC,YAAY,YAAY,SAAS,MAAM,MAAM,SAAS,GAAG,UAAU;CAC1E,IAAI,aAAa,YAAY;CAC7B,OACE,cAAc,KACd,MAAM,WAAW,CAAC,UAAU,QAAQ,cACpC,MAAM,WAAW,CAAC,UAAU,QAAQ,UAEpC;CAEF,MAAM,QAAQ,cAAc,IAAI,MAAM,cAAc;CACpD,MAAM,SAAS,YAAY,QAAQ,MAAM,aAAa;CAEtD,MAAM,cAAc,QAAQ,SAAS,OAAO,MAAM,UAAU,IAAI,IAAI;CAEpE,MAAM,YAAY,SAAS,SAAS,OAAO,OAAO,UAAU,IAAI,IAAI;CACpE,MAAM,YAAY,QAAQ,cAAc,OAAO;CAC/C,MAAM,SAAS,UAAkB,KAAK,IAAI,WAAW,KAAK,IAAI,WAAW,KAAK,CAAC;CAG/E,IAAI,SAAS,MAAM,YAAY;CAE/B,IAAI,UAAU,YAAY,QAAQ;EAChC,MAAM,MAAM,KAAK,YAAY,OAAO,UAAU,IAAI,CAAC,EAAE;EACrD,IAAI,OAAO,QAAQ,YAAY,KAC7B,SAAS;CAEb;CAGA,MAAM,UAAW,SAAS,gBAAgB,OAAA;CAC1C,OAAO;EAAE;EAAQ;CAAO;AAC1B;;;;;;AAOA,IAAM,YAAY,MAAc,YAAoB,aAA6B;CAC/E,MAAM,QAAQ,WAAW;CACzB,IAAI,UAAU,GACZ,OAAO;CAET,OAAO,KACJ,MAAM,IAAI,CAAA,CACV,KAAK,SAAS;EACb,MAAM,WAAW,KAAK,SAAS,KAAK,UAAU,CAAC,CAAC;EAChD,MAAM,OAAO,KAAK,IAAI,GAAG,WAAW,KAAK;EACzC,OAAO,IAAI,OAAO,IAAI,IAAI,KAAK,MAAM,QAAQ;CAC/C,CAAC,CAAA,CACA,KAAK,IAAI;AACd;;;;;;AAOA,IAAM,YAAY,MAAkB,aAAqB,WAAmB,WAA0B;CACpG,MAAM,EAAE,UAAU;CAClB,MAAM,OAAa,MAAM,MAAM,SAAS;CACxC,MAAM,QAAQ,aAAa,KAAK;CAChC,MAAM,QAAQ,MAAM;CACpB,IAAI,cAAc,KAAK,eAAe,SAAS,cAAc,aAC3D;CAGF,MAAM,SAAS,MAAM;CACrB,MAAM,CAAC,MAAM,MAAM,SAAS,MAAM,MAAM;CAGxC,MAAM,WAAW,YAAY,QAAQ,MAAM,UAAU,CAAC,UAAU,OAAO,MAAM,IAAI;CAEjF,IAAI,WAAW,QAAQ,YAAY,IACjC;CAGF,MAAM,eAAe,SAAS,OAAO,IAAI;CAEzC,MAAM,eAAe,WAAW,YAAY,QAAQ,SAAS,OAAO,MAAM,UAAU,CAAC,UAAU,IAAI,IAAI;CACvG,MAAM,OAAO,SAAS,MAAM,IAAI,YAAY,MAAM,EAAE,GAAG,cAAc,YAAY;CAGjF,MAAM,eAAe,KAAK,MAAM,IAAI;CACpC,MAAM,aAAa,eAAe,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC;CAC7D,MAAM,WAAW,eAAe,KAAK,IAAI,KAAK,GAAG,MAAM,IAAI,MAAM,IAAI;CACrE,MAAM,SAAS,YAAY,QAAQ,GAAG,KAAK,MAAM,KAAK;CAGtD,MAAM,UACJ,YAAY,aACR,CACE;EAAE,MAAM;EAAU;CAAO,GACzB;EAAE,MAAM;EAAY,IAAI;CAAS,CACnC,IACA,CACE;EAAE,MAAM;EAAY,IAAI;CAAS,GACjC;EAAE,MAAM;EAAU;CAAO,CAC3B;CAIN,MAAM,YAAY,MAAM,QAAQ,OAAO;CACvC,MAAM,YAAY,UAAU,OAAO,UAAU,EAAE,KAAK,YAAY,QAAQ,IAAI;CAC5E,MAAM,QAAQ,YAAY,KAAK;CAC/B,KAAK,SAAS;EACZ;EACA,WAAW,EAAE,QAAQ,KAAK,IAAI,OAAO,UAAU,SAAS,EAAE;EAC1D,SAAS,kBAAkB,GAAG,CAAC,SAAS,CAAC;EACzC,WAAW;CACb,CAAC;AACH;AAGA,IAAa,eAAe,WAA2E;CACrG,MAAM,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI;CACzD,MAAM,SAAyC,CAAC;CAChD,KAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,OAAO,OAAO,GAAG,EAAE;EACzB,IAAI,QAAQ,MAAM,QAAQ,KAAK,IAC7B,KAAK,KAAK,KAAK,IAAI,KAAK,IAAI,MAAM,EAAE;OAEpC,OAAO,KAAK,EAAE,GAAG,MAAM,CAAC;CAE5B;CACA,OAAO;AACT;AAKA,IAAM,gBAAgB,OAAoB,QAAiB,YAAsD;CAC/G,MAAM,QAAQ,OAAO;CACrB,OAAO,YACL,QAAQ,KAAK,WAAW;EACtB,MAAM,UAAU,QAAQ,KAAK,QAAQ,IAAI,OAAO,QAAQ,EAAE,CAAC,KAAK,OAAO,MAAM,CAAC;EAC9E,IAAI,QAAQ,IAAI,QAAQ,OAAO,QAAQ,EAAE,CAAC,OAAO,MAAM,IAAI;CAC7D,EAAE,CACJ;AACF;;;;;;AAQA,IAAa,cAAc,MAAkB,eAAyB,WAAmB,WAA0B;CACjH,MAAM,EAAE,UAAU;CAElB,MAAM,QADQ,aAAa,KACb,CAAA,CAAM;CACpB,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,aAAa,CAAC,CAAC,CAAC,QAAQ,UAAU,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CAC/G,IAAI,QAAQ,WAAW,KAAK,QAAQ,SAAS,SAAS,GACpD;CAEF,IAAI,QAAQ,WAAW,GAAG;EAExB,SAAS,MAAM,QAAQ,IAAI,WAAW,MAAM;EAC5C;CACF;CAEA,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,OAAO,QAAQ,KAAK,UAAU,MAAM,IAAI,YAAY,OAAO,MAAM,CAAC,MAAM,OAAO,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI;CAC1G,MAAM,UAAU,aAAa,OAAO,QAAQ,OAAO;CACnD,MAAM,WAAW,YAAY,QAAQ,OAAO,UAAU,CAAC,OAAO,MAAM,IAAI;CACxE,IAAI,QAAQ,MAAM,UAAU,WAAW,MAAM,QAAQ,WAAW,MAAM,EAAE,GACtE;CAGF,MAAM,SAAS,YAAY,QAAQ,GAAG,KAAK,MAAM,KAAK;CACtD,MAAM,UAAU,CAAC,GAAG,QAAQ,KAAK,WAAW;EAAE,MAAM,MAAM;EAAM,IAAI,MAAM;CAAG,EAAE,GAAG;EAAE,MAAM;EAAU;CAAO,CAAC;CAC5G,MAAM,YAAY,MAAM,QAAQ,OAAO;CACvC,MAAM,YAAY,UAAU,OAAO,UAAU,EAAE,KAAK,YAAY,QAAQ,IAAI;CAG5E,MAAM,UAAoB,CAAC;CAC3B,IAAI,SAAS;CACb,KAAK,MAAM,SAAS,SAAS;EAC3B,QAAQ,KAAK,MAAM;EACnB,UAAU,OAAO,MAAM,CAAC,KAAK,OAAO,MAAM,CAAC,OAAO;CACpD;CACA,MAAM,QAAQ,YAAY,KAAK;CAC/B,KAAK,SAAS;EACZ;EACA,WAAW,EAAE,QAAQ,KAAK,IAAI,OAAO,UAAU,SAAS,EAAE;EAC1D,SAAS,kBAAkB,GAAG,OAAO;EACrC,WAAW;CACb,CAAC;AACH;;AAIA,IAAa,iBAAiB,MAAkB,SAAmB,SAA8B;CAC/F,MAAM,EAAE,UAAU;CAClB,MAAM,SAAS,UAAU,KAAK;CAC9B,MAAM,QAAQ,OAAO;CACrB,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,OAAO,CAAC,CAAC,CAAC,QAAQ,UAAU,SAAS,KAAK,QAAQ,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC;CACzG,IAAI,QAAQ,WAAW,GACrB;CAGF,MAAM,UAAU,aAAa,OAAO,QAAQ,OAAO;CACnD,MAAM,UAAU,QAAQ,KACrB,WAAW;EAAE,MAAM,MAAM;EAAM,IAAI,MAAM;CAAG,EAC/C;CACA,IAAI,SAAS,QAAQ,EAAE,CAAC;CACxB,IAAI,QAAQ,QAAQ,KAAK,SAAS,GAAG;EACnC,MAAM,SAAS,QAAQ,EAAE,CAAC,MAAM,MAAM,IAAI,SAAS,OAAO,GAAG,KAAK;EAClE,QAAQ,KAAK;GAAE,MAAM,QAAQ,EAAE,CAAC;GAAM,IAAI,QAAQ,EAAE,CAAC;GAAI;EAAO;EAChE,SAAS,QAAQ,EAAE,CAAC,OAAO,KAAK;CAClC;CAEA,KAAK,SAAS;EACZ;EACA,WAAW,EAAE,QAAQ,KAAK,IAAI,QAAQ,MAAM,IAAI,MAAM,EAAE;EACxD,SAAS,kBAAkB,GAAG,CAAC,CAAC;EAChC,WAAW,QAAQ,OAAO,iBAAiB;CAC7C,CAAC;AACH;AAEA,IAAM,mBAA6B;CACjC;CACA;CACA,YAAY,OAAO,aAAa,SAAS,KAAK,UAAU,MAAM,IAAI,YAAY,MAAM,MAAM,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI;CAC9G;AACF;;;;;;;;AASA,IAAa,eAAe,OAAoB,UAA0B;CACxE,MAAM,OAAO,MAAM,MAAM,SAAS,CAAC,CAAC,KAAK,MAAM,IAAI;CACnD,OAAO,QAAQ,QAAQ,KAAK,aAAa,SAAS,KAAK,aAAa;AACtE;AAEA,IAAa,oBAA+B,CAG1C,qBAAqB,gBAAgB,GACrC,gBAAgB;CAAE;CAAW,SAAS;CAAa;CAAY;CAAW,mBAAmB;CAAM;AAAc,CAAC,CACpH;AAOA,IAAM,eAAe,UAAiC,aAAa,KAAK,CAAC,CAAC,KAAK,SAAS,KAAK,UAAU,IAAI;;AAG3G,IAAa,kBAA2B,SAAS;CAC/C,MAAM,UAAU,YAAY,KAAK,KAAK;CACtC,MAAM,UAAU,KAAK,MAAM,MAAM,qBAAqB,KAAK,KAAK,CAAC;CACjE,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,QAAQ,WAAW,QAAQ,SAAS,CAAC,IAAI,OAAO,EAAE,CAAC;CACjG,OAAO;AACT;;AAGA,IAAa,mBAA4B,SAAS;CAChD,KAAK,SAAS,EAAE,SAAS,kBAAkB,GAAG,CAAC,CAAC,EAAE,CAAC;CACnD,OAAO;AACT;AAGA,IAAM,mBAAmB,MAAkB,UAA2B;CACpE,MAAM,QAAQ,aAAa,KAAK,KAAK;CACrC,MAAM,QAAQ,KAAK,MAAM,UAAU,KAAK;CACxC,MAAM,QAAQ,MAAM,WAAW,SAAS,SAAS,KAAK,UAAU,QAAQ,SAAS,KAAK,UAAU,EAAE;CAClG,IAAI,QAAQ,GACV,OAAO;CAET,MAAM,SAAS,KAAK,IAAI,MAAM,SAAS,GAAG,KAAK,IAAI,GAAG,QAAQ,KAAK,CAAC;CACpE,MAAM,UAAU,IAAI,IAAI,KAAK,MAAM,MAAM,qBAAqB,KAAK,KAAK,CAAC,CAAC;CAC1E,QAAQ,IAAI,MAAM,MAAM,CAAC,UAAU,IAAI;CACvC,QAAQ,IAAI,MAAM,OAAO,CAAC,UAAU,IAAI;CACxC,KAAK,SAAS;EACZ,WAAW,EAAE,QAAQ,MAAM,OAAO,CAAC,aAAa,KAAK;EACrD,SAAS,kBAAkB,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,IAAI,CAAC,CAAC;CAClE,CAAC;CACD,OAAO;AACT;AAEA,IAAa,YAAqB,SAAS,gBAAgB,MAAM,EAAE;AACnE,IAAa,cAAuB,SAAS,gBAAgB,MAAM,CAAC;;;;;;;;ACtWpE,IAAa,kBAAkB,UAA+B;CAC5D,MAAM,OAAO,MAAM,MAAM,SAAS;CAClC,MAAM,UAAU,MAAM,MAAM,qBAAqB,KAAK,KAAK,CAAC;CAG5D,QADE,QAAQ,SAAS,IAAI,QAAQ,KAAK,WAAW,KAAK,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,MAAM,UAAU,KAAK,IAAI,CAAC,EAAA,CAC5F,QAAQ,SAAuB,QAAQ,IAAI;AAC1D;;AAOA,IAAa,qBAAqB,WAAW,OAAgB;;;;;;;AAQ7D,IAAa,iBAA0B,SAAqB;CAC1D,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;CAEtC,MAAM,OADO,KAAK,MAAM,MAAM,SACjB,CAAA,CAAK,KAAK,GAAG;CAC1B,IAAI,CAAC,QAAQ,KAAK,SAAS,SAAS,KAAK,KAAK,aAAa,SAAS,KAAK,aAAa,IACpF,OAAO;CAGT,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,aAAa,IAAI;CACzD,KAAK,SAAS;EACZ,SAAS;GAAE,MAAM,KAAK;GAAM,IAAI,KAAK;GAAI,QAAQ;EAAK;EACtD,WAAW,kBAAgB,OAAO,KAAK,OAAO,CAAC;EAC/C,aAAa,mBAAmB,GAAG,IAAI;CACzC,CAAC;CACD,OAAO;AACT;AAMA,IAAa,kBAA2B,SAAqB;CAC3D,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;CACtC,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS;CACvC,MAAM,UAAU,KAAK,KAAK,GAAG;CAC7B,IAAI,SAAS;EACX,MAAM,WAAW,KAAK,KAAK,OAAO;EAClC,IAAI,YAAY,QAAQ,SAAS,SAAS,OAAO;GAG/C,MAAM,SAAS,IAAI,OAAO,cAAc,KAAK,KAAK,CAAC;GACnD,MAAM,UAAwB,CAAC;GAC/B,KAAK,SAAS,UAAU,SAAS;IAC/B,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,UAAU,IAAI;IACtD,QAAQ,KAAK;KAAE,MAAM,KAAK;KAAM;IAAO,CAAC;GAC1C,CAAC;GAED,IAAI,QAAQ,SAAS,GACnB,KAAK,SAAS,EAAE,QAAQ,CAAC;EAE7B;CACF;CAEA,OAAO;AACT;AAEA,IAAa,kBAA2B,SAAqB;CAC3D,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;CACtC,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS;CACvC,MAAM,UAAU,KAAK,KAAK,GAAG;CAC7B,IAAI,SACE;MAAA,QAAQ,QAAQ,GAAG;GAGrB,MAAM,aAAa,cAAc,KAAK,KAAK;GAC3C,MAAM,UAAwB,CAAC;GAC/B,KAAK,SAAS,UAAU,SAAS;IAC/B,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,UAAU,IAAI;IACtD,QAAQ,KAAK;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK,OAAO;IAAW,CAAC;GAC9D,CAAC;GAED,IAAI,QAAQ,SAAS,GACnB,KAAK,SAAS,EAAE,QAAQ,CAAC;EAE7B;;CAGF,OAAO;AACT;AAMA,IAAa,gBAAyB,SAAqB;CACzD,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;CACtC,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS;CACvC,MAAM,UAAU,KAAK,KAAK,GAAG;CAC7B,IAAI,WAAW,QAAQ,aAAa;EAClC,MAAM,OAAO,QAAQ;EACrB,MAAM,iBAAiB,KAAK,MAAM,IAAI,YAAY,GAAG,SAAS,MAAM,OAAO,CAAC;EAC5E,MAAM,cAAc,KAAK,MAAM,IAAI,YAAY,GAAG,SAAS,MAAM,IAAI,CAAC;EACtE,MAAM,UAAwB,CAC5B;GACE,MAAM,QAAQ,UAAU;GACxB,IAAI,QAAQ,UAAU,OAAO,eAAe;GAC5C,QAAQ;EACV,GACA;GACE,MAAM,KAAK,UAAU;GACrB,IAAI,KAAK,UAAU,OAAO,YAAY;GACtC,QAAQ;EACV,CACF;EAEA,KAAK,SAAS;GACZ;GACA,WAAW,kBAAgB,OAAO,MAAM,YAAY,SAAS,CAAC;GAC9D,gBAAgB;EAClB,CAAC;CACH;CAEA,OAAO;AACT;AAEA,IAAa,cAAuB,SAAqB;CACvD,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;CACtC,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS;CACvC,MAAM,UAAU,KAAK,KAAK,GAAG;CAC7B,IAAI,WAAW,QAAQ,aAAa;EAClC,MAAM,OAAO,QAAQ;EACrB,MAAM,iBAAiB,KAAK,MAAM,IAAI,YAAY,GAAG,SAAS,MAAM,OAAO,CAAC;EAC5E,MAAM,cAAc,KAAK,MAAM,IAAI,YAAY,GAAG,SAAS,MAAM,IAAI,CAAC;EACtE,MAAM,UAAwB,CAC5B;GACE,MAAM,KAAK,UAAU;GACrB,IAAI,KAAK,UAAU,OAAO,YAAY;GACtC,QAAQ;EACV,GACA;GACE,MAAM,QAAQ,UAAU;GACxB,IAAI,QAAQ,UAAU,OAAO,eAAe;GAC5C,QAAQ;EACV,CACF;EAEA,KAAK,SAAS;GACZ;GACA,WAAW,kBAAgB,OAAO,MAAM,YAAY,SAAS,CAAC;GAC9D,gBAAgB;EAClB,CAAC;CACH;CAEA,OAAO;AACT;;AAOA,IAAa,eAAe,UAA2C;CACrE,MAAM,CAAC,QAAQ,eAAe,KAAK;CACnC,OAAO,QAAQ,MAAM,IAAI,YAAY,KAAK,aAAa,MAAM,KAAK,aAAa,EAAE;AACnF;;AAQA,IAAM,YAAY;;;;;;;AAQlB,IAAa,cAAc,UAAgC;CACzD,MAAM,OAAO,YAAY,KAAK,CAAC,EAAE,KAAK;CACtC,OAAO,CAAC,CAAC,QAAQ,UAAU,KAAK,IAAI;AACtC;;;;;AAMA,IAAa,uBAAuB,MAAkB,EAAE,OAAO,UAA6B;CAC1F,MAAM,EAAE,UAAU;CAClB,MAAM,CAAC,QAAQ,eAAe,KAAK;CACnC,IAAI,CAAC,MACH,OAAO;CAGT,MAAM,SAAS,IAAI,OAAO,cAAc,KAAK,IAAI,KAAK,KAAK,IAAI;CAC/D,MAAM,SAAS,IAAI,gBAAgB,KAAK,EAAE,IAAI,IAAI;CAClD,KAAK,SAAS;EACZ,SAAS,CACP;GAAE,MAAM,KAAK,UAAU;GAAM,IAAI,KAAK,aAAa;GAAM,QAAQ;EAAO,GACxE;GAAE,MAAM,KAAK,aAAa;GAAM,IAAI,KAAK,aAAa;GAAI;EAAO,CACnE;EACA,WAAW,kBAAgB,OAAO,KAAK,UAAU,OAAO,OAAO,SAAS,OAAO,MAAM;EACrF,SAAS,kBAAkB,GAAG,CAAC,CAAC;EAChC,WAAW;CACb,CAAC;CAED,OAAO;AACT;;AAOA,IAAa,cAAuB,SAAqB;CACvD,MAAM,EAAE,UAAU;CAClB,MAAM,OAAO,MAAM,MAAM,SAAS;CAClC,MAAM,QAAQ,eAAe,KAAK;CAClC,IAAI,MAAM,WAAW,GACnB,OAAO;CAKT,MAAM,SAAS,YACb,MAAM,KAAK,SAAS;EAClB,MAAM,CAAC,MAAM,MAAM,SAAS,MAAM,IAAI;EACtC,OAAO;GAAE;GAAM,IAAI,KAAK,IAAI,KAAK,GAAG,MAAM,IAAI,MAAM;EAAE;CACxD,CAAC,CACH;CAEA,KAAK,SAAS;EACZ,SAAS,OAAO,KAAK,WAAW;GAAE,MAAM,MAAM;GAAM,IAAI,MAAM;EAAG,EAAE;EACnE,WAAW,kBAAgB,OAAO,OAAO,EAAE,CAAC,IAAI;EAChD,SAAS,kBAAkB,GAAG,CAAC,CAAC;EAChC,WAAW;CACb,CAAC;CAED,OAAO;AACT;;AAGA,IAAa,cAAuB,SAAqB;CACvD,MAAM,EAAE,UAAU;CAClB,MAAM,QAAQ,eAAe,KAAK;CAClC,IAAI,MAAM,WAAW,GACnB,OAAO;CAGT,MAAM,OAAO,cAAc,KAAK;CAChC,MAAM,SAAS,MAAM,EAAE,CAAC,SAAS;CACjC,MAAM,UAAwB,MAC3B,QAAQ,SAAU,SAAS,KAAK,SAAS,SAAS,KAAK,SAAS,MAAO,CAAA,CACvE,KAAK,UAAU;EACd,MAAM,KAAK,UAAU;EACrB,IAAI,KAAK,aAAa;EACtB,QAAQ,IAAI,OAAO,OAAO,KAAK,KAAK,KAAK,SAAS,WAAW;CAC/D,EAAE;CAEJ,IAAI,QAAQ,SAAS,GACnB,KAAK,SAAS,EAAE,QAAQ,CAAC;CAG3B,OAAO;AACT;;AAGA,IAAa,iBACX,SAAO,GAAG;CAIR;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;EACL,OAAO;CACT;CAKA;EACE,KAAK;EACL,KAAK;EACL,QAAQ,SAAS;GACf,MAAM,MAAM,KAAK,MAAM,UAAU,KAAK;GAEtC,KAAK,SAAS;IACZ,SAAS,CAAC;KAAE,MAAM;KAAK,IAAI;KAAK,QAAA;IAAO,CAAC;IACxC,WAAW,kBAAgB,OAAO,MAAM,CAAa;GACvD,CAAC;GACD,OAAO;EACT;CACF;CAKA;EACE,KAAK;EAEL,MAAM,SAAS;GACb,MAAM,OAAO,KAAK,MAAM,MAAM,SAAS;GACvC,MAAM,OAAO,KAAK,KAAK,KAAK,MAAM,UAAU,KAAK,IAAI;GACrD,IACE,QACA,KAAK,MAAM,IAAI,OAAO,KAAK,UAAU,EAAE,CAAC,CAAC,SAAS,KAAK,MAAM,IAAI,OAAO,KAAK,UAAU,IAAI,CAAC,CAAC,WAAW,GACxG;IACA,MAAM,OAAO,KAAK,KAAK,IAAI;IAC3B,IAAI,MAAM;KACR,KAAK,SAAS,EAAE,WAAW,kBAAgB,OAAO,KAAK,aAAa,IAAI,EAAE,CAAC;KAC3E,OAAO;IACT;GACF;GAEA,OAAO;EACT;CACF;CAMA;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP;CACA;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP;CACA;EACE,KAAK;EACL,OAAO;CACT;CACA;EACE,KAAK;EACL,OAAO;CACT;CAKA;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP;CACA;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP;CAIA;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP;CAIA;EACE,KAAK;EACL,gBAAgB;EAChB,KAAK;CACP;AACF,CAAC;;;ACjXH,IAAM,WAAmC;CAAE,MAAM;CAAc,KAAK;AAAW;AAE/E,IAAM,cAAc;AAIpB,IAAM,mBAAmB;;AAGzB,IAAa,oBAA6B,SAAS;CACjD,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,UAAU,KAAK,IAAI;CACjE,IAAI,KAAK,KAAK,KAAK,MAAM,MAAM,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC,KAAK,KAAK,IAAI,GACvE,OAAO;CAGT,KAAK,SAAS;EACZ,SAAS;GAAE,MAAM,KAAK;GAAM,IAAI,KAAK;GAAI,QAAQ;EAAY;EAC7D,WAAW,kBAAgB,OAAO,KAAK,OAAO,CAAkB;CAClE,CAAC;CACD,KAAK,MAAM;CACX,OAAO;AACT;;;;;AAMA,IAAa,SAAS,UAAwB,CAAC,MAAiB;CAC9D,MAAM,EAAE,MAAM,QAAQ;EAAE,GAAG;EAAU,GAAG;CAAQ;CAEhD,OAAO;EAEL,WAAW,UACT,MAAM;GACJ,cAA6B,WAAW;GAExC,YAAY,MAAkB;IAC5B,KAAK,cAAc,KAAK,QAAQ,IAAI;GACtC;GAEA,OAAO,QAAoB;IACzB,IAAI,OAAO,cAAc,OAAO,gBAAgB,OAAO,cACrD,KAAK,cAAc,KAAK,QAAQ,OAAO,IAAI;GAE/C;GAEA,QAAgB,MAAiC;IAC/C,IAAI,CAAC,KAAK,UACR,OAAO,WAAW;IAEpB,MAAM,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,UAAU,KAAK,IAAI;IACjE,IAAI,CAAC,iBAAiB,KAAK,KAAK,IAAI,KAAK,CAAC,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC,KAAK,KAAK,IAAI,GAClF,OAAO,WAAW;IAEpB,OAAO,WAAW,IAAI,CAAC,WAAW,OAAO;KAAE,QAAQ,IAAI,kBAAkB,IAAI;KAAG,MAAM;IAAE,CAAC,CAAC,CAAC,MAAM,KAAK,EAAE,CAAC,CAAC;GAC5G;EACF,GACA,EAAE,cAAc,WAAW,OAAO,YAAY,CAChD;EAGA,WAAW,UACT,MAAM;GACJ;GACA;GACA,QAAuB;GACvB;GAEA,YAAY,MAAkB;IAC5B,KAAK,OAAO;IACZ,MAAM,YAAY,KAAK;IACvB,IAAI,iBAAiB,SAAS,CAAC,CAAC,aAAa,UAC3C,UAAU,MAAM,WAAW;IAK7B,MAAM,SAAS,OAAO,GAAG,QAAQ,CAAA,CAC9B,WAAW,kEAAkE,CAAA,CAC7E,WAAW;KAAE,QAAQ;KAAU,cAAc;KAAK,SAAS;IAAI,CAAC,CAAA,CAChE,OAAO,OAAO,GAAG,KAAK,CAAC,CAAC,WAAW,sBAAsB,CAAC,CAAC,OAAO,OAAO,IAAI,mBAAmB,CAAC,CAAC;IACrG,KAAK,SAAS,OAAO,GAAG,KAAK,CAAC,CAAC,WAAW,iBAAiB,CAAC,CAAC,OAAO,MAAM,CAAC,CAAC;IAE5E,OAAO,KAAK,iBAAiB,cAAc,UAAU;KACnD,MAAM,eAAe;KACrB,iBAAiB,KAAK,IAAI;IAC5B,CAAC;IACD,KAAK,OAAO,MAAM,UAAU;IAC5B,UAAU,YAAY,KAAK,MAAM;IAEjC,MAAM,gBAAgB,KAAK,SAAS;IACpC,KAAK,UAAU,iBAAiB,UAAU,UAAU,SAAS;KAAE,SAAS;KAAM,SAAS;IAAK,CAAC;IAC7F,KAAK,SAAS;GAChB;GAEA,UAAU;IACR,KAAK,UAAU;IACf,KAAK,OAAO,OAAO;IACnB,IAAI,KAAK,SAAS,MAChB,qBAAqB,KAAK,KAAK;GAEnC;GAEA,OAAO,QAAoB;IACzB,IACE,OAAO,cACP,OAAO,gBACP,OAAO,mBACP,OAAO,gBACP,OAAO,iBAEP,KAAK,SAAS;GAElB;GAEA,WAAW;IACT,IAAI,KAAK,SAAS,MAChB,qBAAqB,KAAK,KAAK;IAEjC,KAAK,QAAQ,4BAA4B;KACvC,KAAK,QAAQ;KACb,KAAK,SAAS;IAChB,CAAC;GACH;GAEA,WAAW;IACT,MAAM,EAAE,UAAU,KAAK;IACvB,MAAM,OAAO,MAAM,IAAI,OAAO,MAAM,UAAU,KAAK,IAAI;IAEvD,MAAM,SADQ,KAAK,KAAK,YAAY,KAAK,KAAK,KAAK,MAAM,MAAM,CAAC,MAAM,MAAM,SAAS,CAAC,CAAC,KAAK,KAAK,IAAI,IAC9E,KAAK,KAAK,YAAY,KAAK,IAAI,IAAI;IAC1D,IAAI,CAAC,QAAQ;KACX,KAAK,OAAO,MAAM,UAAU;KAC5B;IACF;IAEA,MAAM,EAAE,MAAM,KAAK,KAAK,WAAW,sBAAsB;IACzD,KAAK,OAAO,MAAM,MAAM,IAAI,OAAO,MAAM,OAAO,UAAU,EAAE;IAC5D,KAAK,OAAO,MAAM,OAAO,GAAG,IAAA,GAAqB;IACjD,KAAK,OAAO,MAAM,UAAU;GAC9B;EACF,CACF;EAEA,aAAW,MAAM;GAEf,oBAAoB;IAClB,UAAU;IACV,QAAQ;IACR,WAAW;IACX,SAAS;IACT,YAAY;IACZ,OAAO;IACP,QAAQ;GACV;GACA,yBAAyB;IACvB,SAAS;IACT,cAAc;IAEd,UAAU;IACV,OAAO;GACT;EACF,CAAC;CACH;AACF;;;;ACnLA,IAAM,kBAAkB;;;;AAKxB,IAAM,cAAc,aAAa,UAAU;CACzC,OAAO,WAAW,UAChB,MAAM;EACJ,SAA+C;EAE/C,YAAY,MAAkB;GAC5B,IAAI,CAAC,YACH;GAGF,MAAM,QAAQ,KAAK,MAAM,IAAI,OAAO,CAAC;GAGrC,IAAI,CAFS,KAAK,MAAM,SAAS,MAAM,MAAM,MAAM,EACrC,CAAA,CAAK,MAAM,eACpB,GAEH,KAAK,SAAS,iBAAiB;IAC7B,KAAK,SAAS;IAEd,KAAK,SAAS;KACZ,SAAS,CAAC;MAAE,MAAM;MAAG,IAAI;MAAG,QAAA;KAAO,CAAC;KACpC,WAAW,kBAAgB,OAAO,CAAa;IACjD,CAAC;GACH,CAAC;EAEL;EAEA,UAAU;GACR,IAAI,KAAK,UAAU,MAAM;IACvB,aAAa,KAAK,MAAM;IACxB,KAAK,SAAS;GAChB;EACF;CACF,CACF;AACF;;;;AAKA,IAAa,UAAU,aAAa,UAAU,CAC5C,WAAW,UAAU,GAErB,cAAY,kBAAkB,IAAI,OAAO;CACvC,MAAM,OAAO,GAAG,MAAM,MAAM,SAAS;CAKrC,IAAI,CAAC,GAAG,YAAY;EAClB,MAAM,UAAU,GAAG,MAAM,UAAU,KAAK;EACxC,IAAI,WAAW,MAAM;GAEnB,MAAM,cAAc,KAAK,KAAK,OAAO;GACrC,IAAI,CAAC,aACH,OAAO;GAIT,IAAI,UAAU,YAAY,aAAa,QAAQ,UAAU,YAAY,aAAa,IAAI;IACpF,MAAM,OAAO,GAAG,WAAW,UAAU,KAAK;IAC1C,MAAM,WAAW,QAAQ,OAAO,KAAK,KAAK,IAAI,IAAI,KAAA;IAClD,IAAI,CAAC,UACH,OAAO,CAAC,EAAE,WAAW,kBAAgB,OAAO,YAAY,aAAa,IAAI,EAAE,CAAC;SAE5E,IAAI,YAAY,QAAQ,SAAS,OAE/B,OAAO,CAAC,EAAE,WAAW,kBAAgB,OAAO,YAAY,aAAa,EAAE,EAAE,CAAC;SACrE,IAAI,YAAY,QAAQ,SAAS,OAEtC,OAAO,CAAC,EAAE,WAAW,kBAAgB,OAAO,YAAY,aAAa,IAAI,EAAE,CAAC;SAG5E,IAAI,UAAU,MAAM;KAClB,IAAI,YAAY,UAAU,GAExB,OAAO,CAAC;UAGR,OAAO,CAAC,EAAE,WAAW,kBAAgB,OAAO,YAAY,UAAU,OAAO,CAAC,EAAE,CAAC;IAEjF,OAEE,OAAO,CAAC,EAAE,WAAW,kBAAgB,OAAO,YAAY,aAAa,EAAE,EAAE,CAAC;GAIlF;EACF;EAEA,OAAO;CACT;CAIA,IAAI,GAAG,WAAW,kBAAkB,GAClC,OAAO;CAMT,IAAI,SAAS;CACb,MAAM,UAAwB,CAAC;CAC/B,GAAG,QAAQ,aAAa,OAAO,KAAK,OAAO,KAAK,WAAW;EACzD,MAAM,OAAO,GAAG,WAAW,IAAI,OAAO,KAAK;EAC3C,MAAM,QAAQ,KAAK,KAAK,MAAM,eAAe;EAC7C,IAAI,OAAO;GAGT,MAAM,YADY,GAAG,WAAW,MAAM,SACpB,CAAA,CAAU,KAAK,GAAG,WAAW,UAAU,KAAK,IAAI;GAIlE,IADmB,UAAU,WAAW,UAAU,QAAQ,QAAQ,WAAW,UAAU,KAAK,GAE1F;GAWF,IAFc,KAAK,QAAQ,QAAQ,EAAE,EAAE,UAAU,OACvB,OAAO,MAAM,UAAU,OAAO,QAC3C;IACX,QAAQ,KAAK;KAAE,MAAM,KAAK,OAAO;KAAG,IAAI;IAAI,CAAC;IAC7C;GACF;GAGA,IAAI,UAAU,KAAK;IACjB,IAAI,QAAQ,KAAK,IAAI;KACnB,MAAM,OAAO,GAAG,MAAM,IAAI,OAAO,KAAK;KACtC,IAAI,KAAK,KAAK,MAAM,OAAO,GAAG;MAC5B,IAAI,KAAK,SAAS,GAAG;OAEnB,SAAS;OACT;MACF,OAAO;OAEL,QAAQ,KAAK;QAAE,MAAM,KAAK,OAAO;QAAG,IAAI;OAAI,CAAC;OAC7C;MACF;KACF;IACF;IACA;GACF;GAKA,IAAI,CADgB,KAAK,KAAK,GAAG,MAAM,UAAU,KAAK,IACjD,CAAA,EAAa,cAAc;IAC9B,SAAS;IACT;GACF;GAGA,MAAM,OAAO,KAAK,KAAK,KAAK;GAC5B,IAAI,MAAM,aAAa,SAAS,MAAM,aAAa,MAAM,UAAU,KAAK;IACtE,SAAS;IACT;GACF;GAEA,IAAI,UAAU;IACZ;IACA,MAAM;KAAE,MAAM,KAAK;KAAM,IAAI,KAAK;IAAG;IACrC,GAAG,CAAC,OAAO,GAAG;IACd,GAAG,CAAC,OAAO,GAAG;IACd,QAAQ;KAAE,MAAM,OAAO,SAAS;KAAG,QAAQ,OAAO;IAAO;GAC3D,GAAA;IAAA,YAAA;IAAA,GAAA;IAAA,GAAA;IAAA,GAAA,KAAA;GAAA,CAAC;EACH;CACF,CAAC;CAED,IAAI,QAAQ,SAAS,GAAG;EACtB,IAAI,aAAa,EAAE,QAAQ,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,KAAA;EAAA,CAAC;EAC5B,OAAO,CAAC,EAAE,QAAQ,CAAC;CACrB,OAAO,IAAI,QAAQ;EACjB,IAAI,UAAO,KAAA,GAAA;GAAA,YAAA;GAAA,GAAA;GAAA,GAAA;GAAA,GAAA,KAAA;EAAA,CAAC;EACZ,OAAO,CAAC;CACV;CAEA,OAAO;AACT,CAAC,CACH;;;;AClLA,IAAa,QAAQ,UAAuB,CAAC,MAAiB,CAC5D,WAAW,UACT,MAAM;CACJ;CACA;CACA;CACA;CAEA,YAAY,MAAkB;EAC5B,KAAK,OAAO;EAGZ,MAAM,YAAY,KAAK;EACvB,IAAI,iBAAiB,SAAS,CAAC,CAAC,aAAa,UAC3C,UAAU,MAAM,WAAW;EAK7B,KAAK,MAAM,OAAO,GAAG,WAAW,CAAA,CAC7B,WAAW,2EAA2E,CAAA,CACtF,WAAW;GAAE,gBAAgB;GAAS,aAAa;EAAK,CAAC,CAAA,CACzD,OACC,OAAO,GAAG,KAAK,CAAA,CACZ,WAAW,yBAAyB,CAAA,CACpC,OAAO,OAAO,IAAI,QAAQ,QAAQ,kCAAkC,CAAC,CAC1E,CAAC,CAAC;EAEJ,UAAU,YAAY,KAAK,GAAG;EAK9B,MAAM,gBAAgB,KAAK,eAAe;EAC1C,KAAK,UAAU,iBAAiB,UAAU,UAAU,SAAS;GAAE,SAAS;GAAM,SAAS;EAAK,CAAC;EAC7F,KAAK,eAAe;CACtB;CAEA,UAAU;EACR,KAAK,UAAU;EACf,KAAK,IAAI,OAAO;EAChB,IAAI,KAAK,SAAS,MAChB,qBAAqB,KAAK,KAAK;CAEnC;CAEA,OAAO,QAAoB;EACzB,KAAK,IAAI,QAAQ,UAAU,OAAO,KAAK,WAAW,SAAS;EAC3D,IAAI,CAAC,OAAO,KAAK,UACf;EASQ,IACR,OAAO,cACP,OAAO,gBACP,OAAO,mBACP,OAAO,gBACP,OAAO,iBAEP,KAAK,eAAe;CAExB;CAEA,uBAAuB;EACrB,MAAM,EAAE,GAAG,UAAU,KAAK,KAAK,WAAW,sBAAsB;EAEhE,MAAM,MAAM,KAAK,KAAK,MAAM,UAAU,KAAK;EAE3C,IAAI,CAAC,KAAK,KAAK,MAAM,MAAM,SAAS,CAAC,CAAC,KAAK,GAAG,GAAG;GAC/C,KAAK,IAAI,MAAM,UAAU;GACzB;EACF;EAEA,MAAM,OAAO,KAAK,KAAK,YAAY,GAAG;EACtC,MAAM,SAAS,KAAK,KAAK,YAAY,KAAK,IAAI;EAC9C,IAAI,CAAC,QAAQ;GAGX,KAAK,IAAI,MAAM,UAAU;GACzB;EACF;EAIA,MAAM,aAAa,OAAO,MAAM,OAAO,UAAU;EACjD,MAAM,aAAa,IAAI,QAAA;EAEvB,KAAK,IAAI,MAAM,MAAM,GAAG,UAAU;EAClC,KAAK,IAAI,MAAM,OAAO,GAAG,WAAW;EACpC,KAAK,IAAI,MAAM,UAAU;CAC3B;CAEA,iBAAiB;EACf,IAAI,KAAK,SAAS,MAChB,qBAAqB,KAAK,KAAK;EAGjC,KAAK,QAAQ,sBAAsB,KAAK,qBAAqB,KAAK,IAAI,CAAC;CACzE;AACF,CACF,GAEA,MACF;AAEA,IAAM,SAAS,aAAW,MAAM;CAE9B,uBAAuB;EACrB,UAAU;EACV,SAAS;EACT,QAAQ;CACV;CAIA,qBAAqB,EAEnB,WAAW,wBACb;CACA,4BAA4B;EAC1B,SAAS;EACT,cAAc;EACd,UAAU;EACV,OAAO;CACT;CACA,sCAAsC,EACpC,SAAS,IACX;CAEA,qDAAqD;EACnD,SAAS;EACT,eAAe;CACjB;AACF,CAAC;;;;;;;;;;ACrHD,IAAa,YAAY,EAAE,UAAU,OAAO,iBAAgC,CAAC,MAAiB;CAE5F,KAAK,QAAQ,SAAS,CAAC;CAGvB,aAAa;CAGb,OAAO;CAKP,GAAI,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC;CAGlC,YAAY;CAGZ,iBAAiB,EAAE,iBAAiB,EAAE,CAAC;CAIvC,GAAI,WACA,CAAC,IACD;EACE,MAAM,YAAY;EAClB,KAAK;EACL,aAAW,kBAAkB,GAAG,EAC9B,OAAO,cACT,CAAC;CACH;AACN;;;;;AAMA,IAAM,oBAAoB,CACxB,WAAW,UACT,MAAM;CACJ,cAA6B,WAAW;CACxC,YAAY,MAAkB;EAC5B,KAAK,kBAAkB,KAAK,OAAO,IAAI;CACzC;CAEA,OAAO,QAAoB;EACzB,MAAM,mBACJ,OAAO,WAAW,MAAM,qBAAqB,KAAK,MAAM,OAAO,MAAM,MAAM,qBAAqB,KAAK;EACvG,IACE,OAAO,gBACP,OAAO,cACP,OAAO,mBACP,OAAO,gBACP,kBAEA,KAAK,kBAAkB,OAAO,OAAO,OAAO,IAAI;CAEpD;CAEA,kBAA0B,OAAoB,EAAE,UAAU,EAAE,MAAM,MAAM,YAAwB;EAC9F,MAAM,OAAO,MAAM,MAAM,SAAS;EAClC,MAAM,UAAU,KAAK,KAAK,MAAM,UAAU,KAAK,IAAI;EACnD,MAAM,MAAM,MAAM;EAKlB,MAAM,kBAAkB,MAAM,MAAM,qBAAqB,KAAK,KAAK,CAAC,EAAA,CACjE,KAAK,WAAW;GACf,MAAM,OAAO,KAAK,KAAK,MAAM;GAC7B,OAAO,OAAO,SAAS,MAAM,IAAI,IAAI;EACvC,CAAC,CAAA,CACA,QAAQ,UAAqC,SAAS,IAAI;EAC7D,MAAM,cAAc,QAClB,eAAe,MAAM,CAAC,WAAW,aAAa,OAAO,aAAa,OAAO,OAAO;EAElF,MAAM,cAAmC,CAAC;EAC1C,KAAK,IAAI,UAAU,IAAI,OAAO,IAAI,CAAC,CAAC,QAAQ,WAAW,IAAI,OAAO,EAAE,CAAC,CAAC,QAAQ,WAAW;GACvF,MAAM,OAAO,IAAI,KAAK,OAAO;GAC7B,MAAM,OAAO,KAAK,KAAK,KAAK,IAAI;GAChC,IAAI,MAAM;IACR,MAAM,WAAW,IAAI,OAAO,KAAK,aAAa,IAAI;IAClD,MAAM,SAAS,IAAI,OAAO,KAAK,aAAa,EAAE;IAC9C,YAAY,KACV,WAAW,KAAK,EACd,OAAO,GACL,gBACA,SAAS,WAAW,KAAK,UAAU,sBACnC,OAAO,WAAW,KAAK,UAAU,oBACjC,WAAW,KAAK,IAAI,KAAK,yBACzB,YAAY,SAAS,WAAW,sBAClC,EACF,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,KAAK,IAAI,CAC/B;GACF,OAGE,YAAY,KAAK,WAAW,KAAK,EAAE,OAAO,mBAAmB,CAAC,CAAC,CAAC,MAAM,KAAK,MAAM,KAAK,IAAI,CAAC;EAE/F;EAEA,KAAK,cAAc,WAAW,IAAI,WAAW;CAC/C;AACF,GACA,EACE,cAAc,MAAM,EAAE,YACxB,CACF,GAGA,aAAW,MAAM;CACf,iBAAiB;EAGf,aAAa;EACb,aAAa;CACf;CACA,oCAAoC,EAClC,cAAc,IAChB;CAKA,uBAAuB,EAIrB,YAAY,MACd;CACA,qBAAqB,EAInB,eAAe,MACjB;CAGA,0BAA0B;EACxB,WAAW;EACX,iBAAiB;EACjB,cAAc;CAChB;CAEA,yBAAyB,EACvB,aAAa,iCACf;CAKA,wBAAwB,EACtB,iBAAiB,wFACnB;CACA,+CAA+C,EAC7C,iBAAiB,cACnB;AACF,CAAC,CACH;;;;ACjKA,IAAM,YAAoC;CACxC,IAAI;CACJ,KAAK;CACL,IAAI;CACJ,KAAK;CACL,KAAK;CACL,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,IAAI;CACJ,KAAK;CACL,KAAK;CACL,MAAM;CACN,IAAI;CACJ,MAAM;CACN,IAAI;CACJ,IAAI;CACJ,IAAI;CACJ,KAAK;CACL,MAAM;AACR;;AAGA,IAAM,kBAA0C;CAC9C,YAAY;CACZ,KAAK;CACL,YAAY;CACZ,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;CACN,UAAU;CACV,MAAM;CACN,OAAO;CACP,MAAM;CACN,IAAI;CACJ,QAAQ;CACR,KAAK;CACL,MAAM;AACR;AAEA,IAAa,iBAAiB,aAC5B,WAAY,gBAAgB,aAAa,WAAY,KAAA;AAEvD,IAAM,oBAAoB,SAAqC;CAC7D,MAAM,YAAY,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,YAAY;CACrD,OAAO,YAAa,UAAU,cAAc,YAAa,KAAA;AAC3D;AAcA,IAAa,kBAAkB,SAA4B;CACzD,MAAM,aAAqC,CAAC;CAC5C,MAAM,aAAuB,CAAC;CAG9B,MAAM,CAAC,WAAW,IAAI,GAAG,QADV,KAAK,KAAK,CAAC,CAAC,MAAM,gCAAgC,KAAK,CAAC;CAEvE,KAAK,MAAM,SAAS,MAAM;EACxB,MAAM,YAAY,MAAM,QAAQ,GAAG;EACnC,IAAI,YAAY,GAAG;GACjB,MAAM,MAAM,MAAM,MAAM,GAAG,SAAS;GAEpC,WAAW,OADG,MAAM,MAAM,YAAY,CAAC,CAAC,CAAC,QAAQ,gBAAgB,EAC/C;EACpB,OACE,WAAW,KAAK,MAAM,QAAQ,gBAAgB,EAAE,CAAC;CAErD;CAEA,OAAO;EAAE,UAAU,SAAS,YAAY;EAAG;EAAY;CAAW;AACpE;;AAGA,IAAM,cAAc;;AAGpB,IAAM,WAAW;;;;;;;;;;AAWjB,IAAa,aAAa,MAAc,SAAiC;CACvE,MAAM,OAAO,MAAM,WAAW,QAAQ,MAAM,WAAW;CACvD,MAAM,SAAsB,CAAC;CAC7B,IAAI,QAAQ;CACZ,IAAI,UAAU;CACd,IAAI;CAEJ,IAAI;CACJ,IAAI,aAAa;CACjB,IAAI,YAAY;;CAEhB,IAAI;CAGJ,IAAI,WAAuB,CAAC;CAC5B,IAAI,YAAwB,CAAC;CAC7B,MAAM,cAAc;EAClB,IAAI,CAAC,OACH;EAEF,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,IAAI,SAAS,QAAQ,UAAU,MAAM,GAAG,SAAS;GAChF,MAAM,SAAS,SAAS;GACxB,MAAM,QAAQ,UAAU;GACxB,MAAM,KAAK,KAAK;IAAE,MAAM,UAAU,QAAQ,YAAY,SAAS,YAAY;IAAS;IAAQ;GAAM,CAAC;EACrG;EACA,WAAW,CAAC;EACZ,YAAY,CAAC;CACf;CAEA,MAAM,aAAa,SAAoB;EACrC,MAAM;EACN,OAAO,KAAK,IAAI;EAChB,QAAQ;CACV;CAEA,KAAK,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG;EAGlC,MAAM,OAAO,IAAI,SAAS,IAAI,IAAI,IAAI,MAAM,GAAG,EAAE,IAAI;EAErD,MAAM,SAAS,YAAY,KAAK,IAAI;EACpC,IAAI,QAAQ;GACV,MAAM,GAAG,eAAe,cAAc,WAAW;GACjD,aAAa,OAAO,WAAW;GAC/B,YAAY,OAAO,UAAU;GAC7B,UAAU;IACR,SAAS,QAAQ,KAAK,KAAK,KAAA;IAE3B,KAAK,KAAK,IAAI,GAAG,cAAc,aAAa,EAAE;IAC9C,MAAM,CAAC;GACT,CAAC;GACD;EACF;EAKA,IAAI,KAAK,WAAW,aAAa,GAAG;GAClC,MAAM;GACN,QAAQ,KAAA;GACR,aAAa,KAAA;GACb,YAAY,KAAA;GACZ;EACF;EAIA,IAAI,CAAC,OAAO;GACV,IAAI,KAAK,WAAW,MAAM,GAAG;IAC3B,eAAe,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,KAAK;IACtD;GACF;GACA,IAAI,KAAK,WAAW,MAAM,KAAK,SAAS,KAAK,IAAI,KAAK,KAAK,WAAW,GACpE;GAGF,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;EACxB;EAGA,IAAI,KAAK,WAAW,IAAI,GACtB;EAGF,MAAM,UAAU;EAChB,IAAI,YAAY,KAAA,GACd;EAEF,IAAI,KAAK,WAAW,GAAG,GAAG;GACxB,UAAU,KAAK;IAAE,QAAQ;IAAa,MAAM,KAAK,MAAM,CAAC;GAAE,CAAC;GAC3D;EACF,OAAO,IAAI,KAAK,WAAW,GAAG,GAAG;GAC/B,SAAS,KAAK;IAAE,QAAQ;IAAc,MAAM,KAAK,MAAM,CAAC;GAAE,CAAC;GAC3D;EACF,OAAO;GACL,MAAM;GACN,MAAM,OAAO,KAAK,WAAW,GAAG,IAAI,KAAK,MAAM,CAAC,IAAI;GACpD,QAAQ,KAAK,KAAK;IAChB,MAAM;IACN,QAAQ;KAAE,QAAQ;KAAc;IAAK;IACrC,OAAO;KAAE,QAAQ;KAAa;IAAK;GACrC,CAAC;EACH;EACA,YAAY;CACd;CACA,MAAM;CAGN,MAAM,OAAO,OAAO,GAAG,EAAE;CACzB,MAAM,UAAU,MAAM,KAAK,GAAG,EAAE;CAChC,IAAI,QAAQ,SAAS,SAAS,aAAa,QAAQ,QAAQ,SAAS,MAAM,QAAQ,OAAO,SAAS,IAChG,KAAK,KAAK,IAAI;CAGhB,MAAM,eAAe,QAAQ;CAC7B,OAAO;EACL,MAAM;EACN,UAAU,MAAM,WAAW,SAAS,eAAe,iBAAiB,YAAY,IAAI,KAAA;EACpF,OAAO,MAAM,WAAW,SAAS,WAAW,MAAM;EAClD;EACA;EACA,QAAQ,OAAO,QAAQ,UAAU,MAAM,KAAK,SAAS,CAAC;CACxD;AACF;;AAGA,IAAM,cAAc,WAA4C;CAE9D,MAAM,UAAU,OACb,SAAS,UAAU,MAAM,KAAK,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,CAAA,CAC7D,QAAQ,SAAyB,SAAS,KAAA,CAAS;CACtD,IAAI,QAAQ,WAAW,GACrB;CAEF,MAAM,QAAQ,KAAK,IAAI,GAAG,OAAO;CACjC,MAAM,OAAO,KAAK,IAAI,GAAG,OAAO;CAChC,OAAO,UAAU,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG;AACnD;;;;;;;;ACvQA,IAAa,sBAAgE;CAC3E,MAAM,eAAe,OAAO,eAAe;CAC3C,OAAO,eAAe,OAAO,gBAAgB;AAC/C;;AAGA,IAAM,yBAAS,IAAI,IAAkD;AAErE,IAAM,gBAAgB,SAAuD;CAC3E,IAAI,UAAU,OAAO,IAAI,IAAI;CAC7B,IAAI,CAAC,SAAS;EACZ,MAAM,cAAc,oBAAoB,kBAAkB,WAAW,MAAM,IAAI;EAE/E,UAAU,cAAc,QAAQ,QAAQ,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,KAAA,CAAS,IAAI,QAAQ,QAAQ,KAAA,CAAS;EAC9G,OAAO,IAAI,MAAM,OAAO;CAC1B;CAEA,OAAO;AACT;;;;;;;;;;;;;;AAeA,IAAa,iBAAiB,OAC5B,MACA,UACA,SACA,cAAoD,oBAAoB,UAC5B;CAC5C,MAAM,UAAU,MAAM,aAAa,QAAQ;CAC3C,IAAI,CAAC,SACH;CAGF,MAAM,SAAS,UAAU,GAAG,QAAQ,MAAM;CAC1C,MAAM,SAAS,SAAS;CACxB,MAAM,OAAO,QAAQ,SAAS,OAAO,MAAM,MAAM;CACjD,MAAM,QAA4B,CAAC,SAAS,uBAAuB,CAAC;CACpE,cACE,QACA,MACA,cACC,MAAM,YAAY;EACjB,MAAM,OAAO,MAAM,MAAM,SAAS;EAClC,IAAI,CAAC,SAAS;GACZ,KAAK,YAAY,SAAS,eAAe,IAAI,CAAC;GAC9C;EACF;EACA,MAAM,OAAO,SAAS,cAAc,MAAM;EAC1C,KAAK,YAAY;EACjB,KAAK,cAAc;EACnB,KAAK,YAAY,IAAI;CACvB,SACM,MAAM,KAAK,SAAS,uBAAuB,CAAC,CACpD;CAEA,OAAO,SAAS,MAAM,MAAM,CAAC,IAAI;AACnC;AC1DA,IAAa,iBAAiB,aAAW,MAAM;CAC7C,kBAAkB;EAXlB,qBAAqB;EACrB,oBAAoB;EACpB,mBAAmB;EACnB,uBAAuB;EAGvB,iBAAiB;EACjB,oBAAoB;EAMlB,QAAQ;EACR,QAAQ;EACR,cAAc;EACd,YAAY;EACZ,UAAU;EACV,UAAU;EAMV,UAAU;CACZ;CAMA,mBAAmB;EACjB,SAAS;EACT,YAAY;EACZ,KAAK;EACL,QAAQ;EACR,UAAU;EACV,SAAS;EACT,YAAY;EACZ,YAAY;EACZ,UAAU;EACV,YAAY;EACZ,YAAY;CACd;CACA,mBAAmB;EACjB,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,OAAO;EACP,QAAQ;EACR,MAAM;EACN,SAAS;EACT,QAAQ;EACR,cAAc;EACd,YAAY;EACZ,OAAO;EACP,QAAQ;CACV;CACA,yBAAyB,EAAE,YAAY,6BAA6B;CACpE,uBAAuB;EAAE,OAAO;EAAY,QAAQ;EAAY,YAAY;CAAuB;CACnG,0CAA0C,EAAE,WAAW,iBAAiB;CACxE,oCAAoC,EAAE,SAAS,OAAO;CAEtD,iBAAiB;EAAE,SAAS;EAAQ,UAAU;CAAI;CAElD,gBAAgB;EACd,MAAM;EACN,UAAU;EACV,UAAU;EACV,cAAc;EACd,YAAY;EACZ,OAAO;CACT;CACA,iBAAiB;EAAE,MAAM;EAAY,OAAO;EAAmB,YAAY;CAAM;CAEjF,iBAAiB;EAAE,MAAM;EAAY,oBAAoB;CAAe;CACxE,uBAAuB,EAAE,OAAO,kCAAkC;CAClE,yBAAyB,EAAE,OAAO,qCAAqC;CACvE,iBAAiB;EAAE,MAAM;EAAY,OAAO;CAAyB;CAErE,kBAAkB;EAAE,mBAAmB;EAAQ,MAAM;EAAY,OAAO;CAAyB;CAEjG,wDAAsD,EAAE,SAAS,OAAO;CACxE,yDAAuD,EAAE,SAAS,OAAO;CAMzE,qBAAqB;EACnB,SAAS;EACT,YAAY;EACZ,KAAK;EACL,QAAQ;EACR,UAAU;EACV,SAAS;EACT,aAAa;EACb,YAAY;EACZ,OAAO;EACP,YAAY;EACZ,UAAU;CACZ;CACA,iCAAiC,EAAE,kBAAkB,OAAO;CAC5D,0BAA0B;EAAE,OAAO;EAAY,QAAQ;EAAY,MAAM;CAAW;CAGpF,oBAAoB;EAClB,UAAU;EACV,OAAO;EACP,YAAY;EACZ,sBAAsB;EACtB,UAAU;EACV,cAAc;EACd,YAAY;CACd;CAEA,iBAAiB;EACf,SAAS;EACT,YAAY;EACZ,YAAY;EAGZ,sBAAsB;CACxB;CACA,sDAAoD,EAClD,qBAAqB,0CACvB;CACA,uDAAqD,EACnD,qBAAqB,gCACvB;CAEA,gBAAgB;EACd,SAAS;EACT,SAAS;EACT,gBAAgB;EAChB,KAAK;EACL,OAAO;EACP,oBAAoB;EACpB,YAAY;EAEZ,YAAY;CACd;CACA,iBAAiB,EAAE,OAAO,SAAS;CACnC,iBAAiB;EACf,kBAAkB;EAClB,OAAO;EACP,YAAY;CACd;CACA,iBAAiB;EACf,kBAAkB;EAClB,YAAY;EACZ,cAAc;EACd,SAAS;EACT,OAAO;CACT;CACA,wBAAwB,EAAE,UAAU,WAAW;CAE/C,oBAAoB;EAClB,UAAU;EACV,kBAAkB;EAClB,iBAAiB;EACjB,OAAO;EACP,QAAQ;EACR,SAAS;EACT,YAAY;EACZ,gBAAgB;EAChB,cAAc;EACd,QAAQ;EACR,YAAY;EACZ,OAAO;EACP,UAAU;EACV,YAAY;EACZ,QAAQ;EACR,SAAS;EACT,YAAY;CACd;CACA,+EAA+E,EAAE,SAAS,IAAI;CAE9F,uDAAqD;EACnD,mBAAmB;EACnB,mBAAmB;CACrB;CAEA,kBAAkB,EAAE,YAAY,qBAAqB;CACrD,oBAAoB,EAAE,YAAY,wBAAwB;CAC1D,2DAA2D,EAAE,OAAO,kCAAkC;CACtG,+DAA+D,EAAE,OAAO,qCAAqC;CAE7G,mBAAmB,EACjB,iBACE,0GACJ;AACF,CAAC;;;;;;;AAQD,IAAa,yBACX,aAAW,MAAM,EACf,KAAK;CACH,sBAAsB;CACtB,6BAA6B;CAC7B,0BAA0B;AAC5B,EACF,CAAC;;;;AC9MH,IAAM,gBAAgB;;;;;;;AAQtB,IAAM,eAAe;AACrB,IAAM,aAAa;;;;;;AAOnB,IAAM,aAAa;AACnB,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;;;;;;;;;;;;;;AA2CtB,IAAa,cAAc,UAA6B,CAAC,MAAiB;CACxE;CACA,oBAAoB,GAAG,kBAAkB,OAAO,CAAC;CACjD;CAGA,OAAO,OAAO,mBAAmB,CAAC,CAAC,SAAS,UAC1C,MAAM,SAAS,CAAC,aAAW,YAAY,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAC9D;AACF;;;;;;AAOA,IAAa,kBAAkB,OAAoB,SAAgD;CAEjG,MAAM,QADO,MAAM,SAAS,KAAK,MAAM,KAAK,EAC9B,CAAA,CAAK,MAAM,IAAI;CAC7B,MAAM,UAAU,MAAM;CACtB,MAAM,QAAQ,yBAAyB,KAAK,OAAO;CACnD,IAAI,CAAC,OACH;CAGF,MAAM,OAAO,eAAe,MAAM,EAAE;CACpC,IAAI,KAAK,aAAa,eACpB;CAIF,MAAM,OAAO,MAAM,MAAM,SAAS;CAElC,OAAO,WADM,wBAAwB,KAAK,IAAI,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,MAAM,MAAM,CAAC,EACnE,CAAK,KAAK,IAAI,GAAG,IAAI;AACxC;AAEA,IAAM,qBAAqB,aAA+C;CACxE,OAAO,CAAC,YAAY;CACpB,QAAQ,MAAM,EAAE,YAAY;EAC1B,MAAM,SAAS,eAAe,OAAO,IAAI;EACzC,IAAI,CAAC,UAAU,OAAO,OAAO,WAAW,GACtC;EAGF,MAAM,EAAE,MAAM,OAAO;EACrB,OAAO;GACL;GACA;GACA,YAAY,WAAW,QAAQ;IAC7B,OAAO;IACP,QAAQ,IAAI,gBAAgB,QAAQ,MAAM,SAAS,MAAM,EAAE,GAAG,OAAO;GACvE,CAAC;EACH;CACF;AACF;;AAUA,IAAM,iBAAiB,WAA6C,WAAW,WAAW,WAAW;;AAGrG,IAAM,eAAe,YAAwC,QAAQ,cAAc;AAEnF,IAAM,kBAAN,cAA8B,WAAW;;CAEvC,SAAS;CACT;CACA;CACA;CACA;CACA;;CAEA;CACA;CAEA,YAAY,MAAkB,QAAgB,SAA4B;EACxE,MAAM;EACN,KAAK,QAAQ;EACb,KAAK,UAAU;EACf,KAAK,WAAW;EAChB,KAAK,UAAU,cAAc,QAAQ,MAAM;CAC7C;CAEA,GAAY,OAAsB;EAKhC,OACE,KAAK,YAAY,MAAM,YACtB,KAAK,SAAS,UAAU,aAAa,MAAM,SAAS,UAAU,WAC/D,YAAY,KAAK,QAAQ,MAAM,YAAY,MAAM,QAAQ,KACzD,KAAK,SAAS,kBAAkB,MAAM,SAAS;CAEnD;CAEA,IAAa,kBAA0B;EACrC,MAAM,QAAQ,KAAK,MAAM,OAAO,QAC7B,OAAO,UACN,QAEA,MAAM,KAAK,QAAQ,MAAM,QAAQ,QAAQ,KAAK,YAAY,YAAY,IAAI,SAAS,YAAY,IAAI,IAAI,CAAC,GAC1G,CACF;EAEA,OAAO,gBADW,KAAK,MAAM,OAAO,QAAQ,UAAU,MAAM,GAAG,CAAC,CAAC,SAC9B,kBAAkB,QAAQ;CAC/D;;CAGA,cAAgC;EAC9B,OAAO;CACT;CAEA,UAAyB;EACvB,KAAK,SAAS;EACd,KAAK,WAAW,WAAW;EAC3B,KAAK,YAAY,KAAA;EACjB,KAAK,QAAQ,KAAA;CACf;;;;;CAMA,aAAmB;EACjB,KAAK,OAAO,eAAe;CAC7B;CAEA,MAAe,MAA+B;EAE5C,KAAK,SAAS;EACd,KAAK,QAAQ;EACb,KAAK,WAAW,WAAW;EAC3B,MAAM,OAAO,SAAS,cAAc,KAAK;EACzC,KAAK,YAAY;EACjB,KAAK,QAAQ;EACb,KAAK,YAAY,KAAK,cAAc,CAAC;EAErC,MAAM,OAAO,KAAK,YAAY,SAAS,cAAc,KAAK,CAAC;EAC3D,KAAK,YAAY;EACjB,KAAK,YAAY,IAAI;EAErB,IAAI,KAAK,SAAS,WAAW,UAAU,KAAK,SAAS,WAAW,KAAA,GAAW;GAGzE,KAAK,YAAY,IAAI,gBAAgB,CAAC,WAAW;IAC/C,MAAM,EAAE,UAAU,MAAM;IACxB,MAAM,OAAO,QAAQ,eAAe,WAAW,QAAQ,aAAa,UAAU,KAAK;IACnF,IAAI,SAAS,KAAK,SAAS;KACzB,KAAK,UAAU;KACf,KAAK,YAAY,IAAI;KACrB,KAAK,WAAW;IAClB;GACF,CAAC;GACD,KAAK,UAAU,QAAQ,IAAI;EAC7B;EAEA,OAAO;CACT;CAEA,gBAA6B;EAC3B,MAAM,EAAE,MAAM,UAAU,OAAO,OAAO,YAAY,KAAK;EACvD,MAAM,SAAS,SAAS,cAAc,KAAK;EAC3C,OAAO,YAAY;EAEnB,MAAM,SAAS,OAAO,YAAY,SAAS,cAAc,QAAQ,CAAC;EAClE,OAAO,YAAY;EACnB,OAAO,OAAO;EACd,OAAO,aAAa,iBAAiB,MAAM;EAC3C,OAAO,aAAa,cAAc,UAAU;EAC5C,OAAO,YAAY,MAAM,CAAC;EAC1B,OAAO,iBAAiB,eAAe;GACrC,MAAM,YAAY,OAAO,eAAe,UAAU,OAAO,mBAAmB,KAAK;GACjF,OAAO,aAAa,iBAAiB,OAAO,CAAC,SAAS,CAAC;GACvD,OAAO,aAAa,cAAc,YAAY,WAAW,UAAU;GACnE,KAAK,WAAW;EAClB,CAAC;EAED,IAAI,MAAM;GACR,MAAM,OAAO,OAAO,YAAY,SAAS,cAAc,MAAM,CAAC;GAC9D,KAAK,YAAY;GACjB,MAAM,YAAY,KAAK,YAAY,GAAG;GACtC,IAAI,YAAY,GAAG;IACjB,MAAM,YAAY,KAAK,YAAY,SAAS,cAAc,MAAM,CAAC;IACjE,UAAU,YAAY;IACtB,UAAU,cAAc,KAAK,MAAM,GAAG,YAAY,CAAC;GACrD;GACA,MAAM,OAAO,KAAK,YAAY,SAAS,cAAc,MAAM,CAAC;GAC5D,KAAK,YAAY;GACjB,KAAK,cAAc,YAAY,IAAI,KAAK,MAAM,YAAY,CAAC,IAAI;EACjE;EAEA,IAAI,QAAQ,GAAG;GACb,MAAM,OAAO,OAAO,YAAY,SAAS,cAAc,MAAM,CAAC;GAC9D,KAAK,YAAY;GACjB,KAAK,cAAc,IAAI;EACzB;EACA,IAAI,UAAU,GAAG;GACf,MAAM,OAAO,OAAO,YAAY,SAAS,cAAc,MAAM,CAAC;GAC9D,KAAK,YAAY;GACjB,KAAK,cAAc,IAAI;EACzB;EAEA,MAAM,QAAQ,cAAc,QAAQ;EACpC,IAAI,OAAO;GACT,MAAM,UAAU,OAAO,YAAY,SAAS,cAAc,MAAM,CAAC;GACjE,QAAQ,YAAY;GACpB,QAAQ,cAAc;EACxB;EAEA,IAAI,OAAO;GACT,MAAM,UAAU,OAAO,YAAY,SAAS,cAAc,MAAM,CAAC;GACjE,QAAQ,YAAY;GACpB,QAAQ,cAAc,SAAS,MAAM,QAAQ,KAAK,GAAG;EACvD;EAEA,OAAO;CACT;CAEA,YAAY,MAAyB;EACnC,KAAK,cAAc;EACnB,KAAK,QAAQ,SAAS,KAAK;EAE3B,IAAI,KAAK,OACP,KAAK,MAAM,QAAQ,SAAS,KAAK;EAGnC,KAAK,MAAM,SAAS,KAAK,MAAM,QAAQ;GACrC,IAAI,MAAM,KACR,KAAK,YAAY,SAAS,MAAM,KAAK,MAAM,OAAO,CAAC;GAErD,MAAM,OAAO,KAAK,YAAY,SAAS,cAAc,KAAK,CAAC;GAC3D,KAAK,YAAY;GACjB,MAAM,UAAyB,CAAC;GAChC,MAAM,SAAmB,CAAC;GAC1B,MAAM,QAAkB,CAAC;GACzB,KAAK,MAAM,OAAO,MAAM,MACtB,IAAI,KAAK,YAAY,SACnB,KAAK,gBAAgB,MAAM,KAAK,SAAS,QAAQ,KAAK;QAEtD,KAAK,iBAAiB,MAAM,KAAK,SAAS,QAAQ,KAAK;GAM3D,IAAI,YAAY,KAAK,QAAQ,KAAK,KAAK,MAAM,UAC3C,KAAU,WAAW,KAAK,MAAM,UAAU,MAAM,SAAS,QAAQ,OAAO,OAAO;EAEnF;CACF;CAEA,gBAAgB,MAAmB,KAAc,SAAwB,QAAkB,OAAuB;EAChH,MAAM,UAAU,IAAI,SAAS,aAAa,IAAI,SAAS;EACvD,MAAM,WAAW,IAAI,SAAS,WAAW,IAAI,SAAS;EAEtD,IAAI,IAAI,QAAQ;GACd,KAAK,YAAY,OAAO,IAAI,OAAO,QAAQ,UAAU,MAAM,KAAA,GAAW,UAAU,YAAY,KAAA,CAAS,CAAC;GACtG,MAAM,OAAO,KAAK,YAAY,KAAK,IAAI,OAAO,MAAM,UAAU,YAAY,KAAA,CAAS,CAAC;GACpF,KAAK,eAAe,MAAM,UAAU,IAAI,OAAO,QAAQ,IAAI,OAAO,IAAI;GACtE,QAAQ,KAAK;IAAE,SAAS;IAAM,MAAM;IAAU,OAAO,OAAO;GAAO,CAAC;GACpE,OAAO,KAAK,IAAI,OAAO,IAAI;EAC7B,OAAO;GACL,KAAK,YAAY,OAAO,aAAa,CAAC;GACtC,KAAK,YAAY,OAAO,cAAc,CAAC;EACzC;EAEA,IAAI,IAAI,OAAO;GACb,KAAK,YACH,OAAO,IAAI,MAAM,QAAQ,WAAW,MAAM,KAAA,GAAW,WAAW,UAAU,KAAA,GAAW,eAAe,CACtG;GACA,MAAM,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,MAAM,WAAW,UAAU,KAAA,CAAS,CAAC;GAClF,KAAK,eAAe,MAAM,SAAS,IAAI,MAAM,QAAQ,IAAI,MAAM,IAAI;GACnE,QAAQ,KAAK;IAAE,SAAS;IAAM,MAAM;IAAS,OAAO,MAAM;GAAO,CAAC;GAClE,MAAM,KAAK,IAAI,MAAM,IAAI;EAC3B,OAAO;GACL,KAAK,YAAY,OAAO,eAAe,eAAe,CAAC;GACvD,KAAK,YAAY,OAAO,cAAc,CAAC;EACzC;CACF;CAEA,iBAAiB,MAAmB,KAAc,SAAwB,QAAkB,OAAuB;EAEjH,MAAM,QACJ,IAAI,SAAS,YACT,CACE;GAAE,MAAM;GAAW,QAAQ,IAAI;EAAO,GACtC;GAAE,MAAM;GAAS,OAAO,IAAI;EAAM,CACpC,IACA,CAAC,GAAG;EAEV,KAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,OAAO,KAAK,UAAU,KAAK;GACjC,IAAI,CAAC,MACH;GAEF,MAAM,SAAS,KAAK,SAAS,UAAU,UAAU,KAAK,SAAS,YAAY,YAAY,KAAA;GACvF,KAAK,YAAY,OAAO,KAAK,QAAQ,QAAQ,KAAA,GAAW,MAAM,CAAC;GAC/D,KAAK,YAAY,OAAO,KAAK,OAAO,QAAQ,KAAA,GAAW,MAAM,CAAC;GAC9D,KAAK,YAAY,KAAK,KAAK,SAAS,UAAU,MAAM,KAAK,SAAS,YAAY,MAAM,IAAI,MAAM,CAAC;GAC/F,MAAM,UAAU,KAAK,YAAY,KAAK,KAAK,MAAM,MAAM,CAAC;GAExD,MAAM,OAAO,KAAK,SAAS,YAAY,WAAW;GAClD,MAAM,aAAa,SAAS,WAAW,KAAK,QAAQ,SAAS,KAAK,OAAO;GACzE,IAAI,eAAe,KAAA,GACjB,KAAK,eAAe,SAAS,MAAM,YAAY,KAAK,IAAI;GAE1D,MAAM,SAAS,SAAS,WAAW,SAAS;GAC5C,QAAQ,KAAK;IAAE;IAAS;IAAM,OAAO,OAAO;GAAO,CAAC;GACpD,OAAO,KAAK,KAAK,IAAI;EACvB;CACF;;CAGA,eAAe,MAAmB,MAA8B,MAAc,MAAoB;EAChG,MAAM,EAAE,kBAAkB,KAAK;EAC/B,MAAM,EAAE,SAAS,KAAK;EACtB,IAAI,CAAC,iBAAiB,CAAC,MACrB;EAEF,KAAK,UAAU,IAAI,qBAAqB;EACxC,MAAM,SAAS,SAAS,cAAc,QAAQ;EAC9C,OAAO,OAAO;EACd,OAAO,YAAY;EACnB,OAAO,aAAa,cAAc,mBAAmB,MAAM;EAC3D,OAAO,cAAc;EACrB,OAAO,iBAAiB,cAAc,UAAU,MAAM,eAAe,CAAC;EACtE,OAAO,iBAAiB,UAAU,UAAU;GAC1C,MAAM,gBAAgB;GACtB,cAAc;IAAE;IAAM;IAAM;IAAM;GAAK,GAAG,MAAM;EAClD,CAAC;EAED,KAAK,QAAQ,MAAM;CACrB;CAEA,MAAM,WACJ,UACA,SACA,QACA,OACA,SACe;EACf,MAAM,QAAQ,oBAAoB,KAAK,OAAO,MAAM,MAAM,aAAW,SAAS,IAAI,SAAS;EAC3F,MAAM,CAAC,aAAa,cAAc,MAAM,QAAQ,IAAI,CAClD,OAAO,SAAS,IAAI,eAAe,OAAO,KAAK,IAAI,GAAG,UAAU,SAAS,KAAK,IAAI,KAAA,GAClF,MAAM,SAAS,IAAI,eAAe,MAAM,KAAK,IAAI,GAAG,UAAU,SAAS,KAAK,IAAI,KAAA,CAClF,CAAC;EACD,IAAI,CAAC,KAAK,UAAW,CAAC,eAAe,CAAC,YACpC;EAGF,KAAK,MAAM,EAAE,SAAS,MAAM,WAAW,SAAS;GAC9C,MAAM,YAAY,SAAS,WAAW,cAAc,WAAA,GAAc;GAClE,IAAI,UAAU;IAEZ,MAAM,SAAS,QAAQ,cAAc,2BAA2B;IAChE,QAAQ,cAAc;IACtB,IAAI,QACF,QAAQ,YAAY,MAAM;IAE5B,QAAQ,YAAY,QAAQ;GAC9B;EACF;CACF;AACF;AAMA,IAAM,UAAU,OAA2B,QAAiB,QAAiB,GAAG,YAAmC;CACjH,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,YAAY;EAAC;EAAe,UAAU,WAAW;EAAU,GAAG;CAAO,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CACvG,IAAI,UAAU,KAAA,GAAW;EACvB,MAAM,SAAS,QAAQ,YAAY,SAAS,cAAc,MAAM,CAAC;EACjE,OAAO,cAAc,OAAO,KAAK;CACnC;CACA,IAAI,QAAQ;EACV,MAAM,OAAO,QAAQ,YAAY,SAAS,cAAc,MAAM,CAAC;EAC/D,KAAK,YAAY;EACjB,KAAK,cAAc;CACrB;CAEA,OAAO;AACT;AAEA,IAAM,QAAQ,QAAgB,WAAiC;CAC7D,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,YAAY,CAAC,gBAAgB,UAAU,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAC5F,QAAQ,cAAc;CACtB,OAAO;AACT;AAEA,IAAM,QAAQ,MAAc,WAAiC;CAC3D,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,YAAY,CAAC,gBAAgB,UAAU,WAAW,QAAQ,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,GAAG;CAE5F,QAAQ,cAAc,KAAK,SAAS,IAAI,OAAO;CAC/C,OAAO;AACT;;AAGA,IAAM,UAAU,MAAc,GAAG,YAAmC;CAClE,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,YAAY;EAAC;EAAM;EAAkB,GAAG;CAAO,CAAC,CAAC,KAAK,GAAG;CACjE,OAAO;AACT;AAEA,IAAM,YAAY,OAAe,YAAkC;CACjE,MAAM,UAAU,SAAS,cAAc,KAAK;CAC5C,QAAQ,YAAY;CAEpB,QADqB,YAAY,MAAM,CACvC,CAAA,CAAK,UAAU,IAAI,uBAAuB;CAC1C,MAAM,QAAQ,QAAQ,YAAY,SAAS,cAAc,MAAM,CAAC;CAChE,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,IAAI,SAAS;CACvD,IAAI,SAAS;EACX,MAAM,UAAU,QAAQ,YAAY,SAAS,cAAc,MAAM,CAAC;EAClE,QAAQ,YAAY;EACpB,QAAQ,cAAc;CACxB;CAEA,OAAO;AACT;AAMA,IAAM,OAAO,SAA6B;CACxC,MAAM,UAAU,SAAS,gBAAgB,8BAA8B,KAAK;CAC5E,QAAQ,aAAa,WAAW,WAAW;CAC3C,QAAQ,aAAa,QAAQ,MAAM;CACnC,QAAQ,aAAa,UAAU,cAAc;CAC7C,QAAQ,aAAa,gBAAgB,KAAK;CAC1C,QAAQ,aAAa,kBAAkB,OAAO;CAC9C,QAAQ,aAAa,mBAAmB,OAAO;CAC/C,MAAM,QAAQ,SAAS,gBAAgB,8BAA8B,MAAM;CAC3E,MAAM,aAAa,KAAK,IAAI;CAC5B,QAAQ,YAAY,KAAK;CACzB,OAAO;AACT;AAEA,IAAM,cAA0B,IAAI,cAAc;AAElD,IAAM,cAA0B,IAAI,qBAAqB;;;;ACjgBzD,IAAM,WAAmC;CACvC,aAAa;CACb,aAAa;CACb,aAAa;CACb,gBAAgB;CAChB,gBAAgB;AAClB;;;;;;;;;;AAWA,IAAM,gBAAgB,MAAc,SAAyB;CAC3D,MAAM,OAAO,KAAK,MAAM,IAAI,CAAC,CAAC;CAC9B,IAAI,CAAC,KAAK,WAAW,YAAY,GAC/B,OAAO,KAAK,KAAK;CAGnB,OAAO,KACJ,QAAQ,UAAU,EAAE,CAAA,CACpB,QAAQ,mBAAmB,EAAE,CAAA,CAC7B,KAAK;AACV;;;;;AA2BA,IAAa,sBAAsB,UAA2C;CAC5E,MAAM,UAA8B,CAAC;CACrC,WAAW,KAAK,CAAC,CAAC,QAAQ,EACxB,QAAQ,SAAS;EACf,MAAM,QAAQ,SAAS,KAAK;EAC5B,IAAI,OAAO;GACT,MAAM,QAAQ,aAAa,KAAK,MAAM,MAAM,SAAS,KAAK,MAAM,KAAK,EAAE,CAAC;GACxE,QAAQ,KAAK;IAAE,MAAM,KAAK;IAAM;IAAO;IAAO,OAAO,CAAC;IAAG,OAAO;IAAG,SAAS;GAAE,CAAC;GAC/E,OAAO;EACT;EAEA,IAAI,KAAK,SAAS,cAAc;GAC9B,MAAM,OAAO,eAAe,OAAO,IAAI;GACvC,IAAI,MAAM;IAER,MAAM,QAAQ,QAAQ,GAAG,EAAE,KAAK;KAAE,MAAM;KAAG,OAAO;KAAG,OAAO;KAAI,OAAO,CAAC;KAAG,OAAO;KAAG,SAAS;IAAE;IAChG,IAAI,QAAQ,WAAW,GACrB,QAAQ,KAAK,KAAK;IAEpB,MAAM,OAAO,KAAK,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI;IACvC,IAAI,MAAM;KAGR,MAAM,OAAO,MAAM,MAAM,MAAM,cAAc,UAAU,SAAS,IAAI;KACpE,IAAI,MAAM;MACR,KAAK,SAAS,KAAK;MACnB,KAAK,WAAW,KAAK;KACvB,OACE,MAAM,MAAM,KAAK;MAAE,MAAM,KAAK;MAAM;MAAM,OAAO,KAAK;MAAO,SAAS,KAAK;KAAQ,CAAC;IAExF;IACA,MAAM,SAAS,KAAK;IACpB,MAAM,WAAW,KAAK;GACxB;GACA,OAAO;EACT;CAGF,EACF,CAAC;CAED,OAAO;AACT;;;;;;;;;AAiBA,IAAa,sBAAsB,UAAqC,CAAC,MAAiB,CACxF,WAAW,UACT,MAAM;CACJ;CACA;CACA,kBAA2B,KAAK,cAAc;CAE9C,WAA+B,CAAC;;CAEhC,QAAkD,CAAC;;CAEnD;CACA;CAEA,YAAY,MAAkB;EAC5B,KAAK,QAAQ;EACb,KAAK,QAAQ,SAAS,cAAc,KAAK;EAEzC,KAAK,MAAM,YAAY;EACvB,KAAK,MAAM,QAAQ,UAAU,QAAQ,WAAW;EAChD,KAAK,IAAI,YAAY,KAAK,KAAK;EAC/B,KAAK,UAAU,iBAAiB,UAAU,KAAK,WAAW,EAAE,SAAS,KAAK,CAAC;EAC3E,KAAK,QAAQ;CACf;CAEA,OAAO,QAA0B;EAI/B,IAAI,OAAO,cAAc,WAAW,OAAO,KAAK,MAAM,KAAK,OACzD,KAAK,QAAQ;OACR,IAAI,OAAO,mBAAmB,OAAO,iBAC1C,KAAK,cAAc;CAEvB;CAEA,UAAgB;EACd,KAAK,MAAM,UAAU,oBAAoB,UAAU,KAAK,SAAS;EACjE,IAAI,KAAK,WAAW,KAAA,GAClB,qBAAqB,KAAK,MAAM;EAElC,KAAK,MAAM,OAAO;CACpB;CAEA,UAAgB;EACd,KAAK,QAAQ,WAAW,KAAK,MAAM,KAAK;EACxC,MAAM,UAAU,mBAAmB,KAAK,MAAM,KAAK;EAGnD,IAAI,YAAY,SAAS,KAAK,QAAQ,KAAK,KAAK,MAAM,SAAS,GAC7D;EAGF,KAAK,WAAW;EAChB,KAAK,MAAM,cAAc;EAEzB,KAAK,MAAM,gBAAgB,cAAc,QAAQ,WAAW,CAAC;EAC7D,KAAK,QAAQ,CAAC;EACd,MAAM,aAAa,QAAQ,WAAW,YAAY;EAClD,KAAK,MAAM,SAAS,SAAS;GAG3B,MAAM,YAAY,aAAa,MAAM,MAAM,WAAW;GACtD,KAAK,MAAM,KAAK;IACd,MAAM,MAAM;IACZ,SAAS,KAAK,QAAQ;KACpB,WAAW;KACX,OAAO,MAAM;KACb,MAAM,MAAM;KACZ,MAAM,MAAM;KACZ,OAAO,YAAY,MAAM,QAAQ;KACjC,SAAS,YAAY,MAAM,UAAU;KACrC,OAAO,WAAW,OAAO,SAAS;IACpC,CAAC;GACH,CAAC;GAED,KAAK,MAAM,QAAQ,MAAM,OACvB,KAAK,MAAM,KAAK;IACd,MAAM,KAAK;IACX,SAAS,KAAK,QAAQ;KACpB,WAAW;KACX,OAAO,MAAM;KACb,MAAM,KAAK;KACX,MAAM,KAAK;KACX,OAAO,KAAK;KACZ,SAAS,KAAK;KACd,OAAO,UAAU,IAAI;IACvB,CAAC;GACH,CAAC;EAEL;EAEA,KAAK,cAAc;CACrB;;CAGA,QAAQ,MAQQ;EACd,MAAM,MAAM,KAAK,MAAM,YAAY,SAAS,cAAc,QAAQ,CAAC;EACnE,IAAI,OAAO;EACX,IAAI,YAAY,KAAK;EACrB,IAAI,QAAQ,QAAQ,OAAO,KAAK,KAAK;EACrC,IAAI,iBAAiB,eAAe;GAClC,KAAK,MAAM,SAAS,EAAE,SAAS,aAAW,eAAe,KAAK,MAAM;IAAE,GAAG;IAAS,SAAS;GAAG,CAAC,EAAE,CAAC;EACpG,CAAC;EAED,MAAM,OAAO,IAAI,YAAY,SAAS,cAAc,MAAM,CAAC;EAC3D,KAAK,YAAY;EACjB,KAAK,cAAc,KAAK;EAExB,MAAM,QAAQ,IAAI,YAAY,SAAS,cAAc,MAAM,CAAC;EAC5D,MAAM,YAAY;EAClB,IAAI,KAAK,QAAQ,GAAG;GAClB,MAAM,QAAQ,MAAM,YAAY,SAAS,cAAc,MAAM,CAAC;GAC9D,MAAM,YAAY;GAClB,MAAM,cAAc,IAAI,KAAK;EAC/B;EACA,IAAI,KAAK,UAAU,GAAG;GACpB,MAAM,UAAU,MAAM,YAAY,SAAS,cAAc,MAAM,CAAC;GAChE,QAAQ,YAAY;GACpB,QAAQ,cAAc,IAAI,KAAK;EACjC;EAIA,IAAI,aAAa,cAAc,KAAK,KAAK;EAEzC,OAAO;CACT;;;;;CAMA,gBAAsB;EACpB,IAAI,KAAK,WAAW,KAAA,GAClB;EAEF,KAAK,SAAS,4BAA4B;GACxC,KAAK,SAAS,KAAA;GACd,KAAK,aAAa;EACpB,CAAC;CACH;;CAGA,eAAqB;EACnB,MAAM,EAAE,WAAW,iBAAiB,KAAK,MAAM;EAC/C,MAAM,OAAO,YAAY,gBAAgB,QAAQ,aAAa;EAC9D,IAAI,UAAU;EACd,KAAK,IAAI,QAAQ,GAAG,QAAQ,KAAK,MAAM,QAAQ,SAI7C,IADc,KAAK,MAAM,YAAY,KAAK,MAAM,MAAM,CAAC,IACnD,CAAA,CAAM,OAAO,MACf,UAAU;EAId,KAAK,MAAM,SAAS,EAAE,WAAW,UAAU,QAAQ,gBAAgB,gBAAgB,UAAU,OAAO,CAAC;CACvG;AACF,CACF,GACA,uBACF;;AAGA,IAAM,cAAc,OAAyB,cAA+B;CAC1E,MAAM,QAAQ,CAAC,MAAM,SAAS,kBAAkB;CAChD,IAAI,MAAM,MAAM,SAAS,GACvB,MAAM,KAAK,MAAM,MAAM,KAAK,SAAS,KAAK,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;CAE5D,MAAM,YAAY,YAAY,OAAO,KAAK,IAAI;CAC9C,IAAI,UAAU,SAAS,GACrB,MAAM,KAAK,SAAS;CAEtB,OAAO,MAAM,KAAK,KAAK;AACzB;;AAGA,IAAM,UAAU,EAAE,OAAO,cACvB,CAAC,QAAQ,IAAI,IAAI,UAAU,KAAA,GAAW,UAAU,IAAI,IAAI,YAAY,KAAA,CAAS,CAAA,CAC1E,QAAQ,UAAU,UAAU,KAAA,CAAS,CAAA,CACrC,KAAK,GAAG;;AAGb,IAAM,aAAa,SAAkC,CAAC,KAAK,MAAM,OAAO,IAAI,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;;AAGzG,IAAM,eAAe,MAA0B,UAC7C,KAAK,WAAW,MAAM,UACtB,KAAK,OAAO,OAAO,UAAU;CAC3B,MAAM,QAAQ,MAAM;CACpB,OACE,MAAM,SAAS,MAAM,QACrB,MAAM,UAAU,MAAM,SACtB,MAAM,UAAU,MAAM,SACtB,MAAM,UAAU,MAAM,SACtB,MAAM,YAAY,MAAM,WACxB,MAAM,MAAM,WAAW,MAAM,MAAM,UACnC,MAAM,MAAM,OAAO,MAAM,cAAc;EACrC,MAAM,YAAY,MAAM,MAAM;EAC9B,OACE,KAAK,SAAS,UAAU,QACxB,KAAK,SAAS,UAAU,QACxB,KAAK,UAAU,UAAU,SACzB,KAAK,YAAY,UAAU;CAE/B,CAAC;AAEL,CAAC;AAEH,IAAM,0BAA0B,aAAW,MAAM;CAE/C,KAAK;EAAE,0BAA0B;EAAS,YAAY;CAAW;CACjE,0DAAwD,EAAE,0BAA0B,OAAO;CAC3F,2BAA2B;EACzB,UAAU;EACV,YAAY;EACZ,gBAAgB;EAChB,OAAO;EACP,SAAS;EACT,eAAe;EACf,KAAK;EACL,SAAS;EACT,WAAW;EACX,mBAAmB;EACnB,YAAY;CACd;CAEA,gBAAgB,EAAE,kBAAkB,8BAA8B;CAElE,+CAA+C;EAC7C,SAAS;EACT,qBAAqB;EACrB,YAAY;EACZ,KAAK;EACL,SAAS;EACT,QAAQ;EACR,cAAc;EACd,YAAY;EACZ,WAAW;EACX,QAAQ;CACV;CACA,2DAA2D,EAAE,YAAY,6BAA6B;CACtG,2EAA2E,EACzE,YAAY,+BACd;CACA,yBAAyB,EAAE,kBAAkB,UAAU;CACvD,qCAAqC,EAAE,kBAAkB,IAAI;CAC7D,gEAA8D,EAAE,oBAAoB,UAAU;CAE9F,8CAA8C;EAC5C,OAAO;EACP,UAAU;CACZ;CAEA,wBAAwB,EAAE,oBAAoB,UAAU;CACxD,0CAAwC,EAAE,oBAAoB,OAAO;CACrE,6CAA6C;EAC3C,OAAO;EACP,UAAU;CACZ;CACA,wBAAwB;EACtB,UAAU;EACV,cAAc;EACd,YAAY;CACd;CACA,yBAAyB;EACvB,SAAS;EACT,KAAK;EACL,UAAU;EACV,oBAAoB;CACtB;CAEA,8CAA8C,EAAE,0BAA0B,MAAM;CAChF,uCAAuC,EAAE,SAAS,OAAO;CAEzD,yBAAyB,EAAE,OAAO,kCAAkC;CACpE,2BAA2B,EAAE,OAAO,qCAAqC;CAIzE,wEAAsE,EAAE,SAAS,OAAO;CACxF,wEAAsE,EAAE,SAAS,OAAO;CACxF,yEAAuE;EACrE,qBAAqB;EACrB,cAAc;EACd,WAAW;CACb;AACF,CAAC"}
1
+ {"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 // `\` 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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"}