@dxos/ui-editor 0.8.4-main.2244d791bb → 0.8.4-main.3fbcb4aa9b

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 (170) hide show
  1. package/dist/lib/browser/index.mjs +1847 -1031
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/types/index.mjs +26 -6
  5. package/dist/lib/browser/types/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/index.mjs +1848 -1031
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/types/index.mjs +27 -6
  10. package/dist/lib/node-esm/types/index.mjs.map +4 -4
  11. package/dist/types/src/defaults.d.ts +4 -10
  12. package/dist/types/src/defaults.d.ts.map +1 -1
  13. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  14. package/dist/types/src/extensions/auto-scroll.d.ts +13 -14
  15. package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -1
  16. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -1
  17. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -1
  18. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +5 -2
  19. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -1
  20. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -1
  21. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  23. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  25. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  27. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  28. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  29. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  30. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  31. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  32. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  33. package/dist/types/src/extensions/factories.d.ts +4 -3
  34. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  35. package/dist/types/src/extensions/factories.test.d.ts +2 -0
  36. package/dist/types/src/extensions/factories.test.d.ts.map +1 -0
  37. package/dist/types/src/extensions/focus.d.ts +1 -1
  38. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  39. package/dist/types/src/extensions/index.d.ts +3 -2
  40. package/dist/types/src/extensions/index.d.ts.map +1 -1
  41. package/dist/types/src/extensions/json.d.ts.map +1 -1
  42. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  43. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -0
  45. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  47. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  49. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  50. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  51. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  52. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  53. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  54. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  55. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  56. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -1
  57. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  58. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  59. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  60. package/dist/types/src/extensions/preview/preview.d.ts +2 -0
  61. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  62. package/dist/types/src/extensions/replacer.d.ts.map +1 -1
  63. package/dist/types/src/extensions/scroll-past-end.d.ts +3 -0
  64. package/dist/types/src/extensions/scroll-past-end.d.ts.map +1 -0
  65. package/dist/types/src/extensions/scroller.d.ts +68 -0
  66. package/dist/types/src/extensions/scroller.d.ts.map +1 -0
  67. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  68. package/dist/types/src/extensions/snippets.d.ts +10 -0
  69. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  70. package/dist/types/src/extensions/submit.d.ts.map +1 -1
  71. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
  72. package/dist/types/src/extensions/tags/fader.d.ts +12 -0
  73. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
  74. package/dist/types/src/extensions/tags/index.d.ts +4 -1
  75. package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
  76. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  77. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  78. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  79. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  80. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  81. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  82. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  83. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  84. package/dist/types/src/extensions/tags/xml-tags.d.ts +28 -8
  85. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  86. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -1
  87. package/dist/types/src/index.d.ts +0 -1
  88. package/dist/types/src/index.d.ts.map +1 -1
  89. package/dist/types/src/styles/index.d.ts +0 -2
  90. package/dist/types/src/styles/index.d.ts.map +1 -1
  91. package/dist/types/src/styles/theme.d.ts +15 -0
  92. package/dist/types/src/styles/theme.d.ts.map +1 -1
  93. package/dist/types/src/types/types.d.ts +4 -4
  94. package/dist/types/src/types/types.d.ts.map +1 -1
  95. package/dist/types/src/util/cursor.d.ts +1 -1
  96. package/dist/types/src/util/cursor.d.ts.map +1 -1
  97. package/dist/types/src/util/debug.d.ts.map +1 -1
  98. package/dist/types/src/util/decorations.d.ts.map +1 -1
  99. package/dist/types/src/util/dom.d.ts.map +1 -1
  100. package/dist/types/src/util/facet.d.ts.map +1 -1
  101. package/dist/types/src/util/util.d.ts.map +1 -1
  102. package/dist/types/tsconfig.tsbuildinfo +1 -1
  103. package/package.json +36 -39
  104. package/src/defaults.ts +33 -20
  105. package/src/extensions/annotations.ts +1 -1
  106. package/src/extensions/auto-scroll.ts +149 -120
  107. package/src/extensions/autocomplete/placeholder.ts +37 -18
  108. package/src/extensions/automerge/automerge.test.tsx +37 -11
  109. package/src/extensions/automerge/automerge.ts +5 -7
  110. package/src/extensions/blocks.ts +5 -5
  111. package/src/extensions/comments.ts +5 -6
  112. package/src/extensions/dnd.ts +2 -2
  113. package/src/extensions/factories.test.ts +88 -0
  114. package/src/extensions/factories.ts +32 -15
  115. package/src/extensions/folding.ts +5 -22
  116. package/src/extensions/index.ts +3 -2
  117. package/src/extensions/markdown/action.ts +0 -1
  118. package/src/extensions/markdown/bundle.ts +23 -9
  119. package/src/extensions/markdown/decorate.ts +15 -12
  120. package/src/extensions/markdown/highlight.ts +15 -7
  121. package/src/extensions/markdown/link.ts +27 -33
  122. package/src/extensions/markdown/parser.test.ts +0 -1
  123. package/src/extensions/markdown/styles.ts +42 -9
  124. package/src/extensions/markdown/table.ts +24 -2
  125. package/src/extensions/outliner/outliner.test.ts +0 -1
  126. package/src/extensions/outliner/outliner.ts +3 -4
  127. package/src/extensions/outliner/tree.test.ts +0 -1
  128. package/src/extensions/preview/preview.ts +55 -8
  129. package/src/extensions/scroll-past-end.ts +32 -0
  130. package/src/extensions/scroller.ts +256 -0
  131. package/src/extensions/selection.ts +1 -1
  132. package/src/extensions/snippets.ts +67 -0
  133. package/src/extensions/tags/extended-markdown.test.ts +120 -2
  134. package/src/extensions/tags/extended-markdown.ts +80 -1
  135. package/src/extensions/tags/fader.ts +195 -0
  136. package/src/extensions/tags/index.ts +4 -1
  137. package/src/extensions/tags/testing/text.md +36 -0
  138. package/src/extensions/tags/testing/text.txt +35 -0
  139. package/src/extensions/tags/typewriter.test.ts +65 -0
  140. package/src/extensions/tags/typewriter.ts +594 -0
  141. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  142. package/src/extensions/tags/xml-formatting.ts +125 -0
  143. package/src/extensions/tags/xml-tags.ts +186 -35
  144. package/src/extensions/tags/xml-util.test.ts +199 -24
  145. package/src/extensions/tags/xml-util.ts +62 -5
  146. package/src/index.ts +0 -1
  147. package/src/styles/index.ts +0 -2
  148. package/src/styles/theme.ts +115 -37
  149. package/src/types/types.ts +10 -2
  150. package/src/typings.d.ts +8 -0
  151. package/src/util/cursor.ts +1 -2
  152. package/dist/lib/browser/chunk-HL3YF6WC.mjs +0 -22
  153. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +0 -7
  154. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs +0 -24
  155. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +0 -7
  156. package/dist/types/src/extensions/smooth-scroll.d.ts +0 -78
  157. package/dist/types/src/extensions/smooth-scroll.d.ts.map +0 -1
  158. package/dist/types/src/extensions/tags/streamer.d.ts +0 -12
  159. package/dist/types/src/extensions/tags/streamer.d.ts.map +0 -1
  160. package/dist/types/src/extensions/typewriter.d.ts +0 -10
  161. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  162. package/dist/types/src/styles/markdown.d.ts +0 -8
  163. package/dist/types/src/styles/markdown.d.ts.map +0 -1
  164. package/dist/types/src/styles/tokens.d.ts +0 -3
  165. package/dist/types/src/styles/tokens.d.ts.map +0 -1
  166. package/src/extensions/smooth-scroll.ts +0 -195
  167. package/src/extensions/tags/streamer.ts +0 -243
  168. package/src/extensions/typewriter.ts +0 -68
  169. package/src/styles/markdown.ts +0 -26
  170. package/src/styles/tokens.ts +0 -17
@@ -1,29 +1,42 @@
1
- import {
2
- EditorInputMode,
3
- EditorInputModes,
4
- EditorViewMode,
5
- EditorViewModes
6
- } from "./chunk-HL3YF6WC.mjs";
7
-
8
1
  // src/index.ts
9
- import { EditorState as EditorState3 } from "@codemirror/state";
10
- import { EditorView as EditorView30, keymap as keymap15 } from "@codemirror/view";
2
+ import { EditorState as EditorState4 } from "@codemirror/state";
3
+ import { EditorView as EditorView33, keymap as keymap15 } from "@codemirror/view";
11
4
  import { tags as tags2 } from "@lezer/highlight";
12
5
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
13
6
 
14
7
  // src/defaults.ts
15
8
  import { mx } from "@dxos/ui-theme";
16
- var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
17
- var editorSlots = {
18
- scroll: {
19
- className: "pbs-2"
20
- },
9
+ var editorClassNames = (role) => mx("dx-attention-surface p-0.5 data-[toolbar=disabled]:pt-2 dx-focus-ring-inset", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-h-24" : "dx-container overflow-hidden");
10
+ var documentSlots = {
11
+ content: {
12
+ /**
13
+ * CodeMirror content width.
14
+ * 40rem = 640px. Corresponds to initial plank width (Google docs, Stashpad, etc.)
15
+ * 50rem = 800px. Maximum content width for solo mode.
16
+ * NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).
17
+ */
18
+ className: mx(
19
+ // Inline-size container for widget sizing (children use `max-w-[100cqi]`).
20
+ // NOTE: Use inline-size, not full size containment — `container-type: size` on the
21
+ // editor content breaks CodeMirror's viewport measurement, leaving blank gaps during
22
+ // scroll until a click forces a re-measure.
23
+ "dx-inline-size-container",
24
+ // Wider margin for web (vs. mobile).
25
+ "pointer-fine:max-w-[min(50rem,100%-4rem)] pointer-coarse:max-w-[min(50rem,100%-2rem)]",
26
+ "mx-auto! w-full"
27
+ )
28
+ }
29
+ };
30
+ var compactSlots = {
31
+ content: {
32
+ className: "mx-2!"
33
+ }
34
+ };
35
+ var mobileSlots = {
21
36
  content: {
22
- className: editorWidth
37
+ className: "mx-2!"
23
38
  }
24
39
  };
25
- var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
26
- var stackItemContentEditorClassNames = (role) => mx("p-0.5 dx-focus-ring-inset attention-surface data-[toolbar=disabled]:pbs-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24" : "min-bs-0");
27
40
 
28
41
  // src/extensions/annotations.ts
29
42
  import { RangeSetBuilder } from "@codemirror/state";
@@ -58,7 +71,7 @@ var annotations = ({ match } = {}) => {
58
71
  ".cm-annotation": {
59
72
  textDecoration: "underline",
60
73
  textDecorationStyle: "wavy",
61
- textDecorationColor: "var(--dx-errorText)"
74
+ textDecorationColor: "var(--color-error-text)"
62
75
  }
63
76
  })
64
77
  ];
@@ -207,7 +220,7 @@ var singleValueFacet = (defaultValue) => Facet.define({
207
220
  var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
208
221
  var defaultCursorConverter = {
209
222
  toCursor: (position) => position.toString(),
210
- fromCursor: (cursor2) => parseInt(cursor2)
223
+ fromCursor: (cursor) => parseInt(cursor)
211
224
  };
212
225
  var Cursor = class _Cursor {
213
226
  static converter = singleValueFacet(defaultCursorConverter);
@@ -220,9 +233,9 @@ var Cursor = class _Cursor {
220
233
  to
221
234
  ].join(":");
222
235
  };
223
- static getRangeFromCursor = (state, cursor2) => {
236
+ static getRangeFromCursor = (state, cursor) => {
224
237
  const cursorConverter2 = state.facet(_Cursor.converter);
225
- const parts = cursor2.split(":");
238
+ const parts = cursor.split(":");
226
239
  const from = cursorConverter2.fromCursor(parts[0]);
227
240
  const to = cursorConverter2.fromCursor(parts[1]);
228
241
  return from !== void 0 && to !== void 0 ? {
@@ -258,12 +271,7 @@ var wrapWithCatch = (fn, label) => {
258
271
  } catch (err) {
259
272
  log.catch(err, {
260
273
  label
261
- }, {
262
- F: __dxlog_file,
263
- L: 20,
264
- S: void 0,
265
- C: (f, a) => f(...a)
266
- });
274
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 13, S: void 0 });
267
275
  }
268
276
  };
269
277
  };
@@ -289,12 +297,7 @@ var logChanges = (trs) => {
289
297
  if (changes.length) {
290
298
  log("changes", {
291
299
  changes
292
- }, {
293
- F: __dxlog_file,
294
- L: 54,
295
- S: void 0,
296
- C: (f, a) => f(...a)
297
- });
300
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 44, S: void 0 });
298
301
  }
299
302
  };
300
303
 
@@ -360,30 +363,37 @@ var insertAtLineStart = (view, from, insert) => {
360
363
  };
361
364
 
362
365
  // src/extensions/autocomplete/placeholder.ts
363
- var placeholder = ({ content, delay = 3e3 }) => {
366
+ var placeholder = ({ content, delay = 3e3, focusOnly = false }) => {
364
367
  const plugin = ViewPlugin3.fromClass(class {
365
368
  _timeout;
366
369
  _decorations = Decoration3.none;
367
370
  update(update2) {
371
+ if (!update2.docChanged && !update2.selectionSet && !update2.focusChanged) {
372
+ return;
373
+ }
368
374
  if (this._timeout) {
369
375
  window.clearTimeout(this._timeout);
370
376
  this._timeout = void 0;
371
377
  }
378
+ this._decorations = Decoration3.none;
379
+ if (focusOnly && !update2.view.hasFocus) {
380
+ return;
381
+ }
372
382
  const activeLine = update2.view.state.doc.lineAt(update2.view.state.selection.main.head);
373
- const isEmpty = activeLine.text.trim() === "";
374
- if (isEmpty) {
375
- const lineStart = activeLine.from;
376
- this._timeout = setTimeout(() => {
377
- this._decorations = Decoration3.set([
378
- Decoration3.widget({
379
- widget: new PlaceholderWidget(content),
380
- side: 1
381
- }).range(lineStart)
382
- ]);
383
- update2.view.update([]);
384
- }, delay);
383
+ if (activeLine.text.trim() !== "") {
384
+ return;
385
385
  }
386
- this._decorations = Decoration3.none;
386
+ const lineStart = activeLine.from;
387
+ const view = update2.view;
388
+ this._timeout = setTimeout(() => {
389
+ this._decorations = Decoration3.set([
390
+ Decoration3.widget({
391
+ widget: new PlaceholderWidget(content),
392
+ side: 1
393
+ }).range(lineStart)
394
+ ]);
395
+ view.update([]);
396
+ }, delay);
387
397
  }
388
398
  destroy() {
389
399
  if (this._timeout) {
@@ -502,156 +512,234 @@ var typeahead = ({ onComplete } = {}) => {
502
512
  };
503
513
 
504
514
  // src/extensions/auto-scroll.ts
515
+ import { StateEffect as StateEffect2 } from "@codemirror/state";
505
516
  import { EditorView as EditorView5, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
506
- import { addEventListener, combine } from "@dxos/async";
517
+ import { addEventListener, combine, throttle } from "@dxos/async";
507
518
  import { Domino } from "@dxos/ui";
519
+ import { getSize } from "@dxos/ui-theme";
508
520
 
509
- // src/extensions/smooth-scroll.ts
521
+ // src/extensions/scroller.ts
510
522
  import { StateEffect } from "@codemirror/state";
511
523
  import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
512
- var scrollToLineEffect = StateEffect.define();
513
- var smoothScroll = ({ offset = 0, position = "start" } = {}) => {
514
- const scrollPlugin = ViewPlugin5.fromClass(class SmoothScrollPlugin {
524
+ import { log as log2 } from "@dxos/log";
525
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scroller.ts";
526
+ var scrollerLineEffect = StateEffect.define();
527
+ var scrollerCrawlEffect = StateEffect.define();
528
+ var scrollToLine = (view, options) => {
529
+ view.dispatch({
530
+ effects: scrollerLineEffect.of(options)
531
+ });
532
+ };
533
+ var scroller = ({ overScroll = 0 } = {}) => {
534
+ const scrollPlugin = ViewPlugin5.fromClass(class ScrollerPlugin {
515
535
  view;
536
+ crawler;
516
537
  constructor(view) {
517
538
  this.view = view;
539
+ this.crawler = createCrawler(this.view);
518
540
  }
519
541
  // No-op.
520
542
  destroy() {
543
+ this.crawler.cancel();
521
544
  }
522
- /**
523
- * Perform smooth scroll to the specified line.
524
- */
525
- scrollToLine(lineNumber, options) {
526
- const { offset: animOffset = 0, position: animPosition, behavior } = options;
527
- const scroller = this.view.scrollDOM;
528
- const doc = this.view.state.doc;
529
- const targetLine = Math.max(0, lineNumber - 1);
530
- if (behavior === "instant") {
531
- requestAnimationFrame(() => {
532
- this.view.dispatch({
533
- selection: {
534
- anchor: doc.line(targetLine + 1).from
535
- },
536
- scrollIntoView: true
537
- });
538
- });
539
- return;
540
- }
541
- if (lineNumber === -1) {
542
- this.animateScroll(scroller, scroller.scrollHeight - scroller.clientHeight);
543
- return;
544
- }
545
- if (targetLine >= doc.lines) {
546
- const targetScrollTop2 = scroller.scrollHeight - scroller.clientHeight + (animOffset || 0);
547
- this.animateScroll(scroller, targetScrollTop2);
548
- return;
549
- }
550
- const lineStart = doc.line(targetLine + 1).from;
551
- const coords = this.view.coordsAtPos(lineStart);
552
- if (!coords) {
553
- return;
554
- }
555
- const currentScrollTop = scroller.scrollTop;
556
- const scrollerRect = scroller.getBoundingClientRect();
557
- const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
558
- let targetScrollTop;
559
- if (animPosition === "end") {
560
- targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + animOffset;
545
+ cancel() {
546
+ this.crawler.cancel();
547
+ }
548
+ crawl(start = false) {
549
+ if (start) {
550
+ this.crawler.scroll();
561
551
  } else {
562
- targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + animOffset;
552
+ this.crawler.cancel();
563
553
  }
564
- const clampedScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
565
- this.animateScroll(scroller, clampedScrollTop);
566
554
  }
567
- /**
568
- * Animate scroll using browser's built-in smooth scrolling.
569
- */
570
- animateScroll(element, targetScrollTop) {
571
- if (Math.abs(targetScrollTop - element.scrollTop) < 1) {
572
- return;
555
+ scroll({ line, offset = 0, position, behavior = "instant" }) {
556
+ const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
557
+ const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
558
+ const doc = this.view.state.doc;
559
+ let targetScrollTop = scrollHeight - clientHeight + offset;
560
+ if (line >= 0 && line <= doc.lines - 1) {
561
+ const lineStart = doc.line(line + 1).from;
562
+ const coords = this.view.coordsAtPos(lineStart);
563
+ if (coords) {
564
+ const currentScrollTop = scrollTop;
565
+ const maxScrollTop = scrollHeight - clientHeight;
566
+ if (position === "end") {
567
+ targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
568
+ } else {
569
+ targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
570
+ }
571
+ targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
572
+ }
573
573
  }
574
- setTimeout(() => {
575
- element.scrollTo({
576
- top: targetScrollTop,
577
- behavior: "smooth"
574
+ requestAnimationFrame(() => {
575
+ this.view.scrollDOM.scrollTo({
576
+ top: targetScrollTop
578
577
  });
579
- }, 100);
578
+ });
580
579
  }
581
580
  });
582
581
  return [
583
582
  scrollPlugin,
584
- // Update listener to handle scroll effects.
583
+ // Listen for effect.
585
584
  EditorView4.updateListener.of((update2) => {
586
585
  update2.transactions.forEach((transaction) => {
587
- for (const effect of transaction.effects) {
588
- if (effect.is(scrollToLineEffect)) {
589
- const { line, options = {} } = effect.value;
590
- const plugin = update2.view.plugin(scrollPlugin);
591
- if (plugin) {
592
- plugin.scrollToLine(line, {
593
- offset,
594
- position,
595
- ...options
596
- });
586
+ try {
587
+ const plugin = update2.view.plugin(scrollPlugin);
588
+ if (plugin) {
589
+ for (const effect of transaction.effects) {
590
+ if (effect.is(scrollerCrawlEffect)) {
591
+ plugin.crawl(effect.value);
592
+ } else if (effect.is(scrollerLineEffect)) {
593
+ plugin.scroll(effect.value);
594
+ }
597
595
  }
598
596
  }
597
+ } catch (err) {
598
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 91, S: void 0 });
599
599
  }
600
600
  });
601
+ }),
602
+ // Styles.
603
+ EditorView4.theme({
604
+ ".cm-scroller": {
605
+ overflowY: "scroll",
606
+ // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks
607
+ // expanding their TogglePanel), the browser picks a stable element near the viewport
608
+ // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning
609
+ // logic still has the final word when pinned (forces scrollTop to scrollHeight).
610
+ overflowAnchor: "auto"
611
+ },
612
+ ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
613
+ display: "none"
614
+ },
615
+ ".cm-scroller::-webkit-scrollbar-thumb": {
616
+ background: "transparent",
617
+ transition: "background 0.15s"
618
+ },
619
+ "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
620
+ background: "var(--color-scrollbar-thumb)"
621
+ },
622
+ // Spacer below the last text line. Implemented as a real block pseudo-element
623
+ // (rather than `padding-bottom` on `.cm-content`) so it materializes in the
624
+ // scroller's `scrollHeight` regardless of how `padding` is reset by the base
625
+ // theme or downstream classes — this is what gives auto-scroll its head-room
626
+ // so the last line stays `overScroll` px above the viewport bottom.
627
+ ".cm-content::after": {
628
+ content: '""',
629
+ display: "block",
630
+ height: `${overScroll}px`
631
+ },
632
+ ".cm-scroll-button": {
633
+ position: "absolute",
634
+ bottom: "0.5rem",
635
+ right: "1rem"
636
+ }
601
637
  })
602
638
  ];
603
639
  };
604
- var scrollToLine = (view, line, options) => {
605
- view.dispatch({
606
- effects: scrollToLineEffect.of({
607
- line,
608
- options
609
- })
610
- });
611
- };
640
+ function createCrawler(view, omega = 5, snapThreshold = 5, snapVelocity = 50) {
641
+ const el = view.scrollDOM;
642
+ let currentTop = 0;
643
+ let velocity = 0;
644
+ let rafId = null;
645
+ let lastTime = 0;
646
+ function frame(now) {
647
+ const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1e3);
648
+ lastTime = now;
649
+ const targetTop = el.scrollHeight - el.clientHeight;
650
+ const delta = targetTop - currentTop;
651
+ if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {
652
+ el.scrollTop = targetTop;
653
+ currentTop = targetTop;
654
+ velocity = 0;
655
+ rafId = null;
656
+ lastTime = 0;
657
+ return;
658
+ }
659
+ const accel = omega * omega * delta - 2 * omega * velocity;
660
+ velocity += accel * dt;
661
+ currentTop += velocity * dt;
662
+ el.scrollTop = currentTop;
663
+ rafId = requestAnimationFrame(frame);
664
+ }
665
+ return {
666
+ scroll: () => {
667
+ if (rafId === null) {
668
+ currentTop = el.scrollTop;
669
+ lastTime = 0;
670
+ rafId = requestAnimationFrame(frame);
671
+ }
672
+ },
673
+ cancel: () => {
674
+ if (rafId !== null) {
675
+ cancelAnimationFrame(rafId);
676
+ velocity = 0;
677
+ lastTime = 0;
678
+ rafId = null;
679
+ }
680
+ }
681
+ };
682
+ }
612
683
 
613
684
  // src/extensions/auto-scroll.ts
614
- var autoScroll = ({ pinned = true, threshold = 100, throttleDelay = 500, onAutoScroll } = {}) => {
685
+ var autoScrollEffect = StateEffect2.define();
686
+ var autoScroll = ({ scrollOnResize = true } = {}) => {
615
687
  let buttonContainer;
616
- let isPinned = pinned;
617
- const setPinned = (pinned2) => {
618
- buttonContainer?.classList.toggle("opacity-0", pinned2);
619
- isPinned = pinned2;
620
- };
621
- const scrollToBottom = (view, behavior) => {
622
- setPinned(true);
623
- view.dispatch({
624
- effects: scrollToLineEffect.of({
625
- line: -1,
626
- options: {
627
- position: "end",
628
- behavior
629
- }
630
- })
631
- });
688
+ let isPinned = true;
689
+ let jumpPending = false;
690
+ let enabled = true;
691
+ let firstUpdate = true;
692
+ const setPinned = (pinned) => {
693
+ buttonContainer?.classList.toggle("opacity-0", pinned);
694
+ isPinned = pinned;
632
695
  };
633
- const triggerUpdate = intervalUntilQuiet((view) => {
634
- if (isPinned) {
635
- scrollToBottom(view);
636
- }
637
- }, throttleDelay);
638
696
  return [
639
- // Update listener for logging when scrolling is needed.
640
- EditorView5.updateListener.of(({ view, heightChanged, state }) => {
697
+ // Update listener for scrolling when content changes.
698
+ EditorView5.updateListener.of((update2) => {
699
+ const { view, heightChanged, state, startState } = update2;
700
+ for (const tr of update2.transactions) {
701
+ for (const effect of tr.effects) {
702
+ if (effect.is(autoScrollEffect)) {
703
+ enabled = effect.value;
704
+ if (enabled) {
705
+ setPinned(true);
706
+ view.dispatch({
707
+ effects: scrollerCrawlEffect.of(true)
708
+ });
709
+ } else {
710
+ view.dispatch({
711
+ effects: scrollerCrawlEffect.of(false)
712
+ });
713
+ }
714
+ }
715
+ }
716
+ }
717
+ if (!enabled) {
718
+ return;
719
+ }
720
+ if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
721
+ firstUpdate = false;
722
+ jumpPending = true;
723
+ requestAnimationFrame(() => {
724
+ view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
725
+ jumpPending = false;
726
+ });
727
+ return;
728
+ }
729
+ firstUpdate = false;
730
+ if (jumpPending) {
731
+ return;
732
+ }
641
733
  if (heightChanged) {
642
734
  if (isPinned) {
643
- const coords = view.coordsAtPos(view.state.doc.length);
644
- const scrollerRect = view.scrollDOM.getBoundingClientRect();
645
- const distanceFromBottom = coords ? scrollerRect.bottom - coords.bottom : 0;
646
- if (distanceFromBottom < threshold) {
647
- const shouldScroll = onAutoScroll?.({
648
- view,
649
- distanceFromBottom
650
- }) ?? true;
651
- if (shouldScroll) {
652
- triggerUpdate(view);
653
- }
654
- } else if (distanceFromBottom < 0) {
735
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
736
+ const delta = scrollHeight - scrollTop - clientHeight;
737
+ if (delta > 0) {
738
+ setPinned(true);
739
+ view.dispatch({
740
+ effects: scrollerCrawlEffect.of(true)
741
+ });
742
+ } else if (delta < -1) {
655
743
  setPinned(false);
656
744
  }
657
745
  } else {
@@ -661,17 +749,63 @@ var autoScroll = ({ pinned = true, threshold = 100, throttleDelay = 500, onAutoS
661
749
  }
662
750
  }
663
751
  }),
752
+ // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,
753
+ // window resize). Doc-driven height changes are handled by the updateListener above; this
754
+ // observer covers the case where the viewport changes while the doc length is unchanged.
755
+ scrollOnResize ? ViewPlugin6.fromClass(class {
756
+ observer;
757
+ firstObservation = true;
758
+ destroyed = false;
759
+ constructor(view) {
760
+ const onResize = throttle(() => {
761
+ if (this.destroyed || !enabled) {
762
+ return;
763
+ }
764
+ setPinned(true);
765
+ requestAnimationFrame(() => {
766
+ if (this.destroyed) {
767
+ return;
768
+ }
769
+ view.scrollDOM.scrollTo({
770
+ top: view.scrollDOM.scrollHeight,
771
+ behavior: "instant"
772
+ });
773
+ view.dispatch({
774
+ effects: scrollerCrawlEffect.of(false)
775
+ });
776
+ });
777
+ }, 50);
778
+ this.observer = new ResizeObserver(() => {
779
+ if (this.firstObservation) {
780
+ this.firstObservation = false;
781
+ return;
782
+ }
783
+ onResize();
784
+ });
785
+ this.observer.observe(view.scrollDOM);
786
+ }
787
+ destroy() {
788
+ this.destroyed = true;
789
+ this.observer.disconnect();
790
+ }
791
+ }) : [],
664
792
  // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
665
793
  ViewPlugin6.fromClass(class {
666
794
  cleanup;
667
795
  constructor(view) {
668
- this.cleanup = createUserScrollDetector(view.scrollDOM, () => {
796
+ this.cleanup = createUserScrollDetector(view.scrollDOM, throttle(() => {
669
797
  requestAnimationFrame(() => {
670
798
  const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
671
- const atBottom = scrollHeight - scrollTop - clientHeight < threshold;
672
- setPinned(atBottom);
799
+ const delta = scrollHeight - scrollTop - clientHeight;
800
+ const pinned = delta === 0;
801
+ setPinned(pinned);
802
+ if (!pinned) {
803
+ view.dispatch({
804
+ effects: scrollerCrawlEffect.of(false)
805
+ });
806
+ }
673
807
  });
674
- });
808
+ }, 500));
675
809
  }
676
810
  destroy() {
677
811
  this.cleanup();
@@ -680,41 +814,24 @@ var autoScroll = ({ pinned = true, threshold = 100, throttleDelay = 500, onAutoS
680
814
  // Scroll button.
681
815
  ViewPlugin6.fromClass(class {
682
816
  constructor(view) {
683
- const icon = Domino.of("dx-icon").attributes({
817
+ const icon = Domino.of("dx-icon").classNames(getSize(4)).attributes({
684
818
  icon: "ph--arrow-down--regular"
685
819
  });
686
- const button = Domino.of("button").classNames("dx-button bg-accentSurface").attributes({
820
+ const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
687
821
  "data-density": "fine"
688
- }).children(icon).on("click", () => {
689
- scrollToBottom(view);
822
+ }).append(icon).on("click", () => {
823
+ setPinned(true);
824
+ view.dispatch({
825
+ effects: scrollerLineEffect.of({
826
+ line: -1,
827
+ position: "end",
828
+ behavior: "smooth"
829
+ })
830
+ });
690
831
  });
691
- buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").children(button).root;
832
+ buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").append(button).root;
692
833
  view.scrollDOM.parentElement.appendChild(buttonContainer);
693
834
  }
694
- }),
695
- // Styles.
696
- EditorView5.theme({
697
- ".cm-content": {
698
- paddingBottom: `${threshold}px`
699
- },
700
- ".cm-scroller": {
701
- paddingBottom: "0"
702
- },
703
- ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
704
- display: "none"
705
- },
706
- ".cm-scroller::-webkit-scrollbar-thumb": {
707
- background: "transparent",
708
- transition: "background 0.15s"
709
- },
710
- "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
711
- background: "var(--dx-scrollbarThumb)"
712
- },
713
- ".cm-scroll-button": {
714
- position: "absolute",
715
- bottom: "0.5rem",
716
- right: "1rem"
717
- }
718
835
  })
719
836
  ];
720
837
  };
@@ -727,28 +844,6 @@ function createUserScrollDetector(element, onUserScroll) {
727
844
  }
728
845
  }));
729
846
  }
730
- function intervalUntilQuiet(fn, interval) {
731
- let timer = null;
732
- let quietTimer = null;
733
- let latestArgs;
734
- return (...args) => {
735
- latestArgs = args;
736
- if (quietTimer) {
737
- clearTimeout(quietTimer);
738
- }
739
- quietTimer = setTimeout(() => {
740
- if (timer) {
741
- clearInterval(timer);
742
- timer = null;
743
- }
744
- quietTimer = null;
745
- }, interval);
746
- if (!timer) {
747
- fn(...latestArgs);
748
- timer = setInterval(() => fn(...latestArgs), interval);
749
- }
750
- };
751
- }
752
847
 
753
848
  // src/extensions/automerge/automerge.ts
754
849
  import { next as A3 } from "@automerge/automerge";
@@ -762,42 +857,32 @@ var initialSync = Transaction.userEvent.of("initial.sync");
762
857
 
763
858
  // src/extensions/automerge/cursor.ts
764
859
  import { fromCursor, toCursor } from "@dxos/echo-db";
765
- import { log as log2 } from "@dxos/log";
766
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
860
+ import { log as log3 } from "@dxos/log";
861
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
767
862
  var cursorConverter = (accessor) => ({
768
863
  toCursor: (pos, assoc) => {
769
864
  try {
770
865
  return toCursor(accessor, pos, assoc);
771
866
  } catch (err) {
772
- log2.catch(err, void 0, {
773
- F: __dxlog_file2,
774
- L: 15,
775
- S: void 0,
776
- C: (f, a) => f(...a)
777
- });
867
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 11, S: void 0 });
778
868
  return "";
779
869
  }
780
870
  },
781
- fromCursor: (cursor2) => {
871
+ fromCursor: (cursor) => {
782
872
  try {
783
- return fromCursor(accessor, cursor2);
873
+ return fromCursor(accessor, cursor);
784
874
  } catch (err) {
785
- log2.catch(err, void 0, {
786
- F: __dxlog_file2,
787
- L: 24,
788
- S: void 0,
789
- C: (f, a) => f(...a)
790
- });
875
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 19, S: void 0 });
791
876
  return 0;
792
877
  }
793
878
  }
794
879
  });
795
880
 
796
881
  // src/extensions/automerge/defs.ts
797
- import { Annotation, StateEffect as StateEffect2 } from "@codemirror/state";
882
+ import { Annotation, StateEffect as StateEffect3 } from "@codemirror/state";
798
883
  var getPath = (state, field) => state.field(field).path;
799
884
  var getLastHeads = (state, field) => state.field(field).lastHeads;
800
- var updateHeadsEffect = StateEffect2.define({});
885
+ var updateHeadsEffect = StateEffect3.define({});
801
886
  var updateHeads = (newHeads) => updateHeadsEffect.of({
802
887
  newHeads
803
888
  });
@@ -808,7 +893,7 @@ var isReconcile = (tr) => {
808
893
 
809
894
  // src/extensions/automerge/sync.ts
810
895
  import { next as A2 } from "@automerge/automerge";
811
- import { log as log3 } from "@dxos/log";
896
+ import { log as log4 } from "@dxos/log";
812
897
 
813
898
  // src/extensions/automerge/update-automerge.ts
814
899
  import { next as A } from "@automerge/automerge";
@@ -949,7 +1034,7 @@ var charPath = (textPath, candidatePath) => {
949
1034
  };
950
1035
 
951
1036
  // src/extensions/automerge/sync.ts
952
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/sync.ts";
1037
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/sync.ts";
953
1038
  var Syncer = class {
954
1039
  _handle;
955
1040
  _state;
@@ -972,12 +1057,7 @@ var Syncer = class {
972
1057
  this._pending = false;
973
1058
  }
974
1059
  onEditorChange(view) {
975
- log3("onEditorChange", void 0, {
976
- F: __dxlog_file3,
977
- L: 45,
978
- S: this,
979
- C: (f, a) => f(...a)
980
- });
1060
+ log4("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 35, S: this });
981
1061
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
982
1062
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
983
1063
  if (newHeads) {
@@ -988,12 +1068,7 @@ var Syncer = class {
988
1068
  }
989
1069
  }
990
1070
  onAutomergeChange(view) {
991
- log3("onAutomergeChange", void 0, {
992
- F: __dxlog_file3,
993
- L: 60,
994
- S: this,
995
- C: (f, a) => f(...a)
996
- });
1071
+ log4("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 47, S: this });
997
1072
  const oldHeads = getLastHeads(view.state, this._state);
998
1073
  const newHeads = A2.getHeads(this._handle.doc());
999
1074
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1055,6 +1130,17 @@ var automerge = (accessor) => {
1055
1130
  const value = DocAccessor.getValue(accessor);
1056
1131
  const current = this._view.state.doc.toString();
1057
1132
  if (value !== current) {
1133
+ this._view.dispatch({
1134
+ changes: {
1135
+ from: 0,
1136
+ to: this._view.state.doc.length,
1137
+ insert: value
1138
+ },
1139
+ annotations: [
1140
+ initialSync,
1141
+ reconcileAnnotation.of(true)
1142
+ ]
1143
+ });
1058
1144
  }
1059
1145
  });
1060
1146
  }
@@ -1082,7 +1168,7 @@ import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1082
1168
  import { Decoration as Decoration5, EditorView as EditorView7, ViewPlugin as ViewPlugin8, WidgetType as WidgetType3 } from "@codemirror/view";
1083
1169
  import { Event } from "@dxos/async";
1084
1170
  import { Context } from "@dxos/context";
1085
- var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness.ts";
1171
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness.ts";
1086
1172
  var dummyProvider = {
1087
1173
  remoteStateChange: new Event(),
1088
1174
  open: () => {
@@ -1105,10 +1191,7 @@ var awareness = (provider = dummyProvider) => {
1105
1191
  ];
1106
1192
  };
1107
1193
  var RemoteSelectionsDecorator = class {
1108
- _ctx = new Context(void 0, {
1109
- F: __dxlog_file4,
1110
- L: 80
1111
- });
1194
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 33 });
1112
1195
  _cursorConverter;
1113
1196
  _provider;
1114
1197
  _lastAnchor;
@@ -1319,8 +1402,8 @@ var styles = EditorView7.theme({
1319
1402
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
1320
1403
  import { Context as Context2 } from "@dxos/context";
1321
1404
  import { invariant } from "@dxos/invariant";
1322
- import { log as log4 } from "@dxos/log";
1323
- var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness-provider.ts";
1405
+ import { log as log5 } from "@dxos/log";
1406
+ var __dxlog_file6 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness-provider.ts";
1324
1407
  var DEBOUNCE_INTERVAL = 100;
1325
1408
  var SpaceAwarenessProvider = class {
1326
1409
  _remoteStates = /* @__PURE__ */ new Map();
@@ -1339,10 +1422,7 @@ var SpaceAwarenessProvider = class {
1339
1422
  this._info = info;
1340
1423
  }
1341
1424
  open() {
1342
- this._ctx = new Context2(void 0, {
1343
- F: __dxlog_file5,
1344
- L: 57
1345
- });
1425
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 28 });
1346
1426
  this._postTask = new DeferredTask(this._ctx, async () => {
1347
1427
  if (this._localState) {
1348
1428
  await this._messenger.postMessage(this._channel, {
@@ -1367,14 +1447,9 @@ var SpaceAwarenessProvider = class {
1367
1447
  void this._messenger.postMessage(this._channel, {
1368
1448
  kind: "query"
1369
1449
  }).catch((err) => {
1370
- log4.debug("failed to query awareness", {
1450
+ log5.debug("failed to query awareness", {
1371
1451
  err
1372
- }, {
1373
- F: __dxlog_file5,
1374
- L: 91,
1375
- S: this,
1376
- C: (f, a) => f(...a)
1377
- });
1452
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 57, S: this });
1378
1453
  });
1379
1454
  }
1380
1455
  close() {
@@ -1386,15 +1461,7 @@ var SpaceAwarenessProvider = class {
1386
1461
  return Array.from(this._remoteStates.values());
1387
1462
  }
1388
1463
  update(position) {
1389
- invariant(this._postTask, void 0, {
1390
- F: __dxlog_file5,
1391
- L: 106,
1392
- S: this,
1393
- A: [
1394
- "this._postTask",
1395
- ""
1396
- ]
1397
- });
1464
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 71, S: this, A: ["this._postTask", ""] });
1398
1465
  this._localState = {
1399
1466
  peerId: this._peerId,
1400
1467
  position,
@@ -1403,27 +1470,11 @@ var SpaceAwarenessProvider = class {
1403
1470
  this._postTask.schedule();
1404
1471
  }
1405
1472
  _handleQueryMessage() {
1406
- invariant(this._postTask, void 0, {
1407
- F: __dxlog_file5,
1408
- L: 117,
1409
- S: this,
1410
- A: [
1411
- "this._postTask",
1412
- ""
1413
- ]
1414
- });
1473
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 80, S: this, A: ["this._postTask", ""] });
1415
1474
  this._postTask.schedule();
1416
1475
  }
1417
1476
  _handlePostMessage(message) {
1418
- invariant(message.kind === "post", void 0, {
1419
- F: __dxlog_file5,
1420
- L: 122,
1421
- S: this,
1422
- A: [
1423
- "message.kind === 'post'",
1424
- ""
1425
- ]
1426
- });
1477
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1427
1478
  this._remoteStates.set(message.state.peerId, message.state);
1428
1479
  this.remoteStateChange.emit();
1429
1480
  }
@@ -1432,9 +1483,9 @@ var SpaceAwarenessProvider = class {
1432
1483
  // src/extensions/blast.ts
1433
1484
  import { EditorView as EditorView8, keymap as keymap3 } from "@codemirror/view";
1434
1485
  import defaultsDeep from "lodash.defaultsdeep";
1435
- import { throttle } from "@dxos/async";
1486
+ import { throttle as throttle2 } from "@dxos/async";
1436
1487
  import { invariant as invariant2 } from "@dxos/invariant";
1437
- var __dxlog_file6 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/blast.ts";
1488
+ var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/blast.ts";
1438
1489
  var defaultOptions = {
1439
1490
  effect: 2,
1440
1491
  maxParticles: 200,
@@ -1552,15 +1603,7 @@ var Blaster = class {
1552
1603
  return this._node;
1553
1604
  }
1554
1605
  initialize() {
1555
- invariant2(!this._canvas && !this._ctx, void 0, {
1556
- F: __dxlog_file6,
1557
- L: 142,
1558
- S: this,
1559
- A: [
1560
- "!this._canvas && !this._ctx",
1561
- ""
1562
- ]
1563
- });
1606
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1564
1607
  this._canvas = document.createElement("canvas");
1565
1608
  this._canvas.id = "code-blast-canvas";
1566
1609
  this._canvas.style.position = "absolute";
@@ -1589,15 +1632,7 @@ var Blaster = class {
1589
1632
  }
1590
1633
  }
1591
1634
  start() {
1592
- invariant2(this._canvas && this._ctx, void 0, {
1593
- F: __dxlog_file6,
1594
- L: 181,
1595
- S: this,
1596
- A: [
1597
- "this._canvas && this._ctx",
1598
- ""
1599
- ]
1600
- });
1635
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1601
1636
  this._running = true;
1602
1637
  this.loop();
1603
1638
  }
@@ -1624,11 +1659,11 @@ var Blaster = class {
1624
1659
  this.drawParticles();
1625
1660
  requestAnimationFrame(this.loop.bind(this));
1626
1661
  }
1627
- shake = throttle(({ time }) => {
1662
+ shake = throttle2(({ time }) => {
1628
1663
  this._shakeTime = this._shakeTimeMax || time;
1629
1664
  this._shakeTimeMax = time;
1630
1665
  }, 100);
1631
- spawn = throttle(({ element, point }) => {
1666
+ spawn = throttle2(({ element, point }) => {
1632
1667
  const color = getRGBComponents(element, this._options.color);
1633
1668
  const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
1634
1669
  const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
@@ -1802,11 +1837,11 @@ var blocks = () => [
1802
1837
  ".cm-line.block-line": {
1803
1838
  paddingLeft: "0.75rem",
1804
1839
  paddingRight: "0.75rem",
1805
- borderLeft: "1px solid var(--dx-subduedSeparator)",
1806
- borderRight: "1px solid var(--dx-subduedSeparator)"
1840
+ borderLeft: "1px solid var(--color-subdued-separator)",
1841
+ borderRight: "1px solid var(--color-subdued-separator)"
1807
1842
  },
1808
1843
  ".cm-line.block-single": {
1809
- border: "1px solid var(--dx-subduedSeparator)",
1844
+ border: "1px solid var(--color-subdued-separator)",
1810
1845
  borderRadius: "6px",
1811
1846
  paddingTop: "0.5rem",
1812
1847
  paddingBottom: "0.5rem",
@@ -1814,7 +1849,7 @@ var blocks = () => [
1814
1849
  marginBottom: "0.5rem"
1815
1850
  },
1816
1851
  ".cm-line.block-first": {
1817
- borderTop: "1px solid var(--dx-subduedSeparator)",
1852
+ borderTop: "1px solid var(--color-subdued-separator)",
1818
1853
  borderTopLeftRadius: "6px",
1819
1854
  borderTopRightRadius: "6px",
1820
1855
  paddingTop: "0.5rem",
@@ -1822,7 +1857,7 @@ var blocks = () => [
1822
1857
  },
1823
1858
  ".cm-line.block-middle": {},
1824
1859
  ".cm-line.block-last": {
1825
- borderBottom: "1px solid var(--dx-subduedSeparator)",
1860
+ borderBottom: "1px solid var(--color-subdued-separator)",
1826
1861
  borderBottomLeftRadius: "6px",
1827
1862
  borderBottomRightRadius: "6px",
1828
1863
  paddingBottom: "0.5rem",
@@ -1832,13 +1867,13 @@ var blocks = () => [
1832
1867
  ];
1833
1868
 
1834
1869
  // src/extensions/bookmarks.ts
1835
- import { Prec as Prec3, StateEffect as StateEffect3, StateField as StateField2 } from "@codemirror/state";
1870
+ import { Prec as Prec3, StateEffect as StateEffect4, StateField as StateField2 } from "@codemirror/state";
1836
1871
  import { keymap as keymap4 } from "@codemirror/view";
1837
- import { log as log5 } from "@dxos/log";
1838
- var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1839
- var addBookmark = StateEffect3.define();
1840
- var removeBookmark = StateEffect3.define();
1841
- var clearBookmarks = StateEffect3.define();
1872
+ import { log as log6 } from "@dxos/log";
1873
+ var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1874
+ var addBookmark = StateEffect4.define();
1875
+ var removeBookmark = StateEffect4.define();
1876
+ var clearBookmarks = StateEffect4.define();
1842
1877
  var bookmarks = () => {
1843
1878
  return [
1844
1879
  bookmarksField,
@@ -1847,12 +1882,7 @@ var bookmarks = () => {
1847
1882
  key: "Mod-ArrowUp",
1848
1883
  run: (view) => {
1849
1884
  const bookmarks2 = view.state.field(bookmarksField);
1850
- log5("up", bookmarks2, {
1851
- F: __dxlog_file7,
1852
- L: 29,
1853
- S: void 0,
1854
- C: (f, a) => f(...a)
1855
- });
1885
+ log6("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 18, S: void 0 });
1856
1886
  return true;
1857
1887
  }
1858
1888
  },
@@ -1860,12 +1890,7 @@ var bookmarks = () => {
1860
1890
  key: "Mod-ArrowDown",
1861
1891
  run: (view) => {
1862
1892
  const bookmarks2 = view.state.field(bookmarksField);
1863
- log5("down", bookmarks2, {
1864
- F: __dxlog_file7,
1865
- L: 37,
1866
- S: void 0,
1867
- C: (f, a) => f(...a)
1868
- });
1893
+ log6("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0 });
1869
1894
  return true;
1870
1895
  }
1871
1896
  }
@@ -1902,11 +1927,11 @@ var bookmarksField = StateField2.define({
1902
1927
 
1903
1928
  // src/extensions/comments.ts
1904
1929
  import { invertedEffects } from "@codemirror/commands";
1905
- import { StateEffect as StateEffect4, StateField as StateField3 } from "@codemirror/state";
1930
+ import { StateEffect as StateEffect5, StateField as StateField3 } from "@codemirror/state";
1906
1931
  import { Decoration as Decoration7, EditorView as EditorView11, ViewPlugin as ViewPlugin10, hoverTooltip, keymap as keymap6 } from "@codemirror/view";
1907
1932
  import sortBy from "lodash.sortby";
1908
1933
  import { debounce as debounce2 } from "@dxos/async";
1909
- import { log as log6 } from "@dxos/log";
1934
+ import { log as log7 } from "@dxos/log";
1910
1935
  import { isNonNullable } from "@dxos/util";
1911
1936
 
1912
1937
  // src/extensions/selection.ts
@@ -1915,9 +1940,9 @@ import { EditorView as EditorView10, keymap as keymap5 } from "@codemirror/view"
1915
1940
  import { debounce } from "@dxos/async";
1916
1941
  import { invariant as invariant3 } from "@dxos/invariant";
1917
1942
  import { isTruthy } from "@dxos/util";
1918
- var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/selection.ts";
1943
+ var __dxlog_file9 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/selection.ts";
1919
1944
  var documentId = singleValueFacet();
1920
- var stateRestoreAnnotation = "dxos.org/cm/state-restore";
1945
+ var stateRestoreAnnotation = "org.dxos.cm.state-restore";
1921
1946
  var createEditorStateTransaction = ({ scrollTo, selection }) => {
1922
1947
  return {
1923
1948
  selection,
@@ -1930,28 +1955,12 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1930
1955
  };
1931
1956
  var createEditorStateStore = (keyPrefix) => ({
1932
1957
  getState: (id) => {
1933
- invariant3(id, void 0, {
1934
- F: __dxlog_file8,
1935
- L: 47,
1936
- S: void 0,
1937
- A: [
1938
- "id",
1939
- ""
1940
- ]
1941
- });
1958
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 26, S: void 0, A: ["id", ""] });
1942
1959
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
1943
1960
  return state ? JSON.parse(state) : void 0;
1944
1961
  },
1945
1962
  setState: (id, state) => {
1946
- invariant3(id, void 0, {
1947
- F: __dxlog_file8,
1948
- L: 53,
1949
- S: void 0,
1950
- A: [
1951
- "id",
1952
- ""
1953
- ]
1954
- });
1963
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 31, S: void 0, A: ["id", ""] });
1955
1964
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
1956
1965
  }
1957
1966
  });
@@ -2003,10 +2012,10 @@ var selectionState = ({ getState, setState } = {}) => {
2003
2012
  };
2004
2013
 
2005
2014
  // src/extensions/comments.ts
2006
- var __dxlog_file9 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
2007
- var setComments = StateEffect4.define();
2008
- var setSelection = StateEffect4.define();
2009
- var setCommentState = StateEffect4.define();
2015
+ var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
2016
+ var setComments = StateEffect5.define();
2017
+ var setSelection = StateEffect5.define();
2018
+ var setCommentState = StateEffect5.define();
2010
2019
  var commentsState = StateField3.define({
2011
2020
  create: (state) => ({
2012
2021
  id: state.facet(documentId),
@@ -2048,14 +2057,14 @@ var commentsState = StateField3.define({
2048
2057
  var styles2 = EditorView11.theme({
2049
2058
  ".cm-comment, .cm-comment-current": {
2050
2059
  padding: "3px 0",
2051
- color: "var(--dx-cmCommentText)",
2052
- backgroundColor: "var(--dx-cmCommentSurface)"
2060
+ color: "var(--color-cm-comment-text)",
2061
+ backgroundColor: "var(--color-cm-comment-surface)"
2053
2062
  },
2054
2063
  ".cm-comment > span, .cm-comment-current > span": {
2055
2064
  boxDecorationBreak: "clone",
2056
- boxShadow: "0 0 1px 3px var(--dx-cmCommentSurface)",
2057
- backgroundColor: "var(--dx-cmCommentSurface)",
2058
- color: "var(--dx-cmCommentText)",
2065
+ boxShadow: "0 0 1px 3px var(--color-cm-comment-surface)",
2066
+ backgroundColor: "var(--color-cm-comment-surface)",
2067
+ color: "var(--color-cm-comment-text)",
2059
2068
  cursor: "pointer"
2060
2069
  }
2061
2070
  });
@@ -2073,12 +2082,7 @@ var commentsDecorations = EditorView11.decorations.compute([
2073
2082
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2074
2083
  const range = comment.range;
2075
2084
  if (!range) {
2076
- log6.warn("Invalid range:", range, {
2077
- F: __dxlog_file9,
2078
- L: 140,
2079
- S: void 0,
2080
- C: (f, a) => f(...a)
2081
- });
2085
+ log7.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 93, S: void 0 });
2082
2086
  return void 0;
2083
2087
  } else if (range.from === range.to) {
2084
2088
  return void 0;
@@ -2088,7 +2092,7 @@ var commentsDecorations = EditorView11.decorations.compute([
2088
2092
  }).filter(isNonNullable);
2089
2093
  return Decoration7.set(decorations2);
2090
2094
  });
2091
- var commentClickedEffect = StateEffect4.define();
2095
+ var commentClickedEffect = StateEffect5.define();
2092
2096
  var handleCommentClick = EditorView11.domEventHandlers({
2093
2097
  click: (event, view) => {
2094
2098
  let target = event.target;
@@ -2191,10 +2195,10 @@ var trackPastedComments = (onUpdate) => {
2191
2195
  const { comments: comments2 } = update2.startState.field(commentsState);
2192
2196
  const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
2193
2197
  if (!exists) {
2194
- const cursor2 = Cursor.getCursorFromRange(update2.state, comment);
2198
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
2195
2199
  onUpdate({
2196
2200
  id: comment.id,
2197
- cursor: cursor2
2201
+ cursor
2198
2202
  });
2199
2203
  }
2200
2204
  }
@@ -2206,7 +2210,7 @@ var mapTrackedComment = (comment, changes) => ({
2206
2210
  from: changes.mapPos(comment.from, 1),
2207
2211
  to: changes.mapPos(comment.to, 1)
2208
2212
  });
2209
- var restoreCommentEffect = StateEffect4.define({
2213
+ var restoreCommentEffect = StateEffect5.define({
2210
2214
  map: mapTrackedComment
2211
2215
  });
2212
2216
  var createComment = (view) => {
@@ -2223,13 +2227,13 @@ var createComment = (view) => {
2223
2227
  }
2224
2228
  });
2225
2229
  }
2226
- const cursor2 = Cursor.getCursorFromRange(view.state, {
2230
+ const cursor = Cursor.getCursorFromRange(view.state, {
2227
2231
  from,
2228
2232
  to
2229
2233
  });
2230
- if (cursor2) {
2234
+ if (cursor) {
2231
2235
  options.onCreate?.({
2232
- cursor: cursor2,
2236
+ cursor,
2233
2237
  from,
2234
2238
  location: view.coordsAtPos(from)
2235
2239
  });
@@ -2418,9 +2422,9 @@ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin10.fro
2418
2422
  // src/extensions/debug.ts
2419
2423
  import { syntaxTree } from "@codemirror/language";
2420
2424
  import { StateField as StateField4 } from "@codemirror/state";
2421
- var debugNodeLogger = (log11 = console.log) => {
2425
+ var debugNodeLogger = (log12 = console.log) => {
2422
2426
  const logTokens = (state) => syntaxTree(state).iterate({
2423
- enter: (node) => log11(node.type)
2427
+ enter: (node) => log12(node.type)
2424
2428
  });
2425
2429
  return StateField4.define({
2426
2430
  create: (state) => logTokens(state),
@@ -2455,8 +2459,8 @@ var dropFile = (options = {}) => {
2455
2459
  };
2456
2460
  var styles3 = EditorView12.theme({
2457
2461
  ".cm-dropCursor": {
2458
- borderLeft: "2px solid var(--dx-accentText)",
2459
- color: "var(--dx-accentText)",
2462
+ borderLeft: "2px solid var(--color-accent-text)",
2463
+ color: "var(--color-accent-text)",
2460
2464
  padding: "0 4px"
2461
2465
  },
2462
2466
  ".cm-dropCursor:after": {
@@ -2470,46 +2474,62 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap }
2470
2474
  import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
2471
2475
  import { searchKeymap } from "@codemirror/search";
2472
2476
  import { EditorState } from "@codemirror/state";
2473
- import { EditorView as EditorView15, ViewPlugin as ViewPlugin11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2, scrollPastEnd } from "@codemirror/view";
2477
+ import { EditorView as EditorView16, ViewPlugin as ViewPlugin12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2 } from "@codemirror/view";
2474
2478
  import { vscodeDarkStyle, vscodeLightStyle } from "@uiw/codemirror-theme-vscode";
2475
2479
  import defaultsDeep2 from "lodash.defaultsdeep";
2476
2480
  import { generateName } from "@dxos/display-name";
2477
- import { log as log7 } from "@dxos/log";
2481
+ import { log as log8 } from "@dxos/log";
2478
2482
  import { hexToHue, isTruthy as isTruthy2 } from "@dxos/util";
2479
2483
 
2480
- // src/styles/markdown.ts
2484
+ // src/styles/theme.ts
2485
+ import { EditorView as EditorView13 } from "@codemirror/view";
2481
2486
  import { mx as mx3 } from "@dxos/ui-theme";
2482
2487
  var headings = {
2483
- 1: "text-4xl",
2484
- 2: "text-3xl",
2485
- 3: "text-2xl",
2486
- 4: "text-xl",
2487
- 5: "text-lg",
2488
- 6: ""
2488
+ 1: {
2489
+ className: "text-3xl",
2490
+ fontSize: "var(--text-3xl)",
2491
+ lineHeight: "var(--text-4xl--line-height)"
2492
+ },
2493
+ 2: {
2494
+ className: "text-2xl",
2495
+ fontSize: "var(--text-2xl)",
2496
+ lineHeight: "var(--text-3xl--line-height)"
2497
+ },
2498
+ 3: {
2499
+ className: "text-xl",
2500
+ fontSize: "var(--text-xl)",
2501
+ lineHeight: "var(--text-2xl--line-height)"
2502
+ },
2503
+ 4: {
2504
+ className: "text-lg",
2505
+ fontSize: "var(--text-lg)",
2506
+ lineHeight: "var(--text-xl--line-height)"
2507
+ },
2508
+ 5: {
2509
+ className: "text-base",
2510
+ fontSize: "var(--text-base)",
2511
+ lineHeight: "var(--text-lg--line-height)"
2512
+ },
2513
+ 6: {
2514
+ className: "text-base",
2515
+ fontSize: "var(--text-base)",
2516
+ lineHeight: "var(--text-base--line-height)"
2517
+ }
2489
2518
  };
2519
+ var fontBody = '"Inter Variable", ui-sans-serif, system-ui, sans-serif';
2520
+ var fontMono = '"JetBrains Mono Variable", ui-monospace, "Cascadia Code", "Source Code Pro", monospace';
2490
2521
  var markdownTheme = {
2491
- code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
2492
- codeMark: "font-mono text-primary-500",
2493
- mark: "opacity-50",
2494
- heading: (level) => {
2495
- return mx3(headings[level], "dark:text-neutral-400");
2496
- }
2522
+ code: "font-mono! cm-code-inline",
2523
+ codeMark: "font-mono! cm-code-mark",
2524
+ mark: "font-mono!",
2525
+ heading: (level) => ({
2526
+ className: mx3(headings[level].className, "font-light text-(--color-cm-heading-number)"),
2527
+ color: "var(--color-cm-heading) !important",
2528
+ lineHeight: headings[level].lineHeight,
2529
+ fontSize: headings[level].fontSize,
2530
+ fontWeight: "100 !important"
2531
+ })
2497
2532
  };
2498
-
2499
- // src/styles/theme.ts
2500
- import { EditorView as EditorView13 } from "@codemirror/view";
2501
-
2502
- // src/styles/tokens.ts
2503
- import { tokens } from "@dxos/ui-theme";
2504
- import { get } from "@dxos/util";
2505
- var getToken = (path, defaultValue) => {
2506
- const value = get(tokens, path, defaultValue);
2507
- return value?.toString() ?? "";
2508
- };
2509
- var fontBody = getToken("fontFamily.body");
2510
- var fontMono = getToken("fontFamily.mono");
2511
-
2512
- // src/styles/theme.ts
2513
2533
  var baseTheme = EditorView13.baseTheme({
2514
2534
  /**
2515
2535
  * Outer frame.
@@ -2521,17 +2541,28 @@ var baseTheme = EditorView13.baseTheme({
2521
2541
  /**
2522
2542
  * Scroller
2523
2543
  */
2524
- ".cm-scroller": {},
2544
+ ".cm-scroller": {
2545
+ // Browser scroll-anchoring: see comment in `scroller.ts`. `auto` lets the browser pin a
2546
+ // stable element near the viewport top so widget resizes (e.g. tool-block TogglePanel
2547
+ // open/close) don't jump the user's view.
2548
+ overflowAnchor: "auto"
2549
+ },
2525
2550
  ".cm-scroller::-webkit-scrollbar": {
2526
- width: "8px"
2551
+ width: "var(--scrollbar-size,8px)",
2552
+ height: "var(--scrollbar-size,8px)"
2553
+ },
2554
+ ".cm-scroller::-webkit-scrollbar-corner": {
2555
+ background: "transparent"
2556
+ },
2557
+ ".cm-scroller::-webkit-scrollbar-track": {
2558
+ background: "transparent"
2527
2559
  },
2528
- ".cm-scroller::-webkit-scrollbar-track": {},
2529
2560
  ".cm-scroller::-webkit-scrollbar-thumb": {
2530
2561
  background: "transparent",
2531
2562
  transition: "background 0.15s"
2532
2563
  },
2533
2564
  "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
2534
- background: "var(--dx-scrollbarThumb)"
2565
+ background: "var(--color-scrollbar-thumb)"
2535
2566
  },
2536
2567
  /**
2537
2568
  * Content
@@ -2539,7 +2570,6 @@ var baseTheme = EditorView13.baseTheme({
2539
2570
  */
2540
2571
  ".cm-content": {
2541
2572
  padding: "unset",
2542
- lineHeight: "24px",
2543
2573
  color: "unset"
2544
2574
  },
2545
2575
  /**
@@ -2553,8 +2583,8 @@ var baseTheme = EditorView13.baseTheme({
2553
2583
  ".cm-gutter": {},
2554
2584
  ".cm-gutter.cm-lineNumbers": {
2555
2585
  paddingRight: "4px",
2556
- borderRight: "1px solid var(--dx-subduedSeparator)",
2557
- color: "var(--dx-subduedText)"
2586
+ borderRight: "1px solid var(--color-subdued-separator)",
2587
+ color: "var(--color-subdued)"
2558
2588
  },
2559
2589
  ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
2560
2590
  minWidth: "40px"
@@ -2570,29 +2600,36 @@ var baseTheme = EditorView13.baseTheme({
2570
2600
  * Line.
2571
2601
  */
2572
2602
  ".cm-line": {
2573
- lineHeight: "24px",
2603
+ lineHeight: 1.5,
2574
2604
  paddingInline: 0
2575
2605
  },
2606
+ /**
2607
+ * Force all inline children to inherit line-height to prevent monospace font metrics
2608
+ * (JetBrains Mono ascent/descent) from inflating the line box beyond 24px.
2609
+ */
2610
+ ".cm-line *": {
2611
+ lineHeight: "inherit"
2612
+ },
2576
2613
  ".cm-activeLine": {
2577
- background: "var(--dx-cmActiveLine)"
2614
+ background: "var(--color-cm-active-line)"
2578
2615
  },
2579
2616
  /**
2580
2617
  * Cursor (layer).
2581
2618
  */
2582
2619
  ".cm-cursor, .cm-dropCursor": {
2583
- borderLeft: "2px solid var(--dx-cmCursor)"
2620
+ borderLeft: "2px solid var(--color-cm-cursor)"
2584
2621
  },
2585
2622
  ".cm-placeholder": {
2586
- color: "var(--dx-placeholder)"
2623
+ color: "var(--color-placeholder)"
2587
2624
  },
2588
2625
  /**
2589
2626
  * Selection (layer).
2590
2627
  */
2591
2628
  ".cm-selectionBackground": {
2592
- background: "var(--dx-cmSelection)"
2629
+ background: "var(--color-cm-selection)"
2593
2630
  },
2594
2631
  "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
2595
- background: "var(--dx-cmFocusedSelection)"
2632
+ background: "var(--color-cm-focused-selection)"
2596
2633
  },
2597
2634
  /**
2598
2635
  * Search.
@@ -2602,8 +2639,8 @@ var baseTheme = EditorView13.baseTheme({
2602
2639
  margin: "0 -3px",
2603
2640
  padding: "3px",
2604
2641
  borderRadius: "3px",
2605
- background: "var(--dx-cmHighlightSurface)",
2606
- color: "var(--dx-cmHighlight)"
2642
+ background: "var(--color-cm-highlight-surface)",
2643
+ color: "var(--color-cm-highlight)"
2607
2644
  },
2608
2645
  ".cm-searchMatch-selected": {
2609
2646
  textDecoration: "underline"
@@ -2614,20 +2651,29 @@ var baseTheme = EditorView13.baseTheme({
2614
2651
  ".cm-link": {
2615
2652
  textDecorationLine: "underline",
2616
2653
  textDecorationThickness: "1px",
2617
- textDecorationColor: "var(--dx-separator)",
2654
+ textDecorationColor: "var(--color-separator)",
2618
2655
  textUnderlineOffset: "2px",
2619
2656
  borderRadius: ".125rem"
2620
2657
  },
2621
2658
  ".cm-link > span": {
2622
- color: "var(--dx-accentText)"
2659
+ color: "var(--color-accent-text)"
2660
+ },
2661
+ ".cm-link > span:hover": {
2662
+ color: "var(--color-accent-text-hover)"
2623
2663
  },
2624
2664
  /**
2625
2665
  * Tooltip.
2626
2666
  */
2627
2667
  ".cm-tooltip": {
2628
- background: "var(--dx-baseSurface)"
2668
+ background: "var(--color-modal-surface)"
2629
2669
  },
2630
2670
  ".cm-tooltip-below": {},
2671
+ ".cm-tooltip-hover": {
2672
+ background: "var(--color-modal-surface)",
2673
+ border: "1px solid var(--color-separator)",
2674
+ borderRadius: "4px",
2675
+ overflow: "hidden"
2676
+ },
2631
2677
  /**
2632
2678
  * Autocomplete.
2633
2679
  * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
@@ -2635,7 +2681,7 @@ var baseTheme = EditorView13.baseTheme({
2635
2681
  ".cm-tooltip.cm-tooltip-autocomplete": {
2636
2682
  marginTop: "6px",
2637
2683
  marginLeft: "-10px",
2638
- border: "2px solid var(--dx-separator)",
2684
+ border: "2px solid var(--color-separator)",
2639
2685
  borderRadius: "4px"
2640
2686
  },
2641
2687
  ".cm-tooltip.cm-tooltip-autocomplete > ul": {
@@ -2645,12 +2691,12 @@ var baseTheme = EditorView13.baseTheme({
2645
2691
  padding: "4px"
2646
2692
  },
2647
2693
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2648
- background: "var(--dx-activeSurface)",
2649
- color: "var(--dx-activeSurfaceText)"
2694
+ background: "var(--color-active-surface)",
2695
+ color: "var(--color-base-surface-text)"
2650
2696
  },
2651
2697
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2652
2698
  paddingLeft: "4px !important",
2653
- color: "var(--dx-hoverSurfaceText)"
2699
+ color: "var(--color-base-surface-text)"
2654
2700
  },
2655
2701
  /**
2656
2702
  * Completion info.
@@ -2659,17 +2705,17 @@ var baseTheme = EditorView13.baseTheme({
2659
2705
  width: "360px !important",
2660
2706
  margin: "-10px 1px 0 1px",
2661
2707
  padding: "8px !important",
2662
- borderColor: "var(--dx-separator)"
2708
+ borderColor: "var(--color-separator)"
2663
2709
  },
2664
2710
  ".cm-completionIcon": {
2665
2711
  display: "none"
2666
2712
  },
2667
2713
  ".cm-completionLabel": {
2668
- color: "var(--dx-description)",
2714
+ color: "var(--color-description)",
2669
2715
  padding: "0 4px"
2670
2716
  },
2671
2717
  ".cm-completionMatchedText": {
2672
- color: "var(--dx-baseText)",
2718
+ color: "var(--color-base-surface-text)",
2673
2719
  textDecoration: "none !important"
2674
2720
  },
2675
2721
  /**
@@ -2693,7 +2739,7 @@ var baseTheme = EditorView13.baseTheme({
2693
2739
  backgroundColor: "var(--surface-bg)"
2694
2740
  },
2695
2741
  ".cm-panel input, .cm-panel button, .cm-panel label": {
2696
- color: "var(--dx-subdued)",
2742
+ color: "var(--color-subdued)",
2697
2743
  fontSize: "14px",
2698
2744
  all: "unset",
2699
2745
  margin: "3px !important",
@@ -2701,10 +2747,10 @@ var baseTheme = EditorView13.baseTheme({
2701
2747
  outline: "1px solid transparent"
2702
2748
  },
2703
2749
  ".cm-panel input, .cm-panel button": {
2704
- backgroundColor: "var(--dx-inputSurface)"
2750
+ backgroundColor: "var(--color-input-surface)"
2705
2751
  },
2706
2752
  ".cm-panel input:focus, .cm-panel button:focus": {
2707
- outline: "1px solid var(--dx-neutralFocusIndicator)"
2753
+ outline: "1px solid var(--color-neutral-focus-indicator)"
2708
2754
  },
2709
2755
  ".cm-panel label": {
2710
2756
  display: "inline-flex",
@@ -2717,34 +2763,33 @@ var baseTheme = EditorView13.baseTheme({
2717
2763
  height: "8px",
2718
2764
  marginRight: "6px !important",
2719
2765
  padding: "2px !important",
2720
- color: "var(--dx-neutralFocusIndicator)"
2766
+ color: "var(--color-neutral-focus-indicator)"
2721
2767
  },
2722
2768
  ".cm-panel button": {
2723
2769
  "&:hover": {
2724
- // TODO(burdon): Replace with layer and @apply bg-accentSurfaceHover
2725
- backgroundColor: "var(--dx-accentSurfaceHover) !important"
2770
+ // TODO(burdon): Replace with layer and @apply bg-accent-surface-hover
2771
+ backgroundColor: "var(--color-accent-surface-hover) !important"
2726
2772
  },
2727
2773
  "&:active": {
2728
- backgroundColor: "var(--dx-accentSurfaceHover)"
2774
+ backgroundColor: "var(--color-accent-surface-hover)"
2729
2775
  }
2730
2776
  },
2731
2777
  ".cm-panel.cm-search": {
2732
2778
  padding: "4px",
2733
- borderTop: "1px solid var(--dx-separator)"
2779
+ borderTop: "1px solid var(--color-separator)"
2734
2780
  }
2735
2781
  });
2736
2782
  var editorGutter = EditorView13.theme({
2737
2783
  ".cm-gutters": {
2738
2784
  // NOTE: Non-transparent background required to cover content if scrolling horizontally.
2739
- background: "var(--dx-baseSurface) !important",
2785
+ background: "var(--color-base-surface) !important",
2740
2786
  paddingRight: "1rem"
2741
2787
  }
2742
2788
  });
2743
2789
  var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2744
- // Set metrics on the scroller (this is often what CM uses for layout).
2790
+ // Main content.
2745
2791
  ".cm-scroller": {
2746
- fontFamily: monospace ? fontMono : fontBody,
2747
- fontSize: "16px"
2792
+ fontFamily: monospace ? fontMono : fontBody
2748
2793
  },
2749
2794
  // Maintain defaults for UI components.
2750
2795
  ".cm-content, .cm-gutters, .cm-panel": {
@@ -2754,9 +2799,9 @@ var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2754
2799
  });
2755
2800
 
2756
2801
  // src/extensions/focus.ts
2757
- import { StateEffect as StateEffect5, StateField as StateField5 } from "@codemirror/state";
2802
+ import { StateEffect as StateEffect6, StateField as StateField5 } from "@codemirror/state";
2758
2803
  import { EditorView as EditorView14 } from "@codemirror/view";
2759
- var focusEffect = StateEffect5.define();
2804
+ var focusEffect = StateEffect6.define();
2760
2805
  var focusField = StateField5.define({
2761
2806
  create: () => false,
2762
2807
  update: (value, tr) => {
@@ -2784,9 +2829,32 @@ var focus = [
2784
2829
  })
2785
2830
  ];
2786
2831
 
2832
+ // src/extensions/scroll-past-end.ts
2833
+ import { EditorView as EditorView15, ViewPlugin as ViewPlugin11 } from "@codemirror/view";
2834
+ var scrollPastEndPlugin = ViewPlugin11.fromClass(class {
2835
+ height = 1e3;
2836
+ attrs = {
2837
+ style: "padding-bottom: 1000px"
2838
+ };
2839
+ update({ view }) {
2840
+ const lastLineBlock = view.lineBlockAt(view.state.doc.length);
2841
+ const height = view.dom.clientHeight - lastLineBlock.height - view.documentPadding.top - 0.5;
2842
+ if (height >= 0 && height !== this.height) {
2843
+ this.height = height;
2844
+ this.attrs = {
2845
+ style: `padding-bottom: ${height}px`
2846
+ };
2847
+ }
2848
+ }
2849
+ });
2850
+ var scrollPastEnd = () => [
2851
+ scrollPastEndPlugin,
2852
+ EditorView15.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?.attrs ?? null)
2853
+ ];
2854
+
2787
2855
  // src/extensions/factories.ts
2788
- var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2789
- var tabbable = EditorView15.contentAttributes.of({
2856
+ var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2857
+ var tabbable = EditorView16.contentAttributes.of({
2790
2858
  tabindex: "0"
2791
2859
  });
2792
2860
  var filterChars = (chars) => {
@@ -2839,13 +2907,8 @@ var createBasicExtensions = (propsProp) => {
2839
2907
  const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2840
2908
  return [
2841
2909
  // NOTE: Doesn't catch errors in keymap functions.
2842
- EditorView15.exceptionSink.of((err) => {
2843
- log7.catch(err, void 0, {
2844
- F: __dxlog_file10,
2845
- L: 132,
2846
- S: void 0,
2847
- C: (f, a) => f(...a)
2848
- });
2910
+ EditorView16.exceptionSink.of((err) => {
2911
+ log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
2849
2912
  }),
2850
2913
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2851
2914
  props.bracketMatching && bracketMatching(),
@@ -2854,7 +2917,7 @@ var createBasicExtensions = (propsProp) => {
2854
2917
  props.drawSelection && drawSelection({
2855
2918
  cursorBlinkRate: 1200
2856
2919
  }),
2857
- props.editable !== void 0 && EditorView15.editable.of(props.editable),
2920
+ props.editable !== void 0 && EditorView16.editable.of(props.editable),
2858
2921
  props.focus && focus,
2859
2922
  props.highlightActiveLine && highlightActiveLine(),
2860
2923
  props.history && history(),
@@ -2862,9 +2925,16 @@ var createBasicExtensions = (propsProp) => {
2862
2925
  lineNumbers(),
2863
2926
  editorGutter
2864
2927
  ],
2865
- props.lineWrapping && EditorView15.lineWrapping,
2928
+ props.lineWrapping && EditorView16.lineWrapping,
2866
2929
  props.placeholder && placeholder2(props.placeholder),
2867
2930
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2931
+ // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
2932
+ // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that
2933
+ // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits
2934
+ // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)
2935
+ // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers
2936
+ // depend on being able to populate the doc themselves.
2937
+ props.readOnly && EditorState.transactionFilter.of((tr) => tr.docChanged && (tr.isUserEvent("input") || tr.isUserEvent("delete") || tr.isUserEvent("undo") || tr.isUserEvent("redo")) ? [] : tr),
2868
2938
  props.scrollPastEnd && scrollPastEnd(),
2869
2939
  props.tabbable && tabbable,
2870
2940
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -2894,12 +2964,12 @@ var createBasicExtensions = (propsProp) => {
2894
2964
  };
2895
2965
  var grow = {
2896
2966
  editor: {
2897
- className: "bs-full is-full"
2967
+ className: "h-full w-full"
2898
2968
  }
2899
2969
  };
2900
2970
  var fullWidth = {
2901
2971
  editor: {
2902
- className: "is-full"
2972
+ className: "w-full"
2903
2973
  }
2904
2974
  };
2905
2975
  var defaultThemeSlots = grow;
@@ -2907,24 +2977,29 @@ var defaultStyles = {
2907
2977
  dark: vscodeDarkStyle,
2908
2978
  light: vscodeLightStyle
2909
2979
  };
2910
- var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp } = {}) => {
2980
+ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp, themeMode } = {}) => {
2911
2981
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
2912
2982
  return [
2913
2983
  baseTheme,
2914
- EditorView15.darkTheme.of(themeMode === "dark"),
2984
+ EditorView16.darkTheme.of(themeMode === "dark"),
2915
2985
  createFontTheme({
2916
2986
  monospace
2917
2987
  }),
2918
2988
  syntaxHighlightingProp && syntaxHighlighting(HighlightStyle.define(themeMode === "dark" ? defaultStyles.dark : defaultStyles.light)),
2919
- slots.editor?.className && EditorView15.editorAttributes.of({
2989
+ slots.editor?.className && EditorView16.editorAttributes.of({
2920
2990
  class: slots.editor.className
2921
2991
  }),
2922
- slots.content?.className && EditorView15.contentAttributes.of({
2992
+ slots.content?.className && EditorView16.contentAttributes.of({
2923
2993
  class: slots.content.className
2924
2994
  }),
2925
- slots.scroll?.className && ViewPlugin11.fromClass(class {
2995
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
2926
2996
  constructor(view) {
2927
- view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
2997
+ if (slots.scroller?.className) {
2998
+ view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
2999
+ }
3000
+ if (scrollbarThin) {
3001
+ view.scrollDOM.style.setProperty("--scrollbar-size", "4px");
3002
+ }
2928
3003
  }
2929
3004
  })
2930
3005
  ].filter(isTruthy2);
@@ -2942,8 +3017,8 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2942
3017
  channel: `awareness.${id}`,
2943
3018
  peerId: identity.identityKey.toHex(),
2944
3019
  info: {
2945
- darkColor: `var(--dx-${hue}Cursor)`,
2946
- lightColor: `var(--dx-${hue}Cursor)`,
3020
+ darkColor: `var(--color-${hue}-border)`,
3021
+ lightColor: `var(--color-${hue}-border)`,
2947
3022
  displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex())
2948
3023
  }
2949
3024
  })));
@@ -2953,7 +3028,7 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2953
3028
 
2954
3029
  // src/extensions/folding.ts
2955
3030
  import { codeFolding, foldGutter } from "@codemirror/language";
2956
- import { EditorView as EditorView16 } from "@codemirror/view";
3031
+ import { EditorView as EditorView17 } from "@codemirror/view";
2957
3032
  import { Domino as Domino2, mx as mx4 } from "@dxos/ui";
2958
3033
  var folding = () => {
2959
3034
  return [
@@ -2961,13 +3036,14 @@ var folding = () => {
2961
3036
  placeholderDOM: () => Domino2.of("span").root
2962
3037
  }),
2963
3038
  foldGutter({
3039
+ // NOTE: We can't animate since the element is remounted on state change.
2964
3040
  markerDOM: (open) => {
2965
- return Domino2.of("div").classNames("flex bs-full justify-center items-center").children(Domino2.of("svg", Domino2.SVG).classNames(mx4("is-4 bs-4 cursor-pointer", open && "rotate-90")).children(Domino2.of("use", Domino2.SVG).attributes({
3041
+ return Domino2.of("div").classNames("flex h-full justify-center items-center").append(Domino2.of("svg", Domino2.SVG).classNames(mx4("w-4 h-4 cursor-pointer", open && "rotate-90")).append(Domino2.of("use", Domino2.SVG).attributes({
2966
3042
  href: Domino2.icon("ph--caret-right--regular")
2967
3043
  }))).root;
2968
3044
  }
2969
3045
  }),
2970
- EditorView16.theme({
3046
+ EditorView17.theme({
2971
3047
  ".cm-foldGutter": {
2972
3048
  opacity: 0.3,
2973
3049
  transition: "opacity 0.3s",
@@ -2981,7 +3057,7 @@ var folding = () => {
2981
3057
  };
2982
3058
 
2983
3059
  // src/extensions/hashtag.ts
2984
- import { Decoration as Decoration8, EditorView as EditorView17, MatchDecorator, ViewPlugin as ViewPlugin12, WidgetType as WidgetType4 } from "@codemirror/view";
3060
+ import { Decoration as Decoration8, EditorView as EditorView18, MatchDecorator, ViewPlugin as ViewPlugin13, WidgetType as WidgetType4 } from "@codemirror/view";
2985
3061
  import { getHashStyles, mx as mx5 } from "@dxos/ui-theme";
2986
3062
  var TagWidget = class extends WidgetType4 {
2987
3063
  _text;
@@ -3002,7 +3078,7 @@ var tagMatcher = new MatchDecorator({
3002
3078
  })
3003
3079
  });
3004
3080
  var hashtag = () => [
3005
- ViewPlugin12.fromClass(class {
3081
+ ViewPlugin13.fromClass(class {
3006
3082
  tags;
3007
3083
  constructor(view) {
3008
3084
  this.tags = tagMatcher.createDeco(view);
@@ -3012,11 +3088,11 @@ var hashtag = () => [
3012
3088
  }
3013
3089
  }, {
3014
3090
  decorations: (instance) => instance.tags,
3015
- provide: (plugin) => EditorView17.atomicRanges.of((view) => {
3091
+ provide: (plugin) => EditorView18.atomicRanges.of((view) => {
3016
3092
  return view.plugin(plugin)?.tags || Decoration8.none;
3017
3093
  })
3018
3094
  }),
3019
- EditorView17.theme({
3095
+ EditorView18.theme({
3020
3096
  ".cm-tag": {
3021
3097
  borderRadius: "4px",
3022
3098
  marginRight: "6px",
@@ -3071,18 +3147,18 @@ var schemaLinter = (validate) => (view) => {
3071
3147
  };
3072
3148
 
3073
3149
  // src/extensions/listener.ts
3074
- import { EditorView as EditorView18 } from "@codemirror/view";
3150
+ import { EditorView as EditorView19 } from "@codemirror/view";
3075
3151
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
3076
3152
  var listener = ({ onFocus, onChange }) => {
3077
3153
  return [
3078
- onFocus && EditorView18.focusChangeEffect.of((state, focusing) => {
3154
+ onFocus && EditorView19.focusChangeEffect.of((state, focusing) => {
3079
3155
  onFocus({
3080
3156
  id: state.facet(documentId),
3081
3157
  focusing
3082
3158
  });
3083
3159
  return null;
3084
3160
  }),
3085
- onChange && EditorView18.updateListener.of(({ state, docChanged }) => {
3161
+ onChange && EditorView19.updateListener.of(({ state, docChanged }) => {
3086
3162
  if (docChanged) {
3087
3163
  onChange({
3088
3164
  id: state.facet(documentId),
@@ -3097,7 +3173,7 @@ var listener = ({ onFocus, onChange }) => {
3097
3173
  import { snippet } from "@codemirror/autocomplete";
3098
3174
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3099
3175
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
3100
- import { EditorView as EditorView19, keymap as keymap8 } from "@codemirror/view";
3176
+ import { EditorView as EditorView20, keymap as keymap8 } from "@codemirror/view";
3101
3177
  import { debounceAndThrottle } from "@dxos/async";
3102
3178
  var formattingEquals = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
3103
3179
  var Inline = /* @__PURE__ */ (function(Inline2) {
@@ -4186,7 +4262,7 @@ var getFormatting = (state) => {
4186
4262
  };
4187
4263
  };
4188
4264
  var formattingListener = (onStateChange, delay = 100) => {
4189
- return EditorView19.updateListener.of(debounceAndThrottle((update2) => {
4265
+ return EditorView20.updateListener.of(debounceAndThrottle((update2) => {
4190
4266
  if (update2.docChanged || update2.selectionSet) {
4191
4267
  onStateChange(getFormatting(update2.state));
4192
4268
  }
@@ -4247,8 +4323,7 @@ import { completionKeymap } from "@codemirror/autocomplete";
4247
4323
  import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
4248
4324
  import { jsonLanguage } from "@codemirror/lang-json";
4249
4325
  import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
4250
- import { xml } from "@codemirror/lang-xml";
4251
- import { LanguageDescription, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4326
+ import { foldNodeProp, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4252
4327
  import { languages } from "@codemirror/language-data";
4253
4328
  import { keymap as keymap9 } from "@codemirror/view";
4254
4329
  import { isTruthy as isTruthy3 } from "@dxos/util";
@@ -4369,30 +4444,38 @@ var markdownHighlightStyle = (_options = {}) => {
4369
4444
  ],
4370
4445
  class: "font-mono"
4371
4446
  },
4372
- // Headings.
4447
+ // Headings — use CSS properties only (no class:) so CodeMirror generates scoped CSS via
4448
+ // StyleModule that overrides vscodeDarkStyle's t.heading rule. When class: is present,
4449
+ // HighlightStyle silently ignores all other CSS properties (they're mutually exclusive).
4450
+ // Font sizes use Tailwind v4 CSS variables so nothing is hardcoded.
4451
+ {
4452
+ tag: tags.heading,
4453
+ color: "var(--color-cm-heading) !important",
4454
+ fontWeight: "300"
4455
+ },
4373
4456
  {
4374
4457
  tag: tags.heading1,
4375
- class: markdownTheme.heading(1)
4458
+ ...markdownTheme.heading(1)
4376
4459
  },
4377
4460
  {
4378
4461
  tag: tags.heading2,
4379
- class: markdownTheme.heading(2)
4462
+ ...markdownTheme.heading(2)
4380
4463
  },
4381
4464
  {
4382
4465
  tag: tags.heading3,
4383
- class: markdownTheme.heading(3)
4466
+ ...markdownTheme.heading(3)
4384
4467
  },
4385
4468
  {
4386
4469
  tag: tags.heading4,
4387
- class: markdownTheme.heading(4)
4470
+ ...markdownTheme.heading(4)
4388
4471
  },
4389
4472
  {
4390
4473
  tag: tags.heading5,
4391
- class: markdownTheme.heading(5)
4474
+ ...markdownTheme.heading(5)
4392
4475
  },
4393
4476
  {
4394
4477
  tag: tags.heading6,
4395
- class: markdownTheme.heading(6)
4478
+ ...markdownTheme.heading(6)
4396
4479
  },
4397
4480
  // Emphasis.
4398
4481
  {
@@ -4447,15 +4530,23 @@ var createMarkdownExtensions = (options = {}) => {
4447
4530
  // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
4448
4531
  base: markdownLanguage2,
4449
4532
  // Languages for syntax highlighting fenced code blocks.
4533
+ // Caller-supplied languages are checked first so they can override defaults.
4450
4534
  defaultCodeLanguage: jsonLanguage,
4451
- codeLanguages: languages,
4535
+ codeLanguages: [
4536
+ ...options.codeLanguages ?? [],
4537
+ ...languages
4538
+ ],
4452
4539
  // Don't complete HTML tags.
4453
4540
  completeHTMLTags: false,
4454
4541
  // Parser extensions.
4455
4542
  extensions: [
4456
4543
  // GFM provided by default.
4457
4544
  markdownTagsExtensions,
4458
- ...options.extensions ?? defaultExtensions()
4545
+ ...options.extensions ?? defaultExtensions(),
4546
+ // Disable folding for fenced code blocks by overriding foldNodeProp.
4547
+ // Note: returning null from foldService does not prevent syntaxFolding fallback,
4548
+ // so we must override the node prop directly on the FencedCode node type.
4549
+ noFencedCodeFolding
4459
4550
  ]
4460
4551
  }),
4461
4552
  // Custom styles.
@@ -4470,18 +4561,13 @@ var createMarkdownExtensions = (options = {}) => {
4470
4561
  ].filter(isTruthy3))
4471
4562
  ];
4472
4563
  };
4473
- var xmlLanguageDesc = LanguageDescription.of({
4474
- name: "xml",
4475
- alias: [
4476
- "html",
4477
- "xhtml"
4478
- ],
4479
- extensions: [
4480
- "xml",
4481
- "xhtml"
4482
- ],
4483
- load: async () => xml()
4484
- });
4564
+ var noFencedCodeFolding = {
4565
+ props: [
4566
+ foldNodeProp.add({
4567
+ FencedCode: () => null
4568
+ })
4569
+ ]
4570
+ };
4485
4571
  var defaultExtensions = () => [
4486
4572
  noSetExtHeading,
4487
4573
  noHtml
@@ -4501,19 +4587,19 @@ var debugTree = (cb) => StateField6.define({
4501
4587
  update: (value, tr) => cb(convertTreeToJson(tr.state))
4502
4588
  });
4503
4589
  var convertTreeToJson = (state) => {
4504
- const treeToJson = (cursor2) => {
4590
+ const treeToJson = (cursor) => {
4505
4591
  const node = {
4506
- type: cursor2.type.name,
4507
- from: cursor2.from,
4508
- to: cursor2.to,
4509
- text: state.doc.slice(cursor2.from, cursor2.to).toString(),
4592
+ type: cursor.type.name,
4593
+ from: cursor.from,
4594
+ to: cursor.to,
4595
+ text: state.doc.slice(cursor.from, cursor.to).toString(),
4510
4596
  children: []
4511
4597
  };
4512
- if (cursor2.firstChild()) {
4598
+ if (cursor.firstChild()) {
4513
4599
  do {
4514
- node.children.push(treeToJson(cursor2));
4515
- } while (cursor2.nextSibling());
4516
- cursor2.parent();
4600
+ node.children.push(treeToJson(cursor));
4601
+ } while (cursor.nextSibling());
4602
+ cursor.parent();
4517
4603
  }
4518
4604
  return node;
4519
4605
  };
@@ -4522,17 +4608,16 @@ var convertTreeToJson = (state) => {
4522
4608
 
4523
4609
  // src/extensions/markdown/decorate.ts
4524
4610
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4525
- import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect6 } from "@codemirror/state";
4526
- import { Decoration as Decoration11, EditorView as EditorView23, ViewPlugin as ViewPlugin14, WidgetType as WidgetType7 } from "@codemirror/view";
4611
+ import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect7 } from "@codemirror/state";
4612
+ import { Decoration as Decoration11, EditorView as EditorView24, ViewPlugin as ViewPlugin15, WidgetType as WidgetType7 } from "@codemirror/view";
4527
4613
  import { invariant as invariant4 } from "@dxos/invariant";
4528
- import { mx as mx6 } from "@dxos/ui-theme";
4529
4614
 
4530
4615
  // src/extensions/markdown/changes.ts
4531
4616
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4532
4617
  import { Transaction as Transaction4 } from "@codemirror/state";
4533
- import { ViewPlugin as ViewPlugin13 } from "@codemirror/view";
4618
+ import { ViewPlugin as ViewPlugin14 } from "@codemirror/view";
4534
4619
  var adjustChanges = () => {
4535
- return ViewPlugin13.fromClass(class {
4620
+ return ViewPlugin14.fromClass(class {
4536
4621
  update(update2) {
4537
4622
  const tree = syntaxTree4(update2.state);
4538
4623
  const adjustments = [];
@@ -4674,7 +4759,7 @@ var getValidUrl = (str) => {
4674
4759
  // src/extensions/markdown/image.ts
4675
4760
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4676
4761
  import { StateField as StateField7 } from "@codemirror/state";
4677
- import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType5 } from "@codemirror/view";
4762
+ import { Decoration as Decoration9, EditorView as EditorView21, WidgetType as WidgetType5 } from "@codemirror/view";
4678
4763
  var image = (_options = {}) => {
4679
4764
  return [
4680
4765
  StateField7.define({
@@ -4685,10 +4770,10 @@ var image = (_options = {}) => {
4685
4770
  if (!tr.docChanged && !tr.selection) {
4686
4771
  return value;
4687
4772
  }
4688
- const cursor2 = tr.state.selection.main.head;
4773
+ const cursor = tr.state.selection.main.head;
4689
4774
  const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
4690
- let from = Math.min(cursor2, oldCursor);
4691
- let to = Math.max(cursor2, oldCursor);
4775
+ let from = Math.min(cursor, oldCursor);
4776
+ let to = Math.max(cursor, oldCursor);
4692
4777
  tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
4693
4778
  from = Math.min(from, fromB);
4694
4779
  to = Math.max(to, toB);
@@ -4702,19 +4787,19 @@ var image = (_options = {}) => {
4702
4787
  add: buildDecorations(tr.state, from, to)
4703
4788
  });
4704
4789
  },
4705
- provide: (field) => EditorView20.decorations.from(field)
4790
+ provide: (field) => EditorView21.decorations.from(field)
4706
4791
  })
4707
4792
  ];
4708
4793
  };
4709
4794
  var buildDecorations = (state, from, to) => {
4710
4795
  const decorations2 = [];
4711
- const cursor2 = state.selection.main.head;
4796
+ const cursor = state.selection.main.head;
4712
4797
  syntaxTree5(state).iterate({
4713
4798
  enter: (node) => {
4714
4799
  if (node.name === "Image") {
4715
4800
  const urlNode = node.node.getChild("URL");
4716
4801
  if (urlNode) {
4717
- const hide2 = state.readOnly || cursor2 < node.from || cursor2 > node.to || !state.field(focusField);
4802
+ const hide2 = state.readOnly || cursor < node.from || cursor > node.to || !state.field(focusField);
4718
4803
  const url = state.sliceDoc(urlNode.from, urlNode.to);
4719
4804
  if (url.match(/^https?:\/\//) === null && url.match(/^file?:\/\//) === null) {
4720
4805
  return;
@@ -4762,10 +4847,10 @@ var ImageWidget = class extends WidgetType5 {
4762
4847
  };
4763
4848
 
4764
4849
  // src/extensions/markdown/styles.ts
4765
- import { EditorView as EditorView21 } from "@codemirror/view";
4850
+ import { EditorView as EditorView22 } from "@codemirror/view";
4766
4851
  var bulletListIndentationWidth = 24;
4767
4852
  var orderedListIndentationWidth = 36;
4768
- var formattingStyles = EditorView21.theme({
4853
+ var formattingStyles = EditorView22.theme({
4769
4854
  /**
4770
4855
  * Horizontal rule.
4771
4856
  */
@@ -4774,7 +4859,7 @@ var formattingStyles = EditorView21.theme({
4774
4859
  width: "100%",
4775
4860
  height: "0",
4776
4861
  verticalAlign: "middle",
4777
- borderTop: "1px solid var(--dx-cmSeparator)",
4862
+ borderTop: "1px solid var(--color-cm-separator)",
4778
4863
  opacity: 0.5
4779
4864
  },
4780
4865
  /**
@@ -4797,19 +4882,44 @@ var formattingStyles = EditorView21.theme({
4797
4882
  * Blockquote.
4798
4883
  */
4799
4884
  "& .cm-blockquote": {
4800
- background: "var(--dx-cmCodeblock)",
4801
- borderLeft: "2px solid var(--dx-cmSeparator)",
4885
+ background: "var(--color-cm-codeblock)",
4886
+ borderLeft: "2px solid var(--color-cm-separator)",
4802
4887
  paddingLeft: "1rem",
4803
- margin: "0"
4888
+ margin: 0
4804
4889
  },
4805
4890
  /**
4806
4891
  * Code and codeblocks.
4807
4892
  */
4893
+ "& code": {
4894
+ fontFamily: fontMono,
4895
+ color: "var(--color-cm-code)",
4896
+ whiteSpace: "nowrap"
4897
+ },
4808
4898
  "& .cm-code": {
4809
- fontFamily: fontMono
4899
+ fontFamily: fontMono,
4900
+ color: "var(--color-cm-code)"
4810
4901
  },
4811
- "& .cm-codeblock-line": {
4812
- background: "var(--dx-cmCodeblock)",
4902
+ // Inline code spans (triggered by backticks) use `cm-code-inline` + `font-mono`.
4903
+ // Different monospace font metrics can slightly overflow the fixed CodeMirror line box,
4904
+ // so constrain them to the target 24px height.
4905
+ "& .cm-code-inline": {
4906
+ fontFamily: fontMono,
4907
+ height: "24px",
4908
+ // display: 'inline-flex',
4909
+ alignItems: "center",
4910
+ overflow: "hidden",
4911
+ whiteSpace: "nowrap",
4912
+ color: "var(--color-cm-code-inline)"
4913
+ },
4914
+ "& .cm-code-mark": {
4915
+ fontFamily: fontMono,
4916
+ height: "24px",
4917
+ display: "inline-flex",
4918
+ alignItems: "center",
4919
+ overflow: "hidden"
4920
+ },
4921
+ "& .cm-codeblock-line": {
4922
+ background: "var(--color-cm-codeblock)",
4813
4923
  paddingInline: "1rem !important"
4814
4924
  },
4815
4925
  "& .cm-codeblock-start": {
@@ -4838,16 +4948,24 @@ var formattingStyles = EditorView21.theme({
4838
4948
  */
4839
4949
  ".cm-table *": {
4840
4950
  fontFamily: fontMono,
4951
+ lineHeight: 1.5,
4841
4952
  textDecoration: "none !important"
4842
4953
  },
4843
4954
  ".cm-table-head": {
4844
4955
  padding: "2px 16px 2px 0px",
4956
+ overflowWrap: "break-word",
4957
+ whiteSpace: "pre-wrap",
4958
+ wordBreak: "keep-all",
4845
4959
  textAlign: "left",
4846
- borderBottom: "1px solid var(--dx-cmSeparator)",
4847
- color: "var(--dx-subdued)"
4960
+ color: "var(--color-subdued)",
4961
+ borderBottom: "1px solid var(--color-cm-separator)"
4848
4962
  },
4849
4963
  ".cm-table-cell": {
4850
- padding: "2px 16px 2px 0px"
4964
+ padding: "2px 16px 2px 0px",
4965
+ overflowWrap: "break-word",
4966
+ whiteSpace: "pre-wrap",
4967
+ wordBreak: "keep-all",
4968
+ verticalAlign: "top"
4851
4969
  },
4852
4970
  /**
4853
4971
  * Image.
@@ -4863,12 +4981,12 @@ var formattingStyles = EditorView21.theme({
4863
4981
  },
4864
4982
  ".cm-image-with-loader": {
4865
4983
  display: "block",
4866
- opacity: "0",
4984
+ opacity: 0,
4867
4985
  transitionDuration: "350ms",
4868
4986
  transitionProperty: "opacity"
4869
4987
  },
4870
4988
  ".cm-image-with-loader.cm-loaded-image": {
4871
- opacity: "1"
4989
+ opacity: 1
4872
4990
  },
4873
4991
  ".cm-image-wrapper": {
4874
4992
  "grid-template-columns": "1fr",
@@ -4887,17 +5005,17 @@ var formattingStyles = EditorView21.theme({
4887
5005
  // src/extensions/markdown/table.ts
4888
5006
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4889
5007
  import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4890
- import { Decoration as Decoration10, EditorView as EditorView22, WidgetType as WidgetType6 } from "@codemirror/view";
5008
+ import { Decoration as Decoration10, EditorView as EditorView23, WidgetType as WidgetType6 } from "@codemirror/view";
4891
5009
  var table = (options = {}) => {
4892
5010
  return StateField8.define({
4893
5011
  create: (state) => update(state, options),
4894
5012
  update: (_, tr) => update(tr.state, options),
4895
- provide: (field) => EditorView22.decorations.from(field)
5013
+ provide: (field) => EditorView23.decorations.from(field)
4896
5014
  });
4897
5015
  };
4898
5016
  var update = (state, _options) => {
4899
5017
  const builder = new RangeSetBuilder4();
4900
- const cursor2 = state.selection.main.head;
5018
+ const cursor = state.selection.main.head;
4901
5019
  const tables = [];
4902
5020
  const getTable = () => tables[tables.length - 1];
4903
5021
  const getRow = () => {
@@ -4935,7 +5053,7 @@ var update = (state, _options) => {
4935
5053
  }
4936
5054
  });
4937
5055
  tables.forEach((table2) => {
4938
- const replace = state.readOnly || cursor2 < table2.from || cursor2 > table2.to;
5056
+ const replace = state.readOnly || cursor < table2.from || cursor > table2.to;
4939
5057
  if (replace) {
4940
5058
  builder.add(table2.from, table2.to, Decoration10.replace({
4941
5059
  block: true,
@@ -4949,6 +5067,26 @@ var update = (state, _options) => {
4949
5067
  });
4950
5068
  return builder.finish();
4951
5069
  };
5070
+ var renderCellContent = (el, text) => {
5071
+ const parts = text.split(/(`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_)/);
5072
+ for (const part of parts) {
5073
+ if (part.length > 2 && part.startsWith("`") && part.endsWith("`")) {
5074
+ const code = document.createElement("code");
5075
+ code.textContent = part.slice(1, -1);
5076
+ el.appendChild(code);
5077
+ } else if (part.startsWith("**") && part.endsWith("**") || part.startsWith("__") && part.endsWith("__")) {
5078
+ const strong = document.createElement("strong");
5079
+ strong.textContent = part.slice(2, -2);
5080
+ el.appendChild(strong);
5081
+ } else if (part.startsWith("*") && part.endsWith("*") || part.startsWith("_") && part.endsWith("_")) {
5082
+ const em = document.createElement("em");
5083
+ em.textContent = part.slice(1, -1);
5084
+ el.appendChild(em);
5085
+ } else {
5086
+ el.appendChild(document.createTextNode(part));
5087
+ }
5088
+ }
5089
+ };
4952
5090
  var TableWidget = class extends WidgetType6 {
4953
5091
  _table;
4954
5092
  constructor(_table) {
@@ -4968,7 +5106,7 @@ var TableWidget = class extends WidgetType6 {
4968
5106
  this._table.header?.forEach((cell) => {
4969
5107
  const th = document.createElement("th");
4970
5108
  th.setAttribute("class", "cm-table-head");
4971
- tr.appendChild(th).textContent = cell;
5109
+ renderCellContent(tr.appendChild(th), cell);
4972
5110
  });
4973
5111
  const body = table2.appendChild(document.createElement("tbody"));
4974
5112
  this._table.rows?.forEach((row) => {
@@ -4976,7 +5114,7 @@ var TableWidget = class extends WidgetType6 {
4976
5114
  row.forEach((cell) => {
4977
5115
  const td = document.createElement("td");
4978
5116
  td.setAttribute("class", "cm-table-cell");
4979
- tr2.appendChild(td).textContent = cell;
5117
+ renderCellContent(tr2.appendChild(td), cell);
4980
5118
  });
4981
5119
  });
4982
5120
  return div;
@@ -4984,7 +5122,7 @@ var TableWidget = class extends WidgetType6 {
4984
5122
  };
4985
5123
 
4986
5124
  // src/extensions/markdown/decorate.ts
4987
- var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/markdown/decorate.ts";
5125
+ var __dxlog_file12 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/markdown/decorate.ts";
4988
5126
  var Unicode = {
4989
5127
  emDash: "\u2014",
4990
5128
  bullet: "\u2022",
@@ -5007,7 +5145,6 @@ var LinkButton = class extends WidgetType7 {
5007
5145
  eq(other) {
5008
5146
  return this.url === other.url;
5009
5147
  }
5010
- // TODO(burdon): Create icon and link directly without react?
5011
5148
  toDOM(view) {
5012
5149
  const el = document.createElement("span");
5013
5150
  this.render(el, {
@@ -5084,10 +5221,10 @@ var fencedCodeLine = Decoration11.line({
5084
5221
  class: "cm-code cm-codeblock-line"
5085
5222
  });
5086
5223
  var fencedCodeLineFirst = Decoration11.line({
5087
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-start")
5224
+ class: "cm-code cm-codeblock-line cm-codeblock-start"
5088
5225
  });
5089
5226
  var fencedCodeLineLast = Decoration11.line({
5090
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-end")
5227
+ class: "cm-code cm-codeblock-line cm-codeblock-end"
5091
5228
  });
5092
5229
  var commentBlockLine = fencedCodeLine;
5093
5230
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -5119,15 +5256,7 @@ var buildDecorations2 = (view, options, focus2) => {
5119
5256
  const { state } = view;
5120
5257
  const headerLevels = [];
5121
5258
  const getHeaderLevels = (node, level) => {
5122
- invariant4(level > 0, void 0, {
5123
- F: __dxlog_file11,
5124
- L: 180,
5125
- S: void 0,
5126
- A: [
5127
- "level > 0",
5128
- ""
5129
- ]
5130
- });
5259
+ invariant4(level > 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 160, S: void 0, A: ["level > 0", ""] });
5131
5260
  if (level > headerLevels.length) {
5132
5261
  const len = headerLevels.length;
5133
5262
  headerLevels.length = level;
@@ -5158,15 +5287,7 @@ var buildDecorations2 = (view, options, focus2) => {
5158
5287
  listLevels.pop();
5159
5288
  };
5160
5289
  const getCurrentListLevel = () => {
5161
- invariant4(listLevels.length, void 0, {
5162
- F: __dxlog_file11,
5163
- L: 202,
5164
- S: void 0,
5165
- A: [
5166
- "listLevels.length",
5167
- ""
5168
- ]
5169
- });
5290
+ invariant4(listLevels.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 192, S: void 0, A: ["listLevels.length", ""] });
5170
5291
  return listLevels[listLevels.length - 1];
5171
5292
  };
5172
5293
  const enterNode = (node) => {
@@ -5204,13 +5325,13 @@ var buildDecorations2 = (view, options, focus2) => {
5204
5325
  deco: hide
5205
5326
  });
5206
5327
  } else {
5207
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5328
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + "). ";
5208
5329
  if (num.length) {
5209
5330
  atomicDecoRanges.push({
5210
5331
  from: mark.from,
5211
5332
  to: mark.from + len,
5212
5333
  deco: Decoration11.replace({
5213
- widget: new TextWidget(num, markdownTheme.heading(level))
5334
+ widget: new TextWidget(num, markdownTheme.heading(level).className)
5214
5335
  })
5215
5336
  });
5216
5337
  }
@@ -5387,11 +5508,11 @@ var buildDecorations2 = (view, options, focus2) => {
5387
5508
  }
5388
5509
  decoRanges.push({
5389
5510
  from: marks[0].to,
5390
- to: marks[1].from,
5511
+ to: !editing && options.renderLinkButton ? node.to : marks[1].from,
5391
5512
  deco: Decoration11.mark({
5392
5513
  tagName: "a",
5393
5514
  attributes: {
5394
- class: "cm-link",
5515
+ class: options.renderLinkButton ? "cm-link cm-link-with-button" : "cm-link",
5395
5516
  href: url,
5396
5517
  rel: "noreferrer",
5397
5518
  target: "_blank"
@@ -5469,18 +5590,21 @@ var buildDecorations2 = (view, options, focus2) => {
5469
5590
  deco.add(from, to, d);
5470
5591
  }
5471
5592
  const atomicDeco = new RangeSetBuilder5();
5472
- for (const { from, to, deco: d } of atomicDecoRanges) {
5473
- atomicDeco.add(from, to, d);
5593
+ for (const { from, to, deco: deco2 } of atomicDecoRanges) {
5594
+ if (from < to && state.doc.lineAt(from).number !== state.doc.lineAt(to).number) {
5595
+ continue;
5596
+ }
5597
+ atomicDeco.add(from, to, deco2);
5474
5598
  }
5475
5599
  return {
5476
5600
  deco: deco.finish(),
5477
5601
  atomicDeco: atomicDeco.finish()
5478
5602
  };
5479
5603
  };
5480
- var forceUpdate = StateEffect6.define();
5604
+ var forceUpdate = StateEffect7.define();
5481
5605
  var decorateMarkdown = (options = {}) => {
5482
5606
  return [
5483
- ViewPlugin14.fromClass(class {
5607
+ ViewPlugin15.fromClass(class {
5484
5608
  deco;
5485
5609
  atomicDeco;
5486
5610
  pendingUpdate;
@@ -5515,9 +5639,9 @@ var decorateMarkdown = (options = {}) => {
5515
5639
  }
5516
5640
  }, {
5517
5641
  provide: (plugin) => [
5518
- Prec4.low(EditorView23.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5519
- EditorView23.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5520
- EditorView23.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5642
+ Prec4.low(EditorView24.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5643
+ EditorView24.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5644
+ EditorView24.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5521
5645
  ]
5522
5646
  }),
5523
5647
  image(),
@@ -5549,8 +5673,7 @@ var linkTooltip = (renderTooltip) => {
5549
5673
  return {
5550
5674
  pos: link.from,
5551
5675
  end: link.to,
5552
- // NOTE: Forcing above causes the tooltip to flicker.
5553
- // above: true,
5676
+ above: true,
5554
5677
  create: () => {
5555
5678
  const el = document.createElement("div");
5556
5679
  el.className = tooltipContent({});
@@ -5566,16 +5689,13 @@ var linkTooltip = (renderTooltip) => {
5566
5689
  };
5567
5690
  }
5568
5691
  };
5569
- }, {
5570
- // NOTE: 0 = default of 300ms.
5571
- hoverTime: 1
5572
5692
  });
5573
5693
  };
5574
5694
 
5575
5695
  // src/extensions/mention.ts
5576
5696
  import { autocompletion } from "@codemirror/autocomplete";
5577
- import { log as log8 } from "@dxos/log";
5578
- var __dxlog_file12 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/mention.ts";
5697
+ import { log as log9 } from "@dxos/log";
5698
+ var __dxlog_file13 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/mention.ts";
5579
5699
  var mention = ({ debug, onSearch }) => {
5580
5700
  return autocompletion({
5581
5701
  // TODO(burdon): Not working.
@@ -5587,14 +5707,9 @@ var mention = ({ debug, onSearch }) => {
5587
5707
  icons: false,
5588
5708
  override: [
5589
5709
  (context) => {
5590
- log8.info("completion context", {
5710
+ log9.info("completion context", {
5591
5711
  context
5592
- }, {
5593
- F: __dxlog_file12,
5594
- L: 27,
5595
- S: void 0,
5596
- C: (f, a) => f(...a)
5597
- });
5712
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 18, S: void 0 });
5598
5713
  const match = context.matchBefore(/@(\w+)?/);
5599
5714
  if (!match || match.from === match.to && !context.explicit) {
5600
5715
  return null;
@@ -5611,8 +5726,8 @@ var mention = ({ debug, onSearch }) => {
5611
5726
  };
5612
5727
 
5613
5728
  // src/extensions/modal.ts
5614
- import { StateEffect as StateEffect7, StateField as StateField9 } from "@codemirror/state";
5615
- var modalStateEffect = StateEffect7.define();
5729
+ import { StateEffect as StateEffect8, StateField as StateField9 } from "@codemirror/state";
5730
+ var modalStateEffect = StateEffect8.define();
5616
5731
  var modalStateField = StateField9.define({
5617
5732
  create: () => false,
5618
5733
  update: (value, tr) => {
@@ -5673,7 +5788,7 @@ import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5673
5788
  import { StateField as StateField10 } from "@codemirror/state";
5674
5789
  import { Facet as Facet2 } from "@codemirror/state";
5675
5790
  import { invariant as invariant5 } from "@dxos/invariant";
5676
- var __dxlog_file13 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/tree.ts";
5791
+ var __dxlog_file14 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/tree.ts";
5677
5792
  var itemToJSON = ({ type, index, level, lineRange, contentRange, children }) => {
5678
5793
  return {
5679
5794
  type,
@@ -5827,15 +5942,7 @@ var outlinerTree = (_options = {}) => {
5827
5942
  break;
5828
5943
  }
5829
5944
  case "BulletList": {
5830
- invariant5(current, void 0, {
5831
- F: __dxlog_file13,
5832
- L: 219,
5833
- S: void 0,
5834
- A: [
5835
- "current",
5836
- ""
5837
- ]
5838
- });
5945
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 169, S: void 0, A: ["current", ""] });
5839
5946
  parent = current;
5840
5947
  if (current) {
5841
5948
  current.lineRange.to = current.node.from;
@@ -5844,15 +5951,7 @@ var outlinerTree = (_options = {}) => {
5844
5951
  break;
5845
5952
  }
5846
5953
  case "ListItem": {
5847
- invariant5(parent, void 0, {
5848
- F: __dxlog_file13,
5849
- L: 228,
5850
- S: void 0,
5851
- A: [
5852
- "parent",
5853
- ""
5854
- ]
5855
- });
5954
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 179, S: void 0, A: ["parent", ""] });
5856
5955
  const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
5857
5956
  const docRange = {
5858
5957
  from: state.doc.lineAt(node.from).from,
@@ -5886,42 +5985,18 @@ var outlinerTree = (_options = {}) => {
5886
5985
  break;
5887
5986
  }
5888
5987
  case "ListMark": {
5889
- invariant5(current, void 0, {
5890
- F: __dxlog_file13,
5891
- L: 272,
5892
- S: void 0,
5893
- A: [
5894
- "current",
5895
- ""
5896
- ]
5897
- });
5988
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 219, S: void 0, A: ["current", ""] });
5898
5989
  current.type = "bullet";
5899
5990
  current.contentRange.from = node.from + "- ".length;
5900
5991
  break;
5901
5992
  }
5902
5993
  case "Task": {
5903
- invariant5(current, void 0, {
5904
- F: __dxlog_file13,
5905
- L: 278,
5906
- S: void 0,
5907
- A: [
5908
- "current",
5909
- ""
5910
- ]
5911
- });
5994
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 226, S: void 0, A: ["current", ""] });
5912
5995
  current.type = "task";
5913
5996
  break;
5914
5997
  }
5915
5998
  case "TaskMarker": {
5916
- invariant5(current, void 0, {
5917
- F: __dxlog_file13,
5918
- L: 283,
5919
- S: void 0,
5920
- A: [
5921
- "current",
5922
- ""
5923
- ]
5924
- });
5999
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 232, S: void 0, A: ["current", ""] });
5925
6000
  current.contentRange.from = node.from + "[ ] ".length;
5926
6001
  break;
5927
6002
  }
@@ -5929,29 +6004,13 @@ var outlinerTree = (_options = {}) => {
5929
6004
  },
5930
6005
  leave: (node) => {
5931
6006
  if (node.name === "BulletList") {
5932
- invariant5(parent, void 0, {
5933
- F: __dxlog_file13,
5934
- L: 291,
5935
- S: void 0,
5936
- A: [
5937
- "parent",
5938
- ""
5939
- ]
5940
- });
6007
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 240, S: void 0, A: ["parent", ""] });
5941
6008
  prevSiblings[level--] = void 0;
5942
6009
  parent = parent.parent;
5943
6010
  }
5944
6011
  }
5945
6012
  });
5946
- invariant5(tree, void 0, {
5947
- F: __dxlog_file13,
5948
- L: 298,
5949
- S: void 0,
5950
- A: [
5951
- "tree",
5952
- ""
5953
- ]
5954
- });
6013
+ invariant5(tree, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 246, S: void 0, A: ["tree", ""] });
5955
6014
  return tree;
5956
6015
  };
5957
6016
  return [
@@ -6236,17 +6295,17 @@ var commands = () => keymap11.of([
6236
6295
 
6237
6296
  // src/extensions/outliner/outliner.ts
6238
6297
  import { Prec as Prec5 } from "@codemirror/state";
6239
- import { Decoration as Decoration12, EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6240
- import { mx as mx7 } from "@dxos/ui-theme";
6298
+ import { Decoration as Decoration12, EditorView as EditorView26, ViewPlugin as ViewPlugin18 } from "@codemirror/view";
6299
+ import { mx as mx6 } from "@dxos/ui-theme";
6241
6300
 
6242
6301
  // src/extensions/outliner/editor.ts
6243
6302
  import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
6244
- import { ViewPlugin as ViewPlugin15 } from "@codemirror/view";
6245
- import { log as log9 } from "@dxos/log";
6246
- var __dxlog_file14 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6303
+ import { ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6304
+ import { log as log10 } from "@dxos/log";
6305
+ var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6247
6306
  var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
6248
6307
  var initialize = () => {
6249
- return ViewPlugin15.fromClass(class {
6308
+ return ViewPlugin16.fromClass(class {
6250
6309
  constructor(view) {
6251
6310
  const first = view.state.doc.lineAt(0);
6252
6311
  const text = view.state.sliceDoc(first.from, first.to);
@@ -6375,7 +6434,7 @@ var editor = () => [
6375
6434
  cancel = true;
6376
6435
  return;
6377
6436
  }
6378
- log9("change", {
6437
+ log10("change", {
6379
6438
  item,
6380
6439
  line: {
6381
6440
  from: line.from,
@@ -6393,35 +6452,20 @@ var editor = () => [
6393
6452
  text: insert.toString(),
6394
6453
  length: insert.length
6395
6454
  }
6396
- }, {
6397
- F: __dxlog_file14,
6398
- L: 164,
6399
- S: void 0,
6400
- C: (f, a) => f(...a)
6401
- });
6455
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 174, S: void 0 });
6402
6456
  }
6403
6457
  });
6404
6458
  if (changes.length > 0) {
6405
- log9("modified,", {
6459
+ log10("modified,", {
6406
6460
  changes
6407
- }, {
6408
- F: __dxlog_file14,
6409
- L: 175,
6410
- S: void 0,
6411
- C: (f, a) => f(...a)
6412
- });
6461
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 196, S: void 0 });
6413
6462
  return [
6414
6463
  {
6415
6464
  changes
6416
6465
  }
6417
6466
  ];
6418
6467
  } else if (cancel) {
6419
- log9("cancel", void 0, {
6420
- F: __dxlog_file14,
6421
- L: 178,
6422
- S: void 0,
6423
- C: (f, a) => f(...a)
6424
- });
6468
+ log10("cancel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 205, S: void 0 });
6425
6469
  return [];
6426
6470
  }
6427
6471
  return tr;
@@ -6429,10 +6473,10 @@ var editor = () => [
6429
6473
  ];
6430
6474
 
6431
6475
  // src/extensions/outliner/menu.ts
6432
- import { EditorView as EditorView24, ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6476
+ import { EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6433
6477
  import { addEventListener as addEventListener2 } from "@dxos/async";
6434
6478
  var menu = (options = {}) => [
6435
- ViewPlugin16.fromClass(class {
6479
+ ViewPlugin17.fromClass(class {
6436
6480
  view;
6437
6481
  tag;
6438
6482
  rafId;
@@ -6494,7 +6538,7 @@ var menu = (options = {}) => [
6494
6538
  this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
6495
6539
  }
6496
6540
  }),
6497
- EditorView24.theme({
6541
+ EditorView25.theme({
6498
6542
  ".cm-popover-trigger": {
6499
6543
  position: "fixed",
6500
6544
  padding: "0",
@@ -6530,12 +6574,12 @@ var outliner = (_options = {}) => [
6530
6574
  listPaddingLeft: 8
6531
6575
  }),
6532
6576
  // Researve space for menu.
6533
- EditorView25.contentAttributes.of({
6534
- class: "is-full !mr-[3rem]"
6577
+ EditorView26.contentAttributes.of({
6578
+ class: "w-full !mr-[3rem]"
6535
6579
  })
6536
6580
  ];
6537
6581
  var decorations = () => [
6538
- ViewPlugin17.fromClass(class {
6582
+ ViewPlugin18.fromClass(class {
6539
6583
  decorations = Decoration12.none;
6540
6584
  constructor(view) {
6541
6585
  this.updateDecorations(view.state, view);
@@ -6560,7 +6604,7 @@ var decorations = () => [
6560
6604
  const lineTo = doc.lineAt(item.contentRange.to);
6561
6605
  const isSelected = selection.includes(item.index) || item === current;
6562
6606
  decorations2.push(Decoration12.line({
6563
- class: mx7("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6607
+ class: mx6("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6564
6608
  }).range(line.from, line.from));
6565
6609
  }
6566
6610
  }
@@ -6570,7 +6614,7 @@ var decorations = () => [
6570
6614
  decorations: (v) => v.decorations
6571
6615
  }),
6572
6616
  // Theme.
6573
- EditorView25.theme(Object.assign({
6617
+ EditorView26.theme(Object.assign({
6574
6618
  ".cm-list-item": {
6575
6619
  borderLeftWidth: "1px",
6576
6620
  borderRightWidth: "1px",
@@ -6595,38 +6639,67 @@ var decorations = () => [
6595
6639
  marginBottom: "2px"
6596
6640
  },
6597
6641
  ".cm-list-item-focused": {
6598
- borderColor: "var(--dx-neutralFocusIndicator)"
6642
+ borderColor: "var(--color-neutral-focus-indicator)"
6599
6643
  },
6600
6644
  "&:focus-within .cm-list-item-selected": {
6601
- borderColor: "var(--dx-separator)"
6645
+ borderColor: "var(--color-separator)"
6602
6646
  }
6603
6647
  }))
6604
6648
  ];
6605
6649
 
6606
6650
  // src/extensions/preview/preview.ts
6607
6651
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
6608
- import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField11 } from "@codemirror/state";
6609
- import { Decoration as Decoration13, EditorView as EditorView26, WidgetType as WidgetType8 } from "@codemirror/view";
6652
+ import { RangeSetBuilder as RangeSetBuilder6, StateEffect as StateEffect9, StateField as StateField11 } from "@codemirror/state";
6653
+ import { Decoration as Decoration13, EditorView as EditorView27, ViewPlugin as ViewPlugin19, WidgetType as WidgetType8 } from "@codemirror/view";
6654
+ import { DXN, Entity } from "@dxos/echo";
6655
+ var labelResolvedEffect = StateEffect9.define();
6610
6656
  var preview = (options = {}) => {
6657
+ const viewRef = {
6658
+ current: void 0
6659
+ };
6611
6660
  return [
6612
6661
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
6613
6662
  // "Block decorations may not be specified via plugins".
6614
6663
  StateField11.define({
6615
- create: (state) => buildDecorations3(state, options),
6664
+ create: (state) => buildDecorations3(state, options, viewRef),
6616
6665
  update: (decorations2, tr) => {
6617
- if (tr.docChanged) {
6618
- return buildDecorations3(tr.state, options);
6666
+ if (tr.docChanged || tr.effects.some((effect) => effect.is(labelResolvedEffect))) {
6667
+ return buildDecorations3(tr.state, options, viewRef);
6619
6668
  }
6620
6669
  return decorations2.map(tr.changes);
6621
6670
  },
6622
6671
  provide: (field) => [
6623
- EditorView26.decorations.from(field),
6624
- EditorView26.atomicRanges.of((view) => view.state.field(field))
6672
+ EditorView27.decorations.from(field),
6673
+ EditorView27.atomicRanges.of((view) => view.state.field(field))
6625
6674
  ]
6675
+ }),
6676
+ ViewPlugin19.define((view) => {
6677
+ viewRef.current = view;
6678
+ return {
6679
+ destroy() {
6680
+ viewRef.current = void 0;
6681
+ }
6682
+ };
6626
6683
  })
6627
6684
  ];
6628
6685
  };
6629
- var buildDecorations3 = (state, options) => {
6686
+ var resolveLabel = (db, dxnStr, viewRef) => {
6687
+ const dxn = DXN.tryParse(dxnStr);
6688
+ if (!dxn) {
6689
+ return;
6690
+ }
6691
+ const ref = db.makeRef(dxn);
6692
+ const target = ref.target;
6693
+ if (target) {
6694
+ return Entity.getLabel(target);
6695
+ }
6696
+ void ref.tryLoad().then(() => {
6697
+ viewRef.current?.dispatch({
6698
+ effects: labelResolvedEffect.of(void 0)
6699
+ });
6700
+ });
6701
+ };
6702
+ var buildDecorations3 = (state, options, viewRef) => {
6630
6703
  const builder = new RangeSetBuilder6();
6631
6704
  syntaxTree10(state).iterate({
6632
6705
  enter: (node) => {
@@ -6638,8 +6711,13 @@ var buildDecorations3 = (state, options) => {
6638
6711
  case "Link": {
6639
6712
  const link = getLinkRef(state, node.node);
6640
6713
  if (link) {
6714
+ const resolved = options.db ? resolveLabel(options.db, link.dxn, viewRef) : void 0;
6715
+ const displayLink = resolved ? {
6716
+ ...link,
6717
+ label: resolved
6718
+ } : link;
6641
6719
  builder.add(node.from, node.to, Decoration13.replace({
6642
- widget: new PreviewInlineWidget(options, link),
6720
+ widget: new PreviewInlineWidget(options, displayLink),
6643
6721
  side: 1
6644
6722
  }));
6645
6723
  }
@@ -6715,7 +6793,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6715
6793
  }
6716
6794
  toDOM(_view) {
6717
6795
  const root = document.createElement("div");
6718
- root.classList.add("cm-preview-block", "density-fine");
6796
+ root.classList.add("cm-preview-block", "dx-density-fine");
6719
6797
  this._options.addBlockContainer?.({
6720
6798
  link: this._link,
6721
6799
  el: root
@@ -6731,7 +6809,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6731
6809
  };
6732
6810
 
6733
6811
  // src/extensions/replacer.ts
6734
- import { EditorView as EditorView27 } from "@codemirror/view";
6812
+ import { EditorView as EditorView28 } from "@codemirror/view";
6735
6813
  var defaultReplacements = [
6736
6814
  {
6737
6815
  input: "--",
@@ -6794,7 +6872,7 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6794
6872
  const sortedReplacements = [
6795
6873
  ...replacements
6796
6874
  ].sort((a, b) => b.input.length - a.input.length);
6797
- return EditorView27.inputHandler.of((view, from, to, insert) => {
6875
+ return EditorView28.inputHandler.of((view, from, to, insert) => {
6798
6876
  if (insert.length !== 1) {
6799
6877
  return false;
6800
6878
  }
@@ -6828,12 +6906,69 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6828
6906
  });
6829
6907
  };
6830
6908
 
6909
+ // src/extensions/snippets.ts
6910
+ import { keymap as keymap12 } from "@codemirror/view";
6911
+ var defaultItems = [
6912
+ "hello world!",
6913
+ "this is a test.",
6914
+ "this is [DXOS](https://dxos.org)"
6915
+ ];
6916
+ var snippets2 = ({ delay = 75, items = defaultItems } = {}) => {
6917
+ let timer;
6918
+ let index = 0;
6919
+ return [
6920
+ keymap12.of([
6921
+ {
6922
+ // Reset.
6923
+ key: "alt-meta-'",
6924
+ run: () => {
6925
+ clearTimeout(timer);
6926
+ index = 0;
6927
+ return true;
6928
+ }
6929
+ },
6930
+ {
6931
+ // Next snippet.
6932
+ // TODO(burdon): Press 1-9 to select snippet?
6933
+ key: "Shift-Meta-'",
6934
+ run: (view) => {
6935
+ clearTimeout(timer);
6936
+ const text = items[index++];
6937
+ if (index === items?.length) {
6938
+ index = 0;
6939
+ }
6940
+ let offset = 0;
6941
+ const insert = (delayMs = 0) => {
6942
+ timer = setTimeout(() => {
6943
+ const pos = view.state.selection.main.head;
6944
+ view.dispatch({
6945
+ changes: {
6946
+ from: pos,
6947
+ insert: text[offset++]
6948
+ },
6949
+ selection: {
6950
+ anchor: pos + 1
6951
+ }
6952
+ });
6953
+ if (offset < text.length) {
6954
+ insert(Math.random() * delay * (text[offset] === " " ? 2 : 1));
6955
+ }
6956
+ }, delayMs);
6957
+ };
6958
+ insert();
6959
+ return true;
6960
+ }
6961
+ }
6962
+ ])
6963
+ ];
6964
+ };
6965
+
6831
6966
  // src/extensions/submit.ts
6832
6967
  import { Prec as Prec6 } from "@codemirror/state";
6833
- import { keymap as keymap12 } from "@codemirror/view";
6968
+ import { keymap as keymap13 } from "@codemirror/view";
6834
6969
  var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6835
6970
  return [
6836
- Prec6.highest(keymap12.of([
6971
+ Prec6.highest(keymap13.of([
6837
6972
  {
6838
6973
  key: "Enter",
6839
6974
  preventDefault: true,
@@ -6878,6 +7013,7 @@ var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6878
7013
  // src/extensions/tags/extended-markdown.ts
6879
7014
  import { xmlLanguage } from "@codemirror/lang-xml";
6880
7015
  import { parseMixed } from "@lezer/common";
7016
+ var escapeRegExpSource = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6881
7017
  var extendedMarkdown = ({ registry } = {}) => {
6882
7018
  return [
6883
7019
  createMarkdownExtensions({
@@ -6889,13 +7025,65 @@ var extendedMarkdown = ({ registry } = {}) => {
6889
7025
  {
6890
7026
  name: "SetextHeading",
6891
7027
  parse: () => false
6892
- }
7028
+ },
7029
+ // Custom XML block parser that keeps registered tags as a single HTMLBlock
7030
+ // even when their content contains blank lines.
7031
+ ...xmlBlockParsers(registry)
6893
7032
  ]
6894
7033
  }
6895
7034
  ]
6896
7035
  })
6897
7036
  ];
6898
7037
  };
7038
+ var xmlBlockParsers = (registry) => {
7039
+ const customTags = Object.keys(registry ?? {});
7040
+ if (customTags.length === 0) {
7041
+ return [];
7042
+ }
7043
+ const tagPattern = customTags.map(escapeRegExpSource).join("|");
7044
+ const selfClosePattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/>\\s*$`);
7045
+ const openPattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/?>`);
7046
+ return [
7047
+ {
7048
+ name: "XMLBlock",
7049
+ before: "HTMLBlock",
7050
+ parse: (cx, line) => {
7051
+ const match = openPattern.exec(line.text);
7052
+ if (!match) {
7053
+ return false;
7054
+ }
7055
+ if (selfClosePattern.test(line.text)) {
7056
+ const end2 = cx.lineStart + line.text.length;
7057
+ cx.addElement(cx.elt("HTMLBlock", cx.lineStart, end2));
7058
+ cx.nextLine();
7059
+ return true;
7060
+ }
7061
+ if (match[0].trimEnd().endsWith("/>")) {
7062
+ return false;
7063
+ }
7064
+ const tagName = match[1];
7065
+ const closeTag = `</${tagName}>`;
7066
+ const start = cx.lineStart;
7067
+ if (line.text.includes(closeTag)) {
7068
+ cx.addElement(cx.elt("HTMLBlock", start, start + line.text.length));
7069
+ cx.nextLine();
7070
+ return true;
7071
+ }
7072
+ let end = cx.lineStart + line.text.length;
7073
+ while (cx.nextLine()) {
7074
+ end = cx.lineStart + line.text.length;
7075
+ if (line.text.includes(closeTag)) {
7076
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7077
+ cx.nextLine();
7078
+ return true;
7079
+ }
7080
+ }
7081
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7082
+ return true;
7083
+ }
7084
+ }
7085
+ ];
7086
+ };
6899
7087
  var mixedParser = (registry) => {
6900
7088
  const customTags = Object.keys(registry ?? {});
6901
7089
  const tagPattern = new RegExp(`<(${customTags.join("|")})`);
@@ -6929,219 +7117,793 @@ var mixedParser = (registry) => {
6929
7117
  });
6930
7118
  };
6931
7119
 
6932
- // src/extensions/tags/streamer.ts
6933
- import { StateEffect as StateEffect8, StateField as StateField12 } from "@codemirror/state";
6934
- import { Decoration as Decoration14, EditorView as EditorView28, ViewPlugin as ViewPlugin18, WidgetType as WidgetType9 } from "@codemirror/view";
6935
- import { Domino as Domino3 } from "@dxos/ui";
6936
- import { isTruthy as isTruthy4 } from "@dxos/util";
6937
- var BLINK_RATE = 2e3;
6938
- var streamer = (options = {}) => {
7120
+ // src/extensions/tags/fader.ts
7121
+ import { StateEffect as StateEffect10, StateField as StateField12 } from "@codemirror/state";
7122
+ import { Decoration as Decoration14, EditorView as EditorView29, ViewPlugin as ViewPlugin20 } from "@codemirror/view";
7123
+ var DEFAULT_REMOVAL_DELAY = 5e3;
7124
+ var DEFAULT_COALESCE_WINDOW = 100;
7125
+ var CLEANUP_INTERVAL = 1e3;
7126
+ var fader = (options = {}) => {
7127
+ const removalDelay = DEFAULT_REMOVAL_DELAY;
7128
+ const coalesceWindow = options.coalesce ?? DEFAULT_COALESCE_WINDOW;
7129
+ let lastCount = -1;
7130
+ const log12 = (expiries) => {
7131
+ if (expiries.length !== lastCount) {
7132
+ lastCount = expiries.length;
7133
+ }
7134
+ };
7135
+ const dequeue = StateEffect10.define();
7136
+ const fadeField = StateField12.define({
7137
+ create: () => ({
7138
+ decorations: Decoration14.none,
7139
+ expiries: [],
7140
+ batchStart: 0
7141
+ }),
7142
+ update: ({ decorations: decorations2, expiries, batchStart }, tr) => {
7143
+ for (const effect of tr.effects) {
7144
+ if (effect.is(dequeue)) {
7145
+ const now2 = effect.value;
7146
+ let removeCount = 0;
7147
+ while (removeCount < expiries.length && expiries[removeCount] <= now2) {
7148
+ removeCount++;
7149
+ }
7150
+ if (removeCount > 0) {
7151
+ expiries = expiries.slice(removeCount);
7152
+ let skipped = 0;
7153
+ decorations2 = decorations2.update({
7154
+ filter: () => {
7155
+ if (skipped < removeCount) {
7156
+ skipped++;
7157
+ return false;
7158
+ }
7159
+ return true;
7160
+ }
7161
+ });
7162
+ }
7163
+ }
7164
+ }
7165
+ if (!tr.docChanged) {
7166
+ log12(expiries);
7167
+ return {
7168
+ decorations: decorations2,
7169
+ expiries,
7170
+ batchStart
7171
+ };
7172
+ }
7173
+ let isReset = tr.state.doc.length === 0;
7174
+ if (!isReset && tr.startState.doc.length > 0) {
7175
+ tr.changes.iterChanges((fromA, toA) => {
7176
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7177
+ isReset = true;
7178
+ }
7179
+ });
7180
+ }
7181
+ if (isReset) {
7182
+ log12([]);
7183
+ return {
7184
+ decorations: Decoration14.none,
7185
+ expiries: [],
7186
+ batchStart: 0
7187
+ };
7188
+ }
7189
+ const now = Date.now();
7190
+ const add = [];
7191
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7192
+ if (toA === tr.startState.doc.length && inserted.length > 0) {
7193
+ add.push({
7194
+ from: fromB,
7195
+ to: toB
7196
+ });
7197
+ }
7198
+ });
7199
+ if (add.length > 0) {
7200
+ const canCoalesce = expiries.length > 0 && batchStart > 0 && now - batchStart < coalesceWindow;
7201
+ if (canCoalesce) {
7202
+ let lastFrom = -1;
7203
+ let lastTo = -1;
7204
+ decorations2.between(0, tr.state.doc.length, (from, to) => {
7205
+ lastFrom = from;
7206
+ lastTo = to;
7207
+ });
7208
+ if (lastFrom >= 0) {
7209
+ decorations2 = decorations2.update({
7210
+ filter: (from, to) => !(from === lastFrom && to === lastTo)
7211
+ });
7212
+ const mergedFrom = Math.min(lastFrom, add[0].from);
7213
+ const mergedTo = add[add.length - 1].to;
7214
+ decorations2 = decorations2.update({
7215
+ add: [
7216
+ Decoration14.mark({
7217
+ class: "cm-fader"
7218
+ }).range(mergedFrom, mergedTo)
7219
+ ]
7220
+ });
7221
+ expiries = [
7222
+ ...expiries.slice(0, -1),
7223
+ now + removalDelay
7224
+ ];
7225
+ }
7226
+ } else {
7227
+ batchStart = now;
7228
+ expiries = [
7229
+ ...expiries,
7230
+ now + removalDelay
7231
+ ];
7232
+ decorations2 = decorations2.update({
7233
+ add: add.map(({ from, to }) => Decoration14.mark({
7234
+ class: "cm-fader"
7235
+ }).range(from, to))
7236
+ });
7237
+ }
7238
+ }
7239
+ log12(expiries);
7240
+ return {
7241
+ decorations: decorations2,
7242
+ expiries,
7243
+ batchStart
7244
+ };
7245
+ },
7246
+ provide: (f) => EditorView29.decorations.from(f, (value) => value.decorations)
7247
+ });
7248
+ const cleanup = ViewPlugin20.fromClass(class {
7249
+ view;
7250
+ #timer;
7251
+ constructor(view) {
7252
+ this.view = view;
7253
+ this.#schedule();
7254
+ }
7255
+ update() {
7256
+ this.#schedule();
7257
+ }
7258
+ #schedule() {
7259
+ const { expiries } = this.view.state.field(fadeField);
7260
+ if (expiries.length === 0) {
7261
+ clearTimeout(this.#timer);
7262
+ this.#timer = void 0;
7263
+ return;
7264
+ }
7265
+ if (this.#timer !== void 0) {
7266
+ return;
7267
+ }
7268
+ const delay = Math.max(CLEANUP_INTERVAL, expiries[0] - Date.now());
7269
+ this.#timer = setTimeout(() => {
7270
+ this.#timer = void 0;
7271
+ this.view.dispatch({
7272
+ effects: dequeue.of(Date.now())
7273
+ });
7274
+ }, delay);
7275
+ }
7276
+ destroy() {
7277
+ clearTimeout(this.#timer);
7278
+ }
7279
+ });
6939
7280
  return [
6940
- options.cursor && cursor(),
6941
- options.fadeIn && fadeIn(typeof options.fadeIn === "object" ? options.fadeIn : {})
6942
- ].filter(isTruthy4);
6943
- };
6944
- var cursor = () => {
6945
- const hideCursor = StateEffect8.define();
6946
- const showCursor = StateField12.define({
6947
- create: () => true,
7281
+ fadeField,
7282
+ cleanup,
7283
+ EditorView29.theme({
7284
+ ".cm-fader": {
7285
+ animation: "fader 1s ease-out forwards"
7286
+ },
7287
+ "@keyframes fader": {
7288
+ "0%": {
7289
+ textShadow: "0 0 16px rgba(100, 200, 255, 1), 0 0 32px rgba(100, 200, 255, 0.6)"
7290
+ },
7291
+ "100%": {}
7292
+ }
7293
+ })
7294
+ ];
7295
+ };
7296
+
7297
+ // src/extensions/tags/typewriter.ts
7298
+ import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7299
+ import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7300
+ import { Domino as Domino3 } from "@dxos/ui";
7301
+ var typewriterBypass = Annotation3.define();
7302
+ var typewriterDrainingEffect = StateEffect11.define();
7303
+ var CURSOR_LINGER = 3e3;
7304
+ var FRAME_BUDGET_MS = 4;
7305
+ var CHARS_PER_FRAME = 5;
7306
+ var FLUSH_THRESHOLD = 2e3;
7307
+ var COMPACT_HEAD_THRESHOLD = 4096;
7308
+ var typewriter = (options = {}) => {
7309
+ const streamingTags = options.streamingTags ?? /* @__PURE__ */ new Set();
7310
+ const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;
7311
+ const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;
7312
+ const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;
7313
+ const suppressAppend = StateEffect11.define();
7314
+ const insertChunk = StateEffect11.define();
7315
+ const bufferField = StateField13.define({
7316
+ create: () => ({
7317
+ text: "",
7318
+ head: 0,
7319
+ insertAt: 0
7320
+ }),
6948
7321
  update: (value, tr) => {
7322
+ let { text, head, insertAt } = value;
6949
7323
  for (const effect of tr.effects) {
6950
- if (effect.is(hideCursor)) {
6951
- return false;
7324
+ if (effect.is(suppressAppend)) {
7325
+ if (text.length === head) {
7326
+ insertAt = effect.value.from;
7327
+ }
7328
+ text += effect.value.text;
7329
+ }
7330
+ if (effect.is(insertChunk)) {
7331
+ head += effect.value.text.length;
7332
+ insertAt = effect.value.from + effect.value.text.length;
7333
+ if (head >= COMPACT_HEAD_THRESHOLD || head > 0 && head * 2 >= text.length) {
7334
+ text = text.slice(head);
7335
+ head = 0;
7336
+ }
6952
7337
  }
6953
7338
  }
6954
7339
  if (tr.docChanged) {
6955
- return true;
7340
+ let isReset = tr.state.doc.length === 0;
7341
+ if (!isReset && tr.startState.doc.length > 0) {
7342
+ tr.changes.iterChanges((fromA, toA) => {
7343
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7344
+ isReset = true;
7345
+ }
7346
+ });
7347
+ }
7348
+ if (isReset) {
7349
+ return {
7350
+ text: "",
7351
+ head: 0,
7352
+ insertAt: 0
7353
+ };
7354
+ }
7355
+ if (!tr.effects.some((effect) => effect.is(insertChunk))) {
7356
+ insertAt = tr.changes.mapPos(Math.min(insertAt, tr.startState.doc.length));
7357
+ }
6956
7358
  }
6957
- return value;
7359
+ return {
7360
+ text,
7361
+ head,
7362
+ insertAt
7363
+ };
7364
+ }
7365
+ });
7366
+ const filter = EditorState3.transactionFilter.of((tr) => {
7367
+ if (!tr.docChanged) {
7368
+ return tr;
7369
+ }
7370
+ if (tr.annotation(typewriterBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7371
+ return tr;
7372
+ }
7373
+ let appendedText = "";
7374
+ let appendFrom = -1;
7375
+ let isAppendOnly = true;
7376
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
7377
+ if (toA === tr.startState.doc.length && fromA === toA && inserted.length > 0) {
7378
+ appendedText += inserted.sliceString(0);
7379
+ if (appendFrom === -1) {
7380
+ appendFrom = fromA;
7381
+ }
7382
+ } else {
7383
+ isAppendOnly = false;
7384
+ }
7385
+ });
7386
+ if (!isAppendOnly || appendedText.length === 0) {
7387
+ return tr;
6958
7388
  }
7389
+ return {
7390
+ changes: ChangeSet2.empty(tr.startState.doc.length),
7391
+ effects: suppressAppend.of({
7392
+ from: appendFrom,
7393
+ text: appendedText
7394
+ })
7395
+ };
6959
7396
  });
6960
- const timerPlugin = ViewPlugin18.fromClass(class {
7397
+ const drainPlugin = ViewPlugin21.fromClass(class {
6961
7398
  view;
6962
- timer;
7399
+ _raf;
7400
+ _activeStreamTag = null;
6963
7401
  constructor(view) {
6964
7402
  this.view = view;
6965
7403
  }
6966
7404
  update(update2) {
6967
- if (update2.docChanged) {
6968
- clearTimeout(this.timer);
6969
- this.timer = setTimeout(() => {
6970
- this.view.dispatch({
6971
- effects: hideCursor.of(null)
6972
- });
6973
- }, BLINK_RATE);
7405
+ const { text, head } = update2.state.field(bufferField);
7406
+ const pending = text.length - head;
7407
+ if (pending === 0) {
7408
+ this._activeStreamTag = null;
7409
+ }
7410
+ if (pending > 0 && this._raf === void 0) {
7411
+ this._start();
6974
7412
  }
6975
7413
  }
7414
+ _start() {
7415
+ queueMicrotask(() => {
7416
+ this.view.dispatch({
7417
+ effects: typewriterDrainingEffect.of(true),
7418
+ annotations: typewriterBypass.of(true)
7419
+ });
7420
+ });
7421
+ this._raf = requestAnimationFrame(this._tick);
7422
+ }
7423
+ _tick = () => {
7424
+ const { text, head, insertAt } = this.view.state.field(bufferField);
7425
+ const pending = text.length - head;
7426
+ if (pending === 0) {
7427
+ this.view.dispatch({
7428
+ effects: typewriterDrainingEffect.of(false),
7429
+ annotations: typewriterBypass.of(true)
7430
+ });
7431
+ this._raf = void 0;
7432
+ return;
7433
+ }
7434
+ if (pending > flushThreshold) {
7435
+ const chunk = text.slice(head);
7436
+ this._activeStreamTag = null;
7437
+ this.view.dispatch({
7438
+ changes: {
7439
+ from: insertAt,
7440
+ insert: chunk
7441
+ },
7442
+ effects: insertChunk.of({
7443
+ from: insertAt,
7444
+ text: chunk
7445
+ })
7446
+ });
7447
+ this._raf = requestAnimationFrame(this._tick);
7448
+ return;
7449
+ }
7450
+ const startTime = performance.now();
7451
+ let pos = head;
7452
+ let activeTag = this._activeStreamTag;
7453
+ let charsEmitted = 0;
7454
+ while (pos < text.length && performance.now() - startTime < frameBudgetMs) {
7455
+ const result = flushable(text, pos, streamingTags, activeTag);
7456
+ if (result.count === 0) {
7457
+ break;
7458
+ }
7459
+ if (charsEmitted > 0 && charsEmitted + result.count > charsPerFrame) {
7460
+ break;
7461
+ }
7462
+ if (result.enterTag) {
7463
+ activeTag = result.enterTag;
7464
+ }
7465
+ if (result.exitTag) {
7466
+ activeTag = null;
7467
+ }
7468
+ pos += result.count;
7469
+ charsEmitted += result.count;
7470
+ }
7471
+ const totalCount = pos - head;
7472
+ if (totalCount > 0) {
7473
+ const chunk = text.slice(head, head + totalCount);
7474
+ this._activeStreamTag = activeTag;
7475
+ this.view.dispatch({
7476
+ changes: {
7477
+ from: insertAt,
7478
+ insert: chunk
7479
+ },
7480
+ effects: insertChunk.of({
7481
+ from: insertAt,
7482
+ text: chunk
7483
+ })
7484
+ });
7485
+ }
7486
+ this._raf = requestAnimationFrame(this._tick);
7487
+ };
6976
7488
  destroy() {
6977
- clearTimeout(this.timer);
7489
+ if (this._raf !== void 0) {
7490
+ cancelAnimationFrame(this._raf);
7491
+ }
6978
7492
  }
6979
7493
  });
6980
- const cursorDecoration = StateField12.define({
6981
- create: () => Decoration14.none,
6982
- update: (_decorations, tr) => {
6983
- const show = tr.state.field(showCursor);
6984
- if (!show) {
6985
- return Decoration14.none;
7494
+ return [
7495
+ bufferField,
7496
+ filter,
7497
+ drainPlugin,
7498
+ options.cursor && typewriterCursor(bufferField)
7499
+ ].filter(Boolean);
7500
+ };
7501
+ var typewriterCursor = (bufferField) => {
7502
+ const hideCursor = StateEffect11.define();
7503
+ const visibilityField = StateField13.define({
7504
+ create: () => ({
7505
+ visible: false,
7506
+ insertAt: 0,
7507
+ lastNonWsAt: 0
7508
+ }),
7509
+ update: (value, tr) => {
7510
+ const { text, head, insertAt } = tr.state.field(bufferField);
7511
+ const pending = text.length - head;
7512
+ if (pending > 0) {
7513
+ let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));
7514
+ if (tr.docChanged) {
7515
+ tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
7516
+ const chunk = inserted.sliceString(0);
7517
+ if (chunk.trim().length > 0) {
7518
+ lastNonWsAt = _fromB + chunk.length;
7519
+ }
7520
+ });
7521
+ }
7522
+ return {
7523
+ visible: true,
7524
+ insertAt,
7525
+ lastNonWsAt
7526
+ };
6986
7527
  }
6987
- const endPos = tr.state.doc.length;
6988
- return Decoration14.set([
6989
- Decoration14.widget({
7528
+ for (const effect of tr.effects) {
7529
+ if (effect.is(hideCursor)) {
7530
+ return {
7531
+ ...value,
7532
+ visible: false
7533
+ };
7534
+ }
7535
+ }
7536
+ return value;
7537
+ }
7538
+ });
7539
+ const decorationField = StateField13.define({
7540
+ create: () => Decoration15.none,
7541
+ update: (_decorations, tr) => {
7542
+ const { visible, insertAt, lastNonWsAt } = tr.state.field(visibilityField);
7543
+ if (!visible) {
7544
+ return Decoration15.none;
7545
+ }
7546
+ const { text, head } = tr.state.field(bufferField);
7547
+ const cursorAt = text.length > head ? insertAt : lastNonWsAt;
7548
+ const pos = Math.min(cursorAt, tr.state.doc.length);
7549
+ return Decoration15.set([
7550
+ Decoration15.widget({
6990
7551
  widget: new CursorWidget(),
6991
7552
  side: 1
6992
- }).range(endPos)
7553
+ }).range(pos)
6993
7554
  ]);
6994
7555
  },
6995
- provide: (f) => EditorView28.decorations.from(f)
7556
+ provide: (field) => EditorView30.decorations.from(field)
7557
+ });
7558
+ const timerPlugin = ViewPlugin21.fromClass(class {
7559
+ view;
7560
+ _timer;
7561
+ constructor(view) {
7562
+ this.view = view;
7563
+ }
7564
+ update(update2) {
7565
+ const { text, head } = update2.state.field(bufferField);
7566
+ const { visible } = update2.state.field(visibilityField);
7567
+ const pending = text.length - head;
7568
+ if (pending > 0) {
7569
+ clearTimeout(this._timer);
7570
+ this._timer = void 0;
7571
+ } else if (visible && this._timer === void 0) {
7572
+ this._timer = setTimeout(() => {
7573
+ this.view.dispatch({
7574
+ effects: hideCursor.of(null)
7575
+ });
7576
+ this._timer = void 0;
7577
+ }, CURSOR_LINGER);
7578
+ }
7579
+ }
7580
+ destroy() {
7581
+ clearTimeout(this._timer);
7582
+ }
6996
7583
  });
6997
7584
  return [
6998
- showCursor,
6999
- timerPlugin,
7000
- cursorDecoration
7585
+ visibilityField,
7586
+ decorationField,
7587
+ timerPlugin
7001
7588
  ];
7002
7589
  };
7003
- var CursorWidget = class extends WidgetType9 {
7590
+ var CursorWidget = class _CursorWidget extends WidgetType9 {
7591
+ // All instances are interchangeable — let CM reuse the existing DOM across drips so
7592
+ // the blink animation isn't restarted on every transaction.
7593
+ eq(other) {
7594
+ return other instanceof _CursorWidget;
7595
+ }
7004
7596
  toDOM() {
7005
- const inner = Domino3.of("span").text("\u258F").style({
7006
- animation: "blink 2s infinite"
7597
+ const inner = Domino3.of("span").text("\u2217").style({
7598
+ animation: "blink 1s infinite",
7599
+ animationDelay: "250ms"
7007
7600
  });
7008
7601
  return Domino3.of("span").style({
7009
7602
  opacity: "0.8"
7010
- }).children(inner).root;
7603
+ }).append(inner).root;
7011
7604
  }
7012
7605
  };
7013
- var fadeIn = (options = {}) => {
7014
- const FADE_IN_DURATION = 1e3;
7015
- const DEFAULT_REMOVAL_DELAY = 5e3;
7016
- const removalDelay = options.removalDelay ?? DEFAULT_REMOVAL_DELAY;
7017
- const removeDecoration = StateEffect8.define();
7018
- const fadeField = StateField12.define({
7019
- create: () => Decoration14.none,
7020
- update: (decorations2, tr) => {
7021
- let next = decorations2;
7022
- for (const effect of tr.effects) {
7023
- if (effect.is(removeDecoration)) {
7024
- const target = effect.value;
7025
- next = next.update({
7026
- filter: (from, to) => !(from === target.from && to === target.to)
7027
- });
7028
- }
7029
- }
7030
- if (!tr.docChanged) {
7031
- return next;
7606
+ var OPENING_TAG_NAME = /^<([a-zA-Z][\w-]*)/;
7607
+ var TAG_NAME_PROBE = 64;
7608
+ var escapeRegExpSource2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7609
+ var flushable = (buffer, start, streamingTags, activeStreamTag) => {
7610
+ if (start >= buffer.length) {
7611
+ return {
7612
+ count: 0
7613
+ };
7614
+ }
7615
+ if (activeStreamTag) {
7616
+ const closeTag = `</${activeStreamTag}>`;
7617
+ if (buffer.startsWith(closeTag, start)) {
7618
+ return {
7619
+ count: closeTag.length,
7620
+ exitTag: true
7621
+ };
7622
+ }
7623
+ if (buffer[start] === "<") {
7624
+ return {
7625
+ count: xmlElementLength(buffer, start)
7626
+ };
7627
+ }
7628
+ return {
7629
+ count: 1
7630
+ };
7631
+ }
7632
+ const ch = buffer[start];
7633
+ if (ch === "<") {
7634
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7635
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7636
+ if (nameMatch && streamingTags.has(nameMatch[1])) {
7637
+ const close = buffer.indexOf(">", start);
7638
+ if (close === -1) {
7639
+ return {
7640
+ count: 0
7641
+ };
7032
7642
  }
7033
- let isReset = tr.state.doc.length === 0;
7034
- if (!isReset) {
7035
- tr.changes.iterChanges((fromA, toA) => {
7036
- if (fromA === 0 && toA === tr.startState.doc.length) {
7037
- isReset = true;
7038
- }
7039
- });
7643
+ if (buffer[close - 1] === "/") {
7644
+ return {
7645
+ count: close + 1 - start
7646
+ };
7040
7647
  }
7041
- if (isReset) {
7042
- return Decoration14.none;
7648
+ return {
7649
+ count: close + 1 - start,
7650
+ enterTag: nameMatch[1]
7651
+ };
7652
+ }
7653
+ return {
7654
+ count: xmlElementLength(buffer, start)
7655
+ };
7656
+ }
7657
+ if (ch === "!" && buffer.length > start + 1 && buffer[start + 1] === "[") {
7658
+ return {
7659
+ count: linkLength(buffer, start, start + 1)
7660
+ };
7661
+ }
7662
+ if (ch === "[") {
7663
+ return {
7664
+ count: linkLength(buffer, start, start)
7665
+ };
7666
+ }
7667
+ return {
7668
+ count: 1
7669
+ };
7670
+ };
7671
+ var xmlElementLength = (buffer, start = 0) => {
7672
+ const close = buffer.indexOf(">", start);
7673
+ if (close === -1) {
7674
+ return 0;
7675
+ }
7676
+ if (buffer[close - 1] === "/") {
7677
+ return close + 1 - start;
7678
+ }
7679
+ if (buffer[start + 1] === "/") {
7680
+ return close + 1 - start;
7681
+ }
7682
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7683
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7684
+ if (!nameMatch) {
7685
+ return 1;
7686
+ }
7687
+ const tagName = nameMatch[1];
7688
+ let depth = 0;
7689
+ const tagPattern = new RegExp(`<(/?)${escapeRegExpSource2(tagName)}(\\s[^>]*)?>`, "g");
7690
+ tagPattern.lastIndex = start;
7691
+ let match;
7692
+ while ((match = tagPattern.exec(buffer)) !== null) {
7693
+ const isSelfClosing = match[0].endsWith("/>");
7694
+ const isClosing = match[1] === "/";
7695
+ if (isSelfClosing) {
7696
+ if (depth === 0) {
7697
+ return match.index + match[0].length - start;
7698
+ }
7699
+ } else if (isClosing) {
7700
+ depth--;
7701
+ if (depth === 0) {
7702
+ return match.index + match[0].length - start;
7043
7703
  }
7044
- const add = [];
7045
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7046
- if (fromB === 0 && toB === inserted.length) {
7704
+ } else {
7705
+ depth++;
7706
+ }
7707
+ }
7708
+ return 0;
7709
+ };
7710
+ var linkLength = (buffer, start, bracketAt) => {
7711
+ const bracketClose = buffer.indexOf("]", bracketAt + 1);
7712
+ if (bracketClose === -1) {
7713
+ return 0;
7714
+ }
7715
+ if (bracketClose + 1 >= buffer.length) {
7716
+ return 0;
7717
+ }
7718
+ if (buffer[bracketClose + 1] !== "(") {
7719
+ return 1;
7720
+ }
7721
+ const parenClose = buffer.indexOf(")", bracketClose + 2);
7722
+ if (parenClose === -1) {
7723
+ return 0;
7724
+ }
7725
+ return parenClose + 1 - start;
7726
+ };
7727
+
7728
+ // src/extensions/tags/xml-block-decoration.ts
7729
+ import { xmlLanguage as xmlLanguage2 } from "@codemirror/lang-xml";
7730
+ import { Decoration as Decoration16, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7731
+ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7732
+ const lineDecoration = lineClass ? Decoration16.line({
7733
+ class: lineClass
7734
+ }) : void 0;
7735
+ const contentDecoration = contentClass ? Decoration16.mark({
7736
+ class: contentClass
7737
+ }) : void 0;
7738
+ const hideDecoration = hideTags ? Decoration16.replace({}) : void 0;
7739
+ const buildDecorations5 = (view) => {
7740
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7741
+ if (!text.includes(`<${tag}`)) {
7742
+ return Decoration16.none;
7743
+ }
7744
+ const tree = xmlLanguage2.parser.parse(text);
7745
+ const ranges = [];
7746
+ tree.iterate({
7747
+ enter: (node) => {
7748
+ if (node.type.name !== "Element") {
7047
7749
  return;
7048
7750
  }
7049
- if (toA === tr.startState.doc.length && inserted.length > 0) {
7050
- add.push(Decoration14.mark({
7051
- class: "cm-fade-in"
7052
- }).range(fromB, toB));
7751
+ const openTag = node.node.getChild("OpenTag");
7752
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7753
+ const tagNameNode = openTag?.getChild("TagName");
7754
+ if (!openTag || !tagNameNode) {
7755
+ return;
7053
7756
  }
7054
- });
7055
- return next.update({
7056
- add
7057
- });
7058
- },
7059
- provide: (f) => EditorView28.decorations.from(f)
7060
- });
7061
- const timerPlugin = ViewPlugin18.fromClass(class {
7062
- view;
7063
- // Map a simple key "from-to" to timer id.
7064
- _timers = /* @__PURE__ */ new Map();
7757
+ if (text.slice(tagNameNode.from, tagNameNode.to) !== tag) {
7758
+ return;
7759
+ }
7760
+ const contentFrom = openTag.to;
7761
+ const contentTo = closeTag?.from ?? node.node.to;
7762
+ if (hideDecoration) {
7763
+ ranges.push(hideDecoration.range(openTag.from, openTag.to));
7764
+ if (closeTag) {
7765
+ ranges.push(hideDecoration.range(closeTag.from, closeTag.to));
7766
+ }
7767
+ }
7768
+ if (contentDecoration && contentFrom < contentTo) {
7769
+ ranges.push(contentDecoration.range(contentFrom, contentTo));
7770
+ }
7771
+ if (lineDecoration && contentFrom <= view.state.doc.length) {
7772
+ let pos = contentFrom;
7773
+ while (pos <= contentTo && pos <= view.state.doc.length) {
7774
+ const line = view.state.doc.lineAt(pos);
7775
+ ranges.push(lineDecoration.range(line.from));
7776
+ if (line.to >= contentTo) {
7777
+ break;
7778
+ }
7779
+ pos = line.to + 1;
7780
+ }
7781
+ }
7782
+ }
7783
+ });
7784
+ return Decoration16.set(ranges, true);
7785
+ };
7786
+ return ViewPlugin22.fromClass(class {
7787
+ decorations;
7065
7788
  constructor(view) {
7066
- this.view = view;
7789
+ this.decorations = buildDecorations5(view);
7067
7790
  }
7068
7791
  update(update2) {
7069
- if (!update2.docChanged) {
7070
- return;
7792
+ if (update2.docChanged) {
7793
+ this.decorations = buildDecorations5(update2.view);
7071
7794
  }
7072
- update2.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7073
- if (toA !== update2.startState.doc.length || inserted.length === 0) {
7795
+ }
7796
+ }, {
7797
+ decorations: (instance) => instance.decorations
7798
+ });
7799
+ };
7800
+
7801
+ // src/extensions/tags/xml-formatting.ts
7802
+ import { xmlLanguage as xmlLanguage3 } from "@codemirror/lang-xml";
7803
+ import { Decoration as Decoration17, EditorView as EditorView31, ViewPlugin as ViewPlugin23 } from "@codemirror/view";
7804
+ var XML_TAG_NODES = /* @__PURE__ */ new Set([
7805
+ "OpenTag",
7806
+ "CloseTag",
7807
+ "SelfClosingTag",
7808
+ "MismatchedCloseTag"
7809
+ ]);
7810
+ var xmlElementMark = Decoration17.mark({
7811
+ class: "cm-xml-element"
7812
+ });
7813
+ var xmlTagMark = Decoration17.mark({
7814
+ class: "cm-xml-tag"
7815
+ });
7816
+ var xmlContentMark = Decoration17.mark({
7817
+ class: "cm-xml-content"
7818
+ });
7819
+ var xmlFormatting = ({ skip } = {}) => {
7820
+ const skipSet = skip && skip.length > 0 ? new Set(skip) : void 0;
7821
+ const buildDecorations5 = (view) => {
7822
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7823
+ if (!text.includes("<")) {
7824
+ return Decoration17.none;
7825
+ }
7826
+ const tagNameAt = (node) => text.slice(node.from, node.to);
7827
+ const tree = xmlLanguage3.parser.parse(text);
7828
+ const ranges = [];
7829
+ tree.iterate({
7830
+ enter: (node) => {
7831
+ const name = node.type.name;
7832
+ if (name === "SelfClosingTag" && node.from < node.to) {
7833
+ if (skipSet) {
7834
+ const tagNameNode = node.node.getChild("TagName");
7835
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7836
+ return false;
7837
+ }
7838
+ }
7839
+ ranges.push(xmlElementMark.range(node.from, node.to));
7840
+ ranges.push(xmlTagMark.range(node.from, node.to));
7074
7841
  return;
7075
7842
  }
7076
- const key = `${fromB}-${toB}`;
7077
- if (this._timers.has(key)) {
7078
- clearTimeout(this._timers.get(key));
7843
+ if (XML_TAG_NODES.has(name) && node.from < node.to) {
7844
+ ranges.push(xmlTagMark.range(node.from, node.to));
7845
+ return;
7846
+ }
7847
+ if (name === "Element" && node.from < node.to) {
7848
+ const openTag = node.node.getChild("OpenTag");
7849
+ if (openTag && skipSet) {
7850
+ const tagNameNode = openTag.getChild("TagName");
7851
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7852
+ return false;
7853
+ }
7854
+ }
7855
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7856
+ ranges.push(xmlElementMark.range(node.from, node.to));
7857
+ if (openTag && closeTag && openTag.to < closeTag.from) {
7858
+ ranges.push(xmlContentMark.range(openTag.to, closeTag.from));
7859
+ }
7079
7860
  }
7080
- const totalDelay = FADE_IN_DURATION + removalDelay;
7081
- const id = setTimeout(() => {
7082
- this.view.dispatch({
7083
- effects: removeDecoration.of({
7084
- from: fromB,
7085
- to: toB
7086
- })
7087
- });
7088
- this._timers.delete(key);
7089
- }, totalDelay);
7090
- this._timers.set(key, id);
7091
- });
7092
- }
7093
- destroy() {
7094
- for (const id of this._timers.values()) {
7095
- clearTimeout(id);
7096
7861
  }
7097
- this._timers.clear();
7098
- }
7099
- });
7862
+ });
7863
+ return Decoration17.set(ranges, true);
7864
+ };
7100
7865
  return [
7101
- fadeField,
7102
- timerPlugin,
7103
- EditorView28.theme({
7104
- ".cm-line > span": {
7105
- opacity: "0.8"
7106
- },
7107
- ".cm-fade-in": {
7108
- animation: "fade-in 3s ease-out forwards"
7109
- },
7110
- "@keyframes fade-in": {
7111
- "0%": {
7112
- opacity: "0"
7113
- },
7114
- "80%": {
7115
- opacity: "1"
7116
- },
7117
- "100%": {
7118
- opacity: "0.8"
7866
+ ViewPlugin23.fromClass(class {
7867
+ decorations;
7868
+ constructor(view) {
7869
+ this.decorations = buildDecorations5(view);
7870
+ }
7871
+ update(update2) {
7872
+ if (update2.docChanged) {
7873
+ this.decorations = buildDecorations5(update2.view);
7119
7874
  }
7120
7875
  }
7876
+ }, {
7877
+ decorations: (instance) => instance.decorations
7878
+ }),
7879
+ EditorView31.baseTheme({
7880
+ ".cm-xml-element": {
7881
+ backgroundColor: "var(--color-active-surface)",
7882
+ borderRadius: "0.25rem",
7883
+ padding: "0.25rem"
7884
+ },
7885
+ ".cm-xml-tag": {
7886
+ color: "var(--color-blue-500)",
7887
+ fontFamily: "var(--font-mono)"
7888
+ },
7889
+ ".cm-xml-content": {}
7121
7890
  })
7122
7891
  ];
7123
7892
  };
7124
7893
 
7125
7894
  // src/extensions/tags/xml-tags.ts
7126
7895
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7127
- import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect9, StateField as StateField13 } from "@codemirror/state";
7128
- import { Decoration as Decoration15, EditorView as EditorView29, ViewPlugin as ViewPlugin19, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7896
+ import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7897
+ import { Decoration as Decoration18, EditorView as EditorView32, ViewPlugin as ViewPlugin24, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
7129
7898
  import { invariant as invariant7 } from "@dxos/invariant";
7130
- import { log as log10 } from "@dxos/log";
7899
+ import { log as log11 } from "@dxos/log";
7900
+ import { Domino as Domino4 } from "@dxos/ui";
7131
7901
 
7132
7902
  // src/extensions/tags/xml-util.ts
7133
7903
  import { invariant as invariant6 } from "@dxos/invariant";
7134
- var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7904
+ var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7135
7905
  var nodeToJson = (state, node) => {
7136
- invariant6(node.type.name === "Element", "Node is not an Element", {
7137
- F: __dxlog_file15,
7138
- L: 18,
7139
- S: void 0,
7140
- A: [
7141
- "node.type.name === 'Element'",
7142
- "'Node is not an Element'"
7143
- ]
7144
- });
7906
+ invariant6(node.type.name === "Element", "Node is not an Element", { "~LogMeta": "~LogMeta", F: __dxlog_file16, L: 8, S: void 0, A: ["node.type.name === 'Element'", "'Node is not an Element'"] });
7145
7907
  const openTag = node.node.getChild("OpenTag") || node.node.getChild("SelfClosingTag");
7146
7908
  if (openTag) {
7147
7909
  const tagName = openTag.getChild("TagName");
@@ -7173,13 +7935,23 @@ var nodeToJson = (state, node) => {
7173
7935
  if (node.type.name === "Element" && openTag.type.name !== "SelfClosingTag") {
7174
7936
  const children = [];
7175
7937
  let child = node.node.firstChild;
7938
+ const appendText = (raw) => {
7939
+ if (raw.length === 0) {
7940
+ return;
7941
+ }
7942
+ const last = children[children.length - 1];
7943
+ if (typeof last === "string") {
7944
+ children[children.length - 1] = last + raw;
7945
+ } else {
7946
+ children.push(raw);
7947
+ }
7948
+ };
7176
7949
  while (child) {
7177
7950
  if (child.type.name !== "OpenTag" && child.type.name !== "CloseTag") {
7178
7951
  if (child.type.name === "Text") {
7179
- const text = state.doc.sliceString(child.from, child.to).trim();
7180
- if (text) {
7181
- children.push(text);
7182
- }
7952
+ appendText(state.doc.sliceString(child.from, child.to));
7953
+ } else if (child.type.name === "EntityReference" || child.type.name === "CharacterReference") {
7954
+ appendText(decodeXmlEntity(state.doc.sliceString(child.from, child.to)));
7183
7955
  } else if (child.type.name === "Element") {
7184
7956
  const data = nodeToJson(state, child);
7185
7957
  if (data) {
@@ -7189,26 +7961,63 @@ var nodeToJson = (state, node) => {
7189
7961
  }
7190
7962
  child = child.nextSibling;
7191
7963
  }
7964
+ if (children.length > 0 && typeof children[0] === "string") {
7965
+ children[0] = children[0].trimStart();
7966
+ }
7192
7967
  if (children.length > 0) {
7193
- tag.children = children;
7968
+ const lastIndex = children.length - 1;
7969
+ const last = children[lastIndex];
7970
+ if (typeof last === "string") {
7971
+ children[lastIndex] = last.trimEnd();
7972
+ }
7973
+ }
7974
+ const trimmed = children.filter((value) => typeof value !== "string" || value.length > 0);
7975
+ if (trimmed.length > 0) {
7976
+ tag.children = trimmed;
7194
7977
  }
7195
7978
  }
7196
7979
  return tag;
7197
7980
  }
7198
7981
  };
7982
+ var XML_NAMED_ENTITIES = {
7983
+ "&lt;": "<",
7984
+ "&gt;": ">",
7985
+ "&amp;": "&",
7986
+ "&quot;": '"',
7987
+ "&apos;": "'"
7988
+ };
7989
+ var decodeXmlEntity = (raw) => {
7990
+ const named = XML_NAMED_ENTITIES[raw];
7991
+ if (named !== void 0) {
7992
+ return named;
7993
+ }
7994
+ const numeric = /^&#(x?)([0-9a-fA-F]+);$/.exec(raw);
7995
+ if (numeric) {
7996
+ const code = parseInt(numeric[2], numeric[1] ? 16 : 10);
7997
+ if (Number.isFinite(code)) {
7998
+ try {
7999
+ return String.fromCodePoint(code);
8000
+ } catch {
8001
+ }
8002
+ }
8003
+ }
8004
+ return raw;
8005
+ };
7199
8006
 
7200
8007
  // src/extensions/tags/xml-tags.ts
7201
- var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7202
- var navigatePreviousEffect = StateEffect9.define();
7203
- var navigateNextEffect = StateEffect9.define();
8008
+ var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
8009
+ var navigatePreviousEffect = StateEffect12.define();
8010
+ var navigateNextEffect = StateEffect12.define();
7204
8011
  var getXmlTextChild = (children) => {
7205
8012
  const child = children?.[0];
7206
8013
  return typeof child === "string" ? child : null;
7207
8014
  };
7208
- var xmlTagContextEffect = StateEffect9.define();
7209
- var xmlTagResetEffect = StateEffect9.define();
7210
- var xmlTagUpdateEffect = StateEffect9.define();
7211
- var widgetContextStateField = StateField13.define({
8015
+ var xmlWidgetId = (explicit, fallback) => typeof explicit === "string" && explicit.length > 0 ? explicit : fallback;
8016
+ var escapeRegExpSource3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8017
+ var xmlTagContextEffect = StateEffect12.define();
8018
+ var xmlTagResetEffect = StateEffect12.define();
8019
+ var xmlTagUpdateEffect = StateEffect12.define();
8020
+ var widgetContextStateField = StateField14.define({
7212
8021
  create: () => void 0,
7213
8022
  update: (value, tr) => {
7214
8023
  for (const effect of tr.effects) {
@@ -7219,7 +8028,7 @@ var widgetContextStateField = StateField13.define({
7219
8028
  return value;
7220
8029
  }
7221
8030
  });
7222
- var widgetStateMapStateField = StateField13.define({
8031
+ var widgetStateMapStateField = StateField14.define({
7223
8032
  create: () => ({}),
7224
8033
  update: (map, tr) => {
7225
8034
  for (const effect of tr.effects) {
@@ -7228,15 +8037,10 @@ var widgetStateMapStateField = StateField13.define({
7228
8037
  }
7229
8038
  if (effect.is(xmlTagUpdateEffect)) {
7230
8039
  const { id, value } = effect.value;
7231
- log10("widget updated", {
8040
+ log11("widget updated", {
7232
8041
  id,
7233
8042
  value
7234
- }, {
7235
- F: __dxlog_file16,
7236
- L: 153,
7237
- S: void 0,
7238
- C: (f, a) => f(...a)
7239
- });
8043
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 59, S: void 0 });
7240
8044
  const state = typeof value === "function" ? value(map[id]) : value;
7241
8045
  return {
7242
8046
  ...map,
@@ -7263,15 +8067,10 @@ var createWidgetMap = (setWidgets) => {
7263
8067
  const widgets = /* @__PURE__ */ new Map();
7264
8068
  const notifier = {
7265
8069
  mounted: (state) => {
7266
- log10("widget mounted", {
8070
+ log11("widget mounted", {
7267
8071
  id: state.id,
7268
8072
  tag: state.props._tag
7269
- }, {
7270
- F: __dxlog_file16,
7271
- L: 206,
7272
- S: void 0,
7273
- C: (f, a) => f(...a)
7274
- });
8073
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 101, S: void 0 });
7275
8074
  widgets.set(state.id, state);
7276
8075
  setWidgets?.([
7277
8076
  ...widgets.values()
@@ -7279,15 +8078,10 @@ var createWidgetMap = (setWidgets) => {
7279
8078
  },
7280
8079
  unmounted: (id) => {
7281
8080
  const state = widgets.get(id);
7282
- log10("widget unmounted", {
8081
+ log11("widget unmounted", {
7283
8082
  id,
7284
8083
  tag: state?.props._tag
7285
- }, {
7286
- F: __dxlog_file16,
7287
- L: 212,
7288
- S: void 0,
7289
- C: (f, a) => f(...a)
7290
- });
8084
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 112, S: void 0 });
7291
8085
  widgets.delete(id);
7292
8086
  setWidgets?.([
7293
8087
  ...widgets.values()
@@ -7296,7 +8090,7 @@ var createWidgetMap = (setWidgets) => {
7296
8090
  };
7297
8091
  return notifier;
7298
8092
  };
7299
- var keyHandlers = keymap13.of([
8093
+ var keyHandlers = keymap14.of([
7300
8094
  {
7301
8095
  key: "Mod-ArrowUp",
7302
8096
  run: (view) => {
@@ -7317,7 +8111,7 @@ var keyHandlers = keymap13.of([
7317
8111
  }
7318
8112
  ]);
7319
8113
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7320
- return EditorView29.updateListener.of((update2) => {
8114
+ return EditorView32.updateListener.of((update2) => {
7321
8115
  update2.transactions.forEach((transaction) => {
7322
8116
  for (const effect of transaction.effects) {
7323
8117
  if (effect.is(navigatePreviousEffect)) {
@@ -7345,11 +8139,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7345
8139
  anchor: line.from,
7346
8140
  head: line.from
7347
8141
  },
7348
- effects: scrollToLineEffect.of({
7349
- line: line.number,
7350
- options: {
7351
- offset: -16
7352
- }
8142
+ effects: scrollerLineEffect.of({
8143
+ line: line.number - 1,
8144
+ offset: -16
7353
8145
  })
7354
8146
  });
7355
8147
  continue;
@@ -7380,11 +8172,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7380
8172
  anchor: line.to,
7381
8173
  head: line.to
7382
8174
  },
7383
- effects: scrollToLineEffect.of({
7384
- line: line.number,
7385
- options: {
7386
- offset: -16
7387
- }
8175
+ effects: scrollerLineEffect.of({
8176
+ line: line.number - 1,
8177
+ offset: -16
7388
8178
  })
7389
8179
  });
7390
8180
  } else {
@@ -7394,11 +8184,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7394
8184
  anchor: line.to,
7395
8185
  head: line.to
7396
8186
  },
7397
- effects: scrollToLineEffect.of({
7398
- line: line.number,
7399
- options: {
7400
- position: "end"
7401
- }
8187
+ effects: scrollerLineEffect.of({
8188
+ line: line.number - 1,
8189
+ position: "end"
7402
8190
  })
7403
8191
  });
7404
8192
  }
@@ -7408,7 +8196,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7408
8196
  });
7409
8197
  });
7410
8198
  };
7411
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin19.fromClass(class {
8199
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
7412
8200
  update(update2) {
7413
8201
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
7414
8202
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -7435,19 +8223,25 @@ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin1
7435
8223
  }
7436
8224
  }
7437
8225
  });
7438
- var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.define({
8226
+ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.define({
7439
8227
  create: (state) => {
7440
8228
  return buildDecorations4(state, {
7441
8229
  from: 0,
7442
8230
  to: state.doc.length
7443
8231
  }, registry, notifier);
7444
8232
  },
7445
- update: ({ from, decorations: decorations2 }, tr) => {
8233
+ update: ({ from, streamingFrom, decorations: decorations2 }, tr) => {
7446
8234
  for (const effect of tr.effects) {
7447
8235
  if (effect.is(xmlTagResetEffect)) {
8236
+ if (tr.docChanged) {
8237
+ return buildDecorations4(tr.state, {
8238
+ from: 0,
8239
+ to: tr.state.doc.length
8240
+ }, registry, notifier);
8241
+ }
7448
8242
  return {
7449
8243
  from: 0,
7450
- decorations: Decoration15.none
8244
+ decorations: Decoration18.none
7451
8245
  };
7452
8246
  }
7453
8247
  }
@@ -7455,27 +8249,26 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7455
8249
  const { state } = tr;
7456
8250
  const reset = tr.changes.touchesRange(0, from);
7457
8251
  if (reset) {
7458
- log10("document reset", {
8252
+ log11("document reset", {
7459
8253
  from,
7460
8254
  to: state.doc.length
7461
- }, {
7462
- F: __dxlog_file16,
7463
- L: 371,
7464
- S: void 0,
7465
- C: (f, a) => f(...a)
7466
- });
8255
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 298, S: void 0 });
7467
8256
  return buildDecorations4(state, {
7468
8257
  from: 0,
7469
8258
  to: state.doc.length
7470
8259
  }, registry, notifier);
7471
8260
  } else {
8261
+ const rebuildFrom = streamingFrom ?? from;
7472
8262
  const result = buildDecorations4(state, {
7473
- from,
8263
+ from: rebuildFrom,
7474
8264
  to: state.doc.length
7475
8265
  }, registry, notifier);
7476
8266
  return {
7477
8267
  from: result.from,
8268
+ streamingFrom: result.streamingFrom,
7478
8269
  decorations: decorations2.update({
8270
+ // Remove old streaming decorations — they are rebuilt each tick.
8271
+ filter: (_f, _t, deco) => !deco.spec.streaming,
7479
8272
  add: decorationSetToArray(result.decorations)
7480
8273
  })
7481
8274
  };
@@ -7483,12 +8276,13 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7483
8276
  }
7484
8277
  return {
7485
8278
  from,
8279
+ streamingFrom,
7486
8280
  decorations: decorations2
7487
8281
  };
7488
8282
  },
7489
8283
  provide: (field) => [
7490
- EditorView29.decorations.from(field, (v) => v.decorations),
7491
- EditorView29.atomicRanges.of((view) => view.state.field(field).decorations || Decoration15.none)
8284
+ EditorView32.decorations.from(field, (v) => v.decorations),
8285
+ EditorView32.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
7492
8286
  ]
7493
8287
  });
7494
8288
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -7499,10 +8293,11 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7499
8293
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
7500
8294
  return {
7501
8295
  from: range.from,
7502
- decorations: Decoration15.none
8296
+ decorations: Decoration18.none
7503
8297
  };
7504
8298
  }
7505
8299
  let last = range.from;
8300
+ let streamingFrom;
7506
8301
  tree.iterate({
7507
8302
  from: range.from,
7508
8303
  to: range.to,
@@ -7515,21 +8310,26 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7515
8310
  if (args) {
7516
8311
  const def = registry[args._tag];
7517
8312
  if (def) {
8313
+ if (def.streaming && !node.node.getChild("CloseTag")) {
8314
+ return false;
8315
+ }
7518
8316
  const { block, factory, Component } = def;
7519
- const widgetState = args.id ? widgetStateMap[args.id] : void 0;
7520
8317
  const nodeRange = {
7521
8318
  from: node.node.from,
7522
8319
  to: node.node.to
7523
8320
  };
8321
+ const widgetId = xmlWidgetId(args.id, def.streaming ? `cm-xml-${nodeRange.from}` : `cm-xml-${nodeRange.from}-${nodeRange.to}`);
8322
+ const widgetState = widgetStateMap[widgetId];
7524
8323
  const props = {
7525
- context,
8324
+ id: widgetId,
7526
8325
  range: nodeRange,
8326
+ context,
7527
8327
  ...args,
7528
8328
  ...widgetState
7529
8329
  };
7530
- const widget = factory ? factory(props) : Component ? args.id && new PlaceholderWidget2(args.id, Component, props, notifier) : void 0;
8330
+ const widget = factory ? factory(props) ?? void 0 : Component ? new PlaceholderWidget2(widgetId, Component, props, notifier) : void 0;
7531
8331
  if (widget) {
7532
- builder.add(nodeRange.from, nodeRange.to, Decoration15.replace({
8332
+ builder.add(nodeRange.from, nodeRange.to, Decoration18.replace({
7533
8333
  widget,
7534
8334
  block,
7535
8335
  atomic: true,
@@ -7541,20 +8341,72 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7541
8341
  }
7542
8342
  }
7543
8343
  } catch (err) {
7544
- log10.catch(err, void 0, {
7545
- F: __dxlog_file16,
7546
- L: 456,
7547
- S: void 0,
7548
- C: (f, a) => f(...a)
7549
- });
8344
+ log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 401, S: void 0 });
7550
8345
  }
7551
8346
  return false;
7552
8347
  }
7553
8348
  }
7554
8349
  }
7555
8350
  });
8351
+ const streamingTagNames = Object.entries(registry).filter(([, def]) => def.streaming).map(([name]) => name).sort((a, b) => b.length - a.length);
8352
+ if (streamingTagNames.length > 0) {
8353
+ const tailText = state.sliceDoc(range.from, range.to);
8354
+ const streamingPattern = streamingTagNames.map(escapeRegExpSource3).join("|");
8355
+ const tagPattern = new RegExp(`<(${streamingPattern})(\\s[^>]*)?>`, "g");
8356
+ let match;
8357
+ while ((match = tagPattern.exec(tailText)) !== null) {
8358
+ const tagName = match[1];
8359
+ const closeTag = `</${tagName}>`;
8360
+ const afterOpen = match.index + match[0].length;
8361
+ if (tailText.indexOf(closeTag, afterOpen) === -1) {
8362
+ const absoluteFrom = range.from + match.index;
8363
+ const contentFrom = range.from + afterOpen;
8364
+ const innerText = state.sliceDoc(contentFrom, range.to).trim();
8365
+ const def = registry[tagName];
8366
+ const props = {
8367
+ _tag: tagName,
8368
+ context,
8369
+ range: {
8370
+ from: absoluteFrom,
8371
+ to: range.to
8372
+ },
8373
+ children: innerText ? [
8374
+ innerText
8375
+ ] : void 0
8376
+ };
8377
+ const attrPattern = /(\w+)="([^"]*)"/g;
8378
+ let attrMatch;
8379
+ while ((attrMatch = attrPattern.exec(match[0])) !== null) {
8380
+ props[attrMatch[1]] = attrMatch[2];
8381
+ }
8382
+ const widgetId = xmlWidgetId(props.id, `cm-xml-${absoluteFrom}`);
8383
+ const widgetState = widgetStateMap[widgetId];
8384
+ const mergedProps = {
8385
+ ...props,
8386
+ id: widgetId,
8387
+ ...widgetState
8388
+ };
8389
+ const widget = def.factory ? def.factory(mergedProps) ?? void 0 : def.Component ? new PlaceholderWidget2(widgetId, def.Component, mergedProps, notifier, true) : void 0;
8390
+ if (widget) {
8391
+ builder.add(absoluteFrom, range.to, Decoration18.replace({
8392
+ widget,
8393
+ block: def.block,
8394
+ atomic: true,
8395
+ inclusive: true,
8396
+ tag: tagName,
8397
+ streaming: true,
8398
+ contentFrom
8399
+ }));
8400
+ streamingFrom = absoluteFrom;
8401
+ last = absoluteFrom;
8402
+ }
8403
+ break;
8404
+ }
8405
+ }
8406
+ }
7556
8407
  return {
7557
8408
  from: last,
8409
+ streamingFrom,
7558
8410
  decorations: builder.finish()
7559
8411
  };
7560
8412
  };
@@ -7563,108 +8415,62 @@ var PlaceholderWidget2 = class extends WidgetType10 {
7563
8415
  Component;
7564
8416
  props;
7565
8417
  notifier;
7566
- _root = null;
7567
- constructor(id, Component, props, notifier) {
7568
- super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier;
7569
- invariant7(id, void 0, {
7570
- F: __dxlog_file16,
7571
- L: 482,
7572
- S: this,
7573
- A: [
7574
- "id",
7575
- ""
7576
- ]
7577
- });
8418
+ streaming;
8419
+ #root = null;
8420
+ #view;
8421
+ constructor(id, Component, props, notifier, streaming) {
8422
+ super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier, this.streaming = streaming;
8423
+ invariant7(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 488, S: this, A: ["id", ""] });
7578
8424
  }
7579
8425
  get root() {
7580
- return this._root;
8426
+ return this.#root;
7581
8427
  }
7582
8428
  eq(other) {
8429
+ if (this.streaming) {
8430
+ return false;
8431
+ }
7583
8432
  return this.id === other.id;
7584
8433
  }
7585
8434
  ignoreEvent() {
7586
8435
  return true;
7587
8436
  }
7588
- toDOM(_view) {
7589
- this._root = document.createElement("span");
8437
+ toDOM(view) {
8438
+ this.#view = view;
8439
+ this.#root = Domino4.of("div").classNames("min-h-[24px]").root;
8440
+ const props = Object.assign({}, this.props, {
8441
+ view
8442
+ });
8443
+ this.notifier.mounted({
8444
+ id: this.id,
8445
+ root: this.#root,
8446
+ props,
8447
+ Component: this.Component
8448
+ });
8449
+ return this.#root;
8450
+ }
8451
+ updateDOM(dom) {
8452
+ this.#root = dom;
8453
+ const props = Object.assign({}, this.props, {
8454
+ view: this.#view
8455
+ });
7590
8456
  this.notifier.mounted({
7591
8457
  id: this.id,
7592
- root: this._root,
7593
- props: this.props,
8458
+ root: this.#root,
8459
+ props,
7594
8460
  Component: this.Component
7595
8461
  });
7596
- return this._root;
8462
+ return true;
7597
8463
  }
7598
8464
  destroy(_dom) {
7599
8465
  this.notifier.unmounted(this.id);
7600
- this._root = null;
8466
+ this.#root = null;
8467
+ this.#view = void 0;
7601
8468
  }
7602
8469
  };
7603
-
7604
- // src/extensions/typewriter.ts
7605
- import { keymap as keymap14 } from "@codemirror/view";
7606
- var defaultItems = [
7607
- "hello world!",
7608
- "this is a test.",
7609
- "this is [DXOS](https://dxos.org)"
7610
- ];
7611
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
7612
- let t;
7613
- let idx = 0;
7614
- return [
7615
- keymap14.of([
7616
- {
7617
- // Reset.
7618
- key: "alt-meta-'",
7619
- run: () => {
7620
- clearTimeout(t);
7621
- idx = 0;
7622
- return true;
7623
- }
7624
- },
7625
- {
7626
- // Next prompt.
7627
- // TODO(burdon): Press 1-9 to select prompt?
7628
- key: "Shift-Meta-'",
7629
- run: (view) => {
7630
- clearTimeout(t);
7631
- const text = items[idx++];
7632
- if (idx === items?.length) {
7633
- idx = 0;
7634
- }
7635
- let i = 0;
7636
- const insert = (d = 0) => {
7637
- t = setTimeout(() => {
7638
- const pos = view.state.selection.main.head;
7639
- view.dispatch({
7640
- changes: {
7641
- from: pos,
7642
- insert: text[i++]
7643
- },
7644
- selection: {
7645
- anchor: pos + 1
7646
- }
7647
- });
7648
- if (i < text.length) {
7649
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
7650
- }
7651
- }, d);
7652
- };
7653
- insert();
7654
- return true;
7655
- }
7656
- }
7657
- ])
7658
- ];
7659
- };
7660
8470
  export {
7661
8471
  Cursor,
7662
- EditorInputMode,
7663
- EditorInputModes,
7664
- EditorState3 as EditorState,
7665
- EditorView30 as EditorView,
7666
- EditorViewMode,
7667
- EditorViewModes,
8472
+ EditorState4 as EditorState,
8473
+ EditorView33 as EditorView,
7668
8474
  Inline,
7669
8475
  InputModeExtensions,
7670
8476
  List,
@@ -7681,6 +8487,7 @@ export {
7681
8487
  addStyle,
7682
8488
  annotations,
7683
8489
  autoScroll,
8490
+ autoScrollEffect,
7684
8491
  autocomplete,
7685
8492
  automerge,
7686
8493
  awareness,
@@ -7694,9 +8501,11 @@ export {
7694
8501
  commentClickedEffect,
7695
8502
  comments,
7696
8503
  commentsState,
8504
+ compactSlots,
7697
8505
  convertTreeToJson,
7698
8506
  createBasicExtensions,
7699
8507
  createComment,
8508
+ createCrawler,
7700
8509
  createDataExtensions,
7701
8510
  createEditorStateStore,
7702
8511
  createEditorStateTransaction,
@@ -7716,12 +8525,12 @@ export {
7716
8525
  defaultThemeSlots,
7717
8526
  deleteItem,
7718
8527
  documentId,
8528
+ documentSlots,
7719
8529
  dropFile,
8530
+ editorClassNames,
7720
8531
  editorInputMode,
7721
- editorSlots,
7722
- editorWidth,
7723
- editorWithToolbarLayout,
7724
8532
  extendedMarkdown,
8533
+ fader,
7725
8534
  filterChars,
7726
8535
  flattenRect,
7727
8536
  focus,
@@ -7757,6 +8566,7 @@ export {
7757
8566
  markdownTagsExtensions,
7758
8567
  matchCompletion,
7759
8568
  mention,
8569
+ mobileSlots,
7760
8570
  modalStateEffect,
7761
8571
  modalStateField,
7762
8572
  moveItemDown,
@@ -7776,9 +8586,12 @@ export {
7776
8586
  removeList,
7777
8587
  removeStyle,
7778
8588
  replacer,
8589
+ scrollPastEnd,
7779
8590
  scrollThreadIntoView,
7780
8591
  scrollToLine,
7781
- scrollToLineEffect,
8592
+ scroller,
8593
+ scrollerCrawlEffect,
8594
+ scrollerLineEffect,
7782
8595
  selectionState,
7783
8596
  setBlockquote,
7784
8597
  setComments,
@@ -7786,10 +8599,8 @@ export {
7786
8599
  setSelection,
7787
8600
  setStyle,
7788
8601
  singleValueFacet,
7789
- smoothScroll,
7790
- stackItemContentEditorClassNames,
8602
+ snippets2 as snippets,
7791
8603
  staticCompletion,
7792
- streamer,
7793
8604
  submit,
7794
8605
  tabbable,
7795
8606
  table,
@@ -7808,7 +8619,12 @@ export {
7808
8619
  treeFacet,
7809
8620
  typeahead,
7810
8621
  typewriter,
8622
+ typewriterBypass,
8623
+ typewriterDrainingEffect,
7811
8624
  wrapWithCatch,
8625
+ xmlBlockDecoration,
8626
+ xmlElementLength,
8627
+ xmlFormatting,
7812
8628
  xmlTagContextEffect,
7813
8629
  xmlTagResetEffect,
7814
8630
  xmlTagUpdateEffect,