@dxos/ui-editor 0.8.4-main.ef1bc66f44 → 0.8.4-main.f466a3d56e

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 (181) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +2033 -1155
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +26 -6
  7. package/dist/lib/browser/types/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/index.mjs +2039 -1160
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/types/index.mjs +27 -6
  12. package/dist/lib/node-esm/types/index.mjs.map +4 -4
  13. package/dist/types/src/defaults.d.ts +4 -10
  14. package/dist/types/src/defaults.d.ts.map +1 -1
  15. package/dist/types/src/extensions/annotations.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 +2 -3
  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/scrolling/auto-scroll.d.ts +18 -0
  64. package/dist/types/src/extensions/scrolling/auto-scroll.d.ts.map +1 -0
  65. package/dist/types/src/extensions/scrolling/crawler.d.ts +75 -0
  66. package/dist/types/src/extensions/scrolling/crawler.d.ts.map +1 -0
  67. package/dist/types/src/extensions/scrolling/index.d.ts +5 -0
  68. package/dist/types/src/extensions/scrolling/index.d.ts.map +1 -0
  69. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts +3 -0
  70. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts.map +1 -0
  71. package/dist/types/src/extensions/scrolling/scroller.d.ts +16 -0
  72. package/dist/types/src/extensions/scrolling/scroller.d.ts.map +1 -0
  73. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  74. package/dist/types/src/extensions/snippets.d.ts +10 -0
  75. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  76. package/dist/types/src/extensions/submit.d.ts.map +1 -1
  77. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
  78. package/dist/types/src/extensions/tags/fader.d.ts +12 -0
  79. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
  80. package/dist/types/src/extensions/tags/index.d.ts +4 -1
  81. package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
  82. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  83. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  84. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  85. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  86. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  87. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  88. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  89. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  90. package/dist/types/src/extensions/tags/xml-tags.d.ts +28 -8
  91. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  92. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -1
  93. package/dist/types/src/index.d.ts +0 -1
  94. package/dist/types/src/index.d.ts.map +1 -1
  95. package/dist/types/src/styles/index.d.ts +0 -2
  96. package/dist/types/src/styles/index.d.ts.map +1 -1
  97. package/dist/types/src/styles/theme.d.ts +15 -0
  98. package/dist/types/src/styles/theme.d.ts.map +1 -1
  99. package/dist/types/src/types/types.d.ts +4 -4
  100. package/dist/types/src/types/types.d.ts.map +1 -1
  101. package/dist/types/src/util/cursor.d.ts +1 -1
  102. package/dist/types/src/util/cursor.d.ts.map +1 -1
  103. package/dist/types/src/util/debug.d.ts.map +1 -1
  104. package/dist/types/src/util/decorations.d.ts.map +1 -1
  105. package/dist/types/src/util/dom.d.ts.map +1 -1
  106. package/dist/types/src/util/facet.d.ts.map +1 -1
  107. package/dist/types/src/util/util.d.ts.map +1 -1
  108. package/dist/types/tsconfig.tsbuildinfo +1 -1
  109. package/package.json +37 -40
  110. package/src/defaults.ts +33 -20
  111. package/src/extensions/annotations.ts +1 -1
  112. package/src/extensions/autocomplete/placeholder.ts +37 -18
  113. package/src/extensions/automerge/automerge.test.tsx +37 -11
  114. package/src/extensions/automerge/automerge.ts +5 -7
  115. package/src/extensions/blocks.ts +5 -5
  116. package/src/extensions/comments.ts +5 -6
  117. package/src/extensions/dnd.ts +2 -2
  118. package/src/extensions/factories.test.ts +88 -0
  119. package/src/extensions/factories.ts +32 -15
  120. package/src/extensions/folding.ts +5 -22
  121. package/src/extensions/index.ts +2 -3
  122. package/src/extensions/markdown/action.ts +0 -1
  123. package/src/extensions/markdown/bundle.ts +23 -9
  124. package/src/extensions/markdown/decorate.ts +15 -12
  125. package/src/extensions/markdown/highlight.ts +15 -7
  126. package/src/extensions/markdown/link.ts +27 -33
  127. package/src/extensions/markdown/parser.test.ts +0 -1
  128. package/src/extensions/markdown/styles.ts +42 -9
  129. package/src/extensions/markdown/table.ts +24 -2
  130. package/src/extensions/outliner/outliner.test.ts +0 -1
  131. package/src/extensions/outliner/outliner.ts +3 -4
  132. package/src/extensions/outliner/tree.test.ts +0 -1
  133. package/src/extensions/preview/preview.ts +55 -8
  134. package/src/extensions/scrolling/auto-scroll.ts +244 -0
  135. package/src/extensions/scrolling/crawler.ts +263 -0
  136. package/src/extensions/scrolling/index.ts +8 -0
  137. package/src/extensions/scrolling/scroll-past-end.ts +32 -0
  138. package/src/extensions/scrolling/scroller.ts +27 -0
  139. package/src/extensions/selection.ts +1 -1
  140. package/src/extensions/snippets.ts +67 -0
  141. package/src/extensions/tags/extended-markdown.test.ts +120 -2
  142. package/src/extensions/tags/extended-markdown.ts +80 -1
  143. package/src/extensions/tags/fader.ts +195 -0
  144. package/src/extensions/tags/index.ts +4 -1
  145. package/src/extensions/tags/testing/text.md +36 -0
  146. package/src/extensions/tags/testing/text.txt +35 -0
  147. package/src/extensions/tags/typewriter.test.ts +65 -0
  148. package/src/extensions/tags/typewriter.ts +594 -0
  149. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  150. package/src/extensions/tags/xml-formatting.ts +125 -0
  151. package/src/extensions/tags/xml-tags.ts +186 -35
  152. package/src/extensions/tags/xml-util.test.ts +199 -24
  153. package/src/extensions/tags/xml-util.ts +62 -5
  154. package/src/index.ts +0 -1
  155. package/src/styles/index.ts +0 -2
  156. package/src/styles/theme.ts +125 -33
  157. package/src/types/types.ts +10 -2
  158. package/src/typings.d.ts +8 -0
  159. package/src/util/cursor.ts +1 -2
  160. package/dist/lib/browser/chunk-HL3YF6WC.mjs +0 -22
  161. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +0 -7
  162. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs +0 -24
  163. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +0 -7
  164. package/dist/types/src/extensions/autoscroll.d.ts +0 -20
  165. package/dist/types/src/extensions/autoscroll.d.ts.map +0 -1
  166. package/dist/types/src/extensions/scrolling.d.ts +0 -78
  167. package/dist/types/src/extensions/scrolling.d.ts.map +0 -1
  168. package/dist/types/src/extensions/tags/streamer.d.ts +0 -12
  169. package/dist/types/src/extensions/tags/streamer.d.ts.map +0 -1
  170. package/dist/types/src/extensions/typewriter.d.ts +0 -10
  171. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  172. package/dist/types/src/styles/markdown.d.ts +0 -8
  173. package/dist/types/src/styles/markdown.d.ts.map +0 -1
  174. package/dist/types/src/styles/tokens.d.ts +0 -3
  175. package/dist/types/src/styles/tokens.d.ts.map +0 -1
  176. package/src/extensions/autoscroll.ts +0 -165
  177. package/src/extensions/scrolling.ts +0 -189
  178. package/src/extensions/tags/streamer.ts +0 -243
  179. package/src/extensions/typewriter.ts +0 -68
  180. package/src/styles/markdown.ts +0 -26
  181. 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) {
@@ -501,233 +511,10 @@ var typeahead = ({ onComplete } = {}) => {
501
511
  ];
502
512
  };
503
513
 
504
- // src/extensions/autoscroll.ts
505
- import { StateEffect as StateEffect2 } from "@codemirror/state";
506
- import { EditorView as EditorView5, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
507
- import { debounce } from "@dxos/async";
508
- import { Domino } from "@dxos/ui";
509
-
510
- // src/extensions/scrolling.ts
511
- import { StateEffect } from "@codemirror/state";
512
- import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
513
- var scrollToLineEffect = StateEffect.define();
514
- var smoothScroll = ({ offset = 0, position = "start" } = {}) => {
515
- const scrollPlugin = ViewPlugin5.fromClass(class SmoothScrollPlugin {
516
- view;
517
- constructor(view) {
518
- this.view = view;
519
- }
520
- // No-op.
521
- destroy() {
522
- }
523
- /**
524
- * Perform smooth scroll to the specified line.
525
- */
526
- scrollToLine(lineNumber, options) {
527
- const { offset: animOffset = 0, position: animPosition, behavior } = options;
528
- const doc = this.view.state.doc;
529
- const scroller = this.view.scrollDOM;
530
- const targetLine = Math.max(0, lineNumber - 1);
531
- if (behavior === "instant") {
532
- requestAnimationFrame(() => {
533
- this.view.dispatch({
534
- selection: {
535
- anchor: doc.line(targetLine + 1).from
536
- },
537
- scrollIntoView: true
538
- });
539
- });
540
- return;
541
- }
542
- if (targetLine >= doc.lines) {
543
- const targetScrollTop2 = scroller.scrollHeight - scroller.clientHeight + (animOffset || 0);
544
- this.animateScroll(scroller, targetScrollTop2);
545
- return;
546
- }
547
- const lineStart = doc.line(targetLine + 1).from;
548
- const coords = this.view.coordsAtPos(lineStart);
549
- if (!coords) {
550
- return;
551
- }
552
- const currentScrollTop = scroller.scrollTop;
553
- const scrollerRect = scroller.getBoundingClientRect();
554
- const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
555
- let targetScrollTop;
556
- if (animPosition === "end") {
557
- targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + animOffset;
558
- } else {
559
- targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + animOffset;
560
- }
561
- const clampedScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
562
- this.animateScroll(scroller, clampedScrollTop);
563
- }
564
- /**
565
- * Animate scroll using browser's built-in smooth scrolling.
566
- */
567
- animateScroll(element, targetScrollTop) {
568
- if (Math.abs(targetScrollTop - element.scrollTop) < 1) {
569
- return;
570
- }
571
- element.scrollTo({
572
- top: targetScrollTop,
573
- behavior: "smooth"
574
- });
575
- }
576
- });
577
- return [
578
- scrollPlugin,
579
- // Update listener to handle scroll effects.
580
- EditorView4.updateListener.of((update2) => {
581
- update2.transactions.forEach((transaction) => {
582
- for (const effect of transaction.effects) {
583
- if (effect.is(scrollToLineEffect)) {
584
- const { line, options = {} } = effect.value;
585
- const plugin = update2.view.plugin(scrollPlugin);
586
- if (plugin) {
587
- plugin.scrollToLine(line, {
588
- offset,
589
- position,
590
- ...options
591
- });
592
- }
593
- }
594
- }
595
- });
596
- })
597
- ];
598
- };
599
- var scrollToLine = (view, line, options) => {
600
- view.dispatch({
601
- effects: scrollToLineEffect.of({
602
- line,
603
- options
604
- })
605
- });
606
- };
607
-
608
- // src/extensions/autoscroll.ts
609
- var scrollToBottomEffect = StateEffect2.define();
610
- var autoScroll = ({ autoScroll: autoScroll2 = true, threshold = 100, throttleDelay = 1e3, onAutoScroll } = {}) => {
611
- let buttonContainer;
612
- let hideTimeout;
613
- let lastScrollTop = 0;
614
- let isPinned = true;
615
- const setPinned = (pin) => {
616
- isPinned = pin;
617
- buttonContainer?.classList.toggle("opacity-0", pin);
618
- };
619
- const hideScrollbar = (view) => {
620
- view.scrollDOM.classList.add("cm-hide-scrollbar");
621
- clearTimeout(hideTimeout);
622
- hideTimeout = setTimeout(() => {
623
- view.scrollDOM.classList.remove("cm-hide-scrollbar");
624
- }, 1e3);
625
- };
626
- const scrollToBottom = (view, behavior) => {
627
- setPinned(true);
628
- hideScrollbar(view);
629
- const line = view.state.doc.lineAt(view.state.doc.length);
630
- view.dispatch({
631
- selection: {
632
- anchor: line.to,
633
- head: line.to
634
- },
635
- effects: scrollToLineEffect.of({
636
- line: line.number,
637
- options: {
638
- position: "end",
639
- offset: threshold,
640
- behavior
641
- }
642
- })
643
- });
644
- };
645
- const checkDistance = debounce((view) => {
646
- const scrollerRect = view.scrollDOM.getBoundingClientRect();
647
- const coords = view.coordsAtPos(view.state.doc.length);
648
- const distanceFromBottom = coords ? coords.bottom - scrollerRect.bottom : 0;
649
- setPinned(distanceFromBottom < 0);
650
- }, 1e3);
651
- const triggerUpdate = debounce((view) => scrollToBottom(view), throttleDelay);
652
- return [
653
- // Update listener for logging when scrolling is needed.
654
- EditorView5.updateListener.of(({ view, transactions, heightChanged }) => {
655
- transactions.forEach((transaction) => {
656
- for (const effect of transaction.effects) {
657
- if (effect.is(scrollToBottomEffect)) {
658
- scrollToBottom(view, effect.value);
659
- }
660
- }
661
- });
662
- if (heightChanged && isPinned) {
663
- const coords = view.coordsAtPos(view.state.doc.length);
664
- const scrollerRect = view.scrollDOM.getBoundingClientRect();
665
- const distanceFromBottom = coords ? scrollerRect.bottom - coords.bottom : 0;
666
- if (autoScroll2 && distanceFromBottom < threshold) {
667
- const shouldScroll = onAutoScroll?.({
668
- view,
669
- distanceFromBottom
670
- }) ?? true;
671
- if (shouldScroll) {
672
- triggerUpdate(view);
673
- }
674
- } else if (distanceFromBottom < 0) {
675
- setPinned(false);
676
- }
677
- }
678
- }),
679
- // Detect user scroll.
680
- EditorView5.domEventHandlers({
681
- scroll: (event, view) => {
682
- const currentScrollTop = view.scrollDOM.scrollTop;
683
- const scrollingUp = currentScrollTop < lastScrollTop;
684
- lastScrollTop = currentScrollTop;
685
- if (scrollingUp) {
686
- setPinned(false);
687
- } else {
688
- checkDistance(view);
689
- }
690
- }
691
- }),
692
- // Scroll button.
693
- ViewPlugin6.fromClass(class {
694
- constructor(view) {
695
- const icon = Domino.of("dx-icon").attributes({
696
- icon: "ph--arrow-down--regular"
697
- });
698
- const button = Domino.of("button").classNames("dx-button bg-accentSurface").attributes({
699
- "data-density": "fine"
700
- }).children(icon).on("click", () => {
701
- scrollToBottom(view);
702
- });
703
- buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").children(button).root;
704
- view.scrollDOM.parentElement.appendChild(buttonContainer);
705
- }
706
- }),
707
- // Styles.
708
- EditorView5.theme({
709
- ".cm-scroller": {
710
- scrollbarWidth: "thin"
711
- },
712
- ".cm-scroller.cm-hide-scrollbar": {
713
- scrollbarWidth: "none"
714
- },
715
- ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
716
- display: "none"
717
- },
718
- ".cm-scroll-button": {
719
- position: "absolute",
720
- bottom: "0.5rem",
721
- right: "1rem"
722
- }
723
- })
724
- ];
725
- };
726
-
727
514
  // src/extensions/automerge/automerge.ts
728
515
  import { next as A3 } from "@automerge/automerge";
729
516
  import { StateField, Transaction as Transaction2 } from "@codemirror/state";
730
- import { EditorView as EditorView6, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
517
+ import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
731
518
  import { DocAccessor } from "@dxos/echo-db";
732
519
 
733
520
  // src/extensions/state.ts
@@ -743,35 +530,25 @@ var cursorConverter = (accessor) => ({
743
530
  try {
744
531
  return toCursor(accessor, pos, assoc);
745
532
  } catch (err) {
746
- log2.catch(err, void 0, {
747
- F: __dxlog_file2,
748
- L: 15,
749
- S: void 0,
750
- C: (f, a) => f(...a)
751
- });
533
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 11, S: void 0 });
752
534
  return "";
753
535
  }
754
536
  },
755
- fromCursor: (cursor2) => {
537
+ fromCursor: (cursor) => {
756
538
  try {
757
- return fromCursor(accessor, cursor2);
539
+ return fromCursor(accessor, cursor);
758
540
  } catch (err) {
759
- log2.catch(err, void 0, {
760
- F: __dxlog_file2,
761
- L: 24,
762
- S: void 0,
763
- C: (f, a) => f(...a)
764
- });
541
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 19, S: void 0 });
765
542
  return 0;
766
543
  }
767
544
  }
768
545
  });
769
546
 
770
547
  // src/extensions/automerge/defs.ts
771
- import { Annotation, StateEffect as StateEffect3 } from "@codemirror/state";
548
+ import { Annotation, StateEffect } from "@codemirror/state";
772
549
  var getPath = (state, field) => state.field(field).path;
773
550
  var getLastHeads = (state, field) => state.field(field).lastHeads;
774
- var updateHeadsEffect = StateEffect3.define({});
551
+ var updateHeadsEffect = StateEffect.define({});
775
552
  var updateHeads = (newHeads) => updateHeadsEffect.of({
776
553
  newHeads
777
554
  });
@@ -946,12 +723,7 @@ var Syncer = class {
946
723
  this._pending = false;
947
724
  }
948
725
  onEditorChange(view) {
949
- log3("onEditorChange", void 0, {
950
- F: __dxlog_file3,
951
- L: 45,
952
- S: this,
953
- C: (f, a) => f(...a)
954
- });
726
+ log3("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 35, S: this });
955
727
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
956
728
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
957
729
  if (newHeads) {
@@ -962,12 +734,7 @@ var Syncer = class {
962
734
  }
963
735
  }
964
736
  onAutomergeChange(view) {
965
- log3("onAutomergeChange", void 0, {
966
- F: __dxlog_file3,
967
- L: 60,
968
- S: this,
969
- C: (f, a) => f(...a)
970
- });
737
+ log3("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 47, S: this });
971
738
  const oldHeads = getLastHeads(view.state, this._state);
972
739
  const newHeads = A2.getHeads(this._handle.doc());
973
740
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1020,7 +787,7 @@ var automerge = (accessor) => {
1020
787
  // Track heads.
1021
788
  syncState,
1022
789
  // Reconcile external updates.
1023
- ViewPlugin7.fromClass(class {
790
+ ViewPlugin5.fromClass(class {
1024
791
  _view;
1025
792
  constructor(_view) {
1026
793
  this._view = _view;
@@ -1029,6 +796,17 @@ var automerge = (accessor) => {
1029
796
  const value = DocAccessor.getValue(accessor);
1030
797
  const current = this._view.state.doc.toString();
1031
798
  if (value !== current) {
799
+ this._view.dispatch({
800
+ changes: {
801
+ from: 0,
802
+ to: this._view.state.doc.length,
803
+ insert: value
804
+ },
805
+ annotations: [
806
+ initialSync,
807
+ reconcileAnnotation.of(true)
808
+ ]
809
+ });
1032
810
  }
1033
811
  });
1034
812
  }
@@ -1040,7 +818,7 @@ var automerge = (accessor) => {
1040
818
  };
1041
819
  }),
1042
820
  // Reconcile local updates.
1043
- EditorView6.updateListener.of(({ view, changes, transactions }) => {
821
+ EditorView4.updateListener.of(({ view, changes, transactions }) => {
1044
822
  if (!changes.empty) {
1045
823
  const isInitialSync = transactions.some((tr) => tr.annotation(Transaction2.userEvent) === initialSync.value);
1046
824
  if (!isInitialSync) {
@@ -1053,7 +831,7 @@ var automerge = (accessor) => {
1053
831
 
1054
832
  // src/extensions/awareness/awareness.ts
1055
833
  import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1056
- import { Decoration as Decoration5, EditorView as EditorView7, ViewPlugin as ViewPlugin8, WidgetType as WidgetType3 } from "@codemirror/view";
834
+ import { Decoration as Decoration5, EditorView as EditorView5, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
1057
835
  import { Event } from "@dxos/async";
1058
836
  import { Context } from "@dxos/context";
1059
837
  var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness.ts";
@@ -1072,17 +850,14 @@ var RemoteSelectionChangedAnnotation = Annotation2.define();
1072
850
  var awareness = (provider = dummyProvider) => {
1073
851
  return [
1074
852
  awarenessProvider.of(provider),
1075
- ViewPlugin8.fromClass(RemoteSelectionsDecorator, {
853
+ ViewPlugin6.fromClass(RemoteSelectionsDecorator, {
1076
854
  decorations: (value) => value.decorations
1077
855
  }),
1078
856
  styles
1079
857
  ];
1080
858
  };
1081
859
  var RemoteSelectionsDecorator = class {
1082
- _ctx = new Context(void 0, {
1083
- F: __dxlog_file4,
1084
- L: 80
1085
- });
860
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 33 });
1086
861
  _cursorConverter;
1087
862
  _provider;
1088
863
  _lastAnchor;
@@ -1231,7 +1006,7 @@ var RemoteCaretWidget = class extends WidgetType3 {
1231
1006
  return true;
1232
1007
  }
1233
1008
  };
1234
- var styles = EditorView7.theme({
1009
+ var styles = EditorView5.theme({
1235
1010
  ".cm-collab-selection": {},
1236
1011
  ".cm-collab-selectionLine": {
1237
1012
  padding: 0,
@@ -1313,10 +1088,7 @@ var SpaceAwarenessProvider = class {
1313
1088
  this._info = info;
1314
1089
  }
1315
1090
  open() {
1316
- this._ctx = new Context2(void 0, {
1317
- F: __dxlog_file5,
1318
- L: 57
1319
- });
1091
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 28 });
1320
1092
  this._postTask = new DeferredTask(this._ctx, async () => {
1321
1093
  if (this._localState) {
1322
1094
  await this._messenger.postMessage(this._channel, {
@@ -1343,12 +1115,7 @@ var SpaceAwarenessProvider = class {
1343
1115
  }).catch((err) => {
1344
1116
  log4.debug("failed to query awareness", {
1345
1117
  err
1346
- }, {
1347
- F: __dxlog_file5,
1348
- L: 91,
1349
- S: this,
1350
- C: (f, a) => f(...a)
1351
- });
1118
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 57, S: this });
1352
1119
  });
1353
1120
  }
1354
1121
  close() {
@@ -1360,15 +1127,7 @@ var SpaceAwarenessProvider = class {
1360
1127
  return Array.from(this._remoteStates.values());
1361
1128
  }
1362
1129
  update(position) {
1363
- invariant(this._postTask, void 0, {
1364
- F: __dxlog_file5,
1365
- L: 106,
1366
- S: this,
1367
- A: [
1368
- "this._postTask",
1369
- ""
1370
- ]
1371
- });
1130
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 71, S: this, A: ["this._postTask", ""] });
1372
1131
  this._localState = {
1373
1132
  peerId: this._peerId,
1374
1133
  position,
@@ -1377,34 +1136,18 @@ var SpaceAwarenessProvider = class {
1377
1136
  this._postTask.schedule();
1378
1137
  }
1379
1138
  _handleQueryMessage() {
1380
- invariant(this._postTask, void 0, {
1381
- F: __dxlog_file5,
1382
- L: 117,
1383
- S: this,
1384
- A: [
1385
- "this._postTask",
1386
- ""
1387
- ]
1388
- });
1139
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 80, S: this, A: ["this._postTask", ""] });
1389
1140
  this._postTask.schedule();
1390
1141
  }
1391
1142
  _handlePostMessage(message) {
1392
- invariant(message.kind === "post", void 0, {
1393
- F: __dxlog_file5,
1394
- L: 122,
1395
- S: this,
1396
- A: [
1397
- "message.kind === 'post'",
1398
- ""
1399
- ]
1400
- });
1143
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1401
1144
  this._remoteStates.set(message.state.peerId, message.state);
1402
1145
  this.remoteStateChange.emit();
1403
1146
  }
1404
1147
  };
1405
1148
 
1406
1149
  // src/extensions/blast.ts
1407
- import { EditorView as EditorView8, keymap as keymap3 } from "@codemirror/view";
1150
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1408
1151
  import defaultsDeep from "lodash.defaultsdeep";
1409
1152
  import { throttle } from "@dxos/async";
1410
1153
  import { invariant as invariant2 } from "@dxos/invariant";
@@ -1453,7 +1196,7 @@ var blast = (options = defaultOptions) => {
1453
1196
  };
1454
1197
  return [
1455
1198
  // Cursor moved.
1456
- EditorView8.updateListener.of((update2) => {
1199
+ EditorView6.updateListener.of((update2) => {
1457
1200
  if (blaster?.node !== update2.view.scrollDOM) {
1458
1201
  if (blaster) {
1459
1202
  blaster.destroy();
@@ -1526,15 +1269,7 @@ var Blaster = class {
1526
1269
  return this._node;
1527
1270
  }
1528
1271
  initialize() {
1529
- invariant2(!this._canvas && !this._ctx, void 0, {
1530
- F: __dxlog_file6,
1531
- L: 142,
1532
- S: this,
1533
- A: [
1534
- "!this._canvas && !this._ctx",
1535
- ""
1536
- ]
1537
- });
1272
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1538
1273
  this._canvas = document.createElement("canvas");
1539
1274
  this._canvas.id = "code-blast-canvas";
1540
1275
  this._canvas.style.position = "absolute";
@@ -1563,15 +1298,7 @@ var Blaster = class {
1563
1298
  }
1564
1299
  }
1565
1300
  start() {
1566
- invariant2(this._canvas && this._ctx, void 0, {
1567
- F: __dxlog_file6,
1568
- L: 181,
1569
- S: this,
1570
- A: [
1571
- "this._canvas && this._ctx",
1572
- ""
1573
- ]
1574
- });
1301
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1575
1302
  this._running = true;
1576
1303
  this.loop();
1577
1304
  }
@@ -1711,9 +1438,9 @@ var random = (min, max) => {
1711
1438
 
1712
1439
  // src/extensions/blocks.ts
1713
1440
  import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1714
- import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin9 } from "@codemirror/view";
1441
+ import { Decoration as Decoration6, EditorView as EditorView7, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
1715
1442
  import { mx as mx2 } from "@dxos/ui-theme";
1716
- var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1443
+ var paragraphBlockPlugin = ViewPlugin7.fromClass(class {
1717
1444
  decorations;
1718
1445
  constructor(view) {
1719
1446
  this.decorations = this.build(view);
@@ -1772,15 +1499,15 @@ var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1772
1499
  });
1773
1500
  var blocks = () => [
1774
1501
  paragraphBlockPlugin,
1775
- EditorView9.baseTheme({
1502
+ EditorView7.baseTheme({
1776
1503
  ".cm-line.block-line": {
1777
1504
  paddingLeft: "0.75rem",
1778
1505
  paddingRight: "0.75rem",
1779
- borderLeft: "1px solid var(--dx-subduedSeparator)",
1780
- borderRight: "1px solid var(--dx-subduedSeparator)"
1506
+ borderLeft: "1px solid var(--color-subdued-separator)",
1507
+ borderRight: "1px solid var(--color-subdued-separator)"
1781
1508
  },
1782
1509
  ".cm-line.block-single": {
1783
- border: "1px solid var(--dx-subduedSeparator)",
1510
+ border: "1px solid var(--color-subdued-separator)",
1784
1511
  borderRadius: "6px",
1785
1512
  paddingTop: "0.5rem",
1786
1513
  paddingBottom: "0.5rem",
@@ -1788,7 +1515,7 @@ var blocks = () => [
1788
1515
  marginBottom: "0.5rem"
1789
1516
  },
1790
1517
  ".cm-line.block-first": {
1791
- borderTop: "1px solid var(--dx-subduedSeparator)",
1518
+ borderTop: "1px solid var(--color-subdued-separator)",
1792
1519
  borderTopLeftRadius: "6px",
1793
1520
  borderTopRightRadius: "6px",
1794
1521
  paddingTop: "0.5rem",
@@ -1796,7 +1523,7 @@ var blocks = () => [
1796
1523
  },
1797
1524
  ".cm-line.block-middle": {},
1798
1525
  ".cm-line.block-last": {
1799
- borderBottom: "1px solid var(--dx-subduedSeparator)",
1526
+ borderBottom: "1px solid var(--color-subdued-separator)",
1800
1527
  borderBottomLeftRadius: "6px",
1801
1528
  borderBottomRightRadius: "6px",
1802
1529
  paddingBottom: "0.5rem",
@@ -1806,13 +1533,13 @@ var blocks = () => [
1806
1533
  ];
1807
1534
 
1808
1535
  // src/extensions/bookmarks.ts
1809
- import { Prec as Prec3, StateEffect as StateEffect4, StateField as StateField2 } from "@codemirror/state";
1536
+ import { Prec as Prec3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1810
1537
  import { keymap as keymap4 } from "@codemirror/view";
1811
1538
  import { log as log5 } from "@dxos/log";
1812
1539
  var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1813
- var addBookmark = StateEffect4.define();
1814
- var removeBookmark = StateEffect4.define();
1815
- var clearBookmarks = StateEffect4.define();
1540
+ var addBookmark = StateEffect2.define();
1541
+ var removeBookmark = StateEffect2.define();
1542
+ var clearBookmarks = StateEffect2.define();
1816
1543
  var bookmarks = () => {
1817
1544
  return [
1818
1545
  bookmarksField,
@@ -1821,12 +1548,7 @@ var bookmarks = () => {
1821
1548
  key: "Mod-ArrowUp",
1822
1549
  run: (view) => {
1823
1550
  const bookmarks2 = view.state.field(bookmarksField);
1824
- log5("up", bookmarks2, {
1825
- F: __dxlog_file7,
1826
- L: 29,
1827
- S: void 0,
1828
- C: (f, a) => f(...a)
1829
- });
1551
+ log5("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 18, S: void 0 });
1830
1552
  return true;
1831
1553
  }
1832
1554
  },
@@ -1834,12 +1556,7 @@ var bookmarks = () => {
1834
1556
  key: "Mod-ArrowDown",
1835
1557
  run: (view) => {
1836
1558
  const bookmarks2 = view.state.field(bookmarksField);
1837
- log5("down", bookmarks2, {
1838
- F: __dxlog_file7,
1839
- L: 37,
1840
- S: void 0,
1841
- C: (f, a) => f(...a)
1842
- });
1559
+ log5("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 26, S: void 0 });
1843
1560
  return true;
1844
1561
  }
1845
1562
  }
@@ -1876,27 +1593,27 @@ var bookmarksField = StateField2.define({
1876
1593
 
1877
1594
  // src/extensions/comments.ts
1878
1595
  import { invertedEffects } from "@codemirror/commands";
1879
- import { StateEffect as StateEffect5, StateField as StateField3 } from "@codemirror/state";
1880
- import { Decoration as Decoration7, EditorView as EditorView11, ViewPlugin as ViewPlugin10, hoverTooltip, keymap as keymap6 } from "@codemirror/view";
1596
+ import { StateEffect as StateEffect3, StateField as StateField3 } from "@codemirror/state";
1597
+ import { Decoration as Decoration7, EditorView as EditorView9, ViewPlugin as ViewPlugin8, hoverTooltip, keymap as keymap6 } from "@codemirror/view";
1881
1598
  import sortBy from "lodash.sortby";
1882
- import { debounce as debounce3 } from "@dxos/async";
1599
+ import { debounce as debounce2 } from "@dxos/async";
1883
1600
  import { log as log6 } from "@dxos/log";
1884
1601
  import { isNonNullable } from "@dxos/util";
1885
1602
 
1886
1603
  // src/extensions/selection.ts
1887
1604
  import { Transaction as Transaction3 } from "@codemirror/state";
1888
- import { EditorView as EditorView10, keymap as keymap5 } from "@codemirror/view";
1889
- import { debounce as debounce2 } from "@dxos/async";
1605
+ import { EditorView as EditorView8, keymap as keymap5 } from "@codemirror/view";
1606
+ import { debounce } from "@dxos/async";
1890
1607
  import { invariant as invariant3 } from "@dxos/invariant";
1891
1608
  import { isTruthy } from "@dxos/util";
1892
1609
  var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/selection.ts";
1893
1610
  var documentId = singleValueFacet();
1894
- var stateRestoreAnnotation = "dxos.org/cm/state-restore";
1611
+ var stateRestoreAnnotation = "org.dxos.cm.state-restore";
1895
1612
  var createEditorStateTransaction = ({ scrollTo, selection }) => {
1896
1613
  return {
1897
1614
  selection,
1898
1615
  scrollIntoView: !scrollTo,
1899
- effects: scrollTo ? EditorView10.scrollIntoView(scrollTo, {
1616
+ effects: scrollTo ? EditorView8.scrollIntoView(scrollTo, {
1900
1617
  yMargin: 96
1901
1618
  }) : void 0,
1902
1619
  annotations: Transaction3.userEvent.of(stateRestoreAnnotation)
@@ -1904,33 +1621,17 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1904
1621
  };
1905
1622
  var createEditorStateStore = (keyPrefix) => ({
1906
1623
  getState: (id) => {
1907
- invariant3(id, void 0, {
1908
- F: __dxlog_file8,
1909
- L: 47,
1910
- S: void 0,
1911
- A: [
1912
- "id",
1913
- ""
1914
- ]
1915
- });
1624
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0, A: ["id", ""] });
1916
1625
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
1917
1626
  return state ? JSON.parse(state) : void 0;
1918
1627
  },
1919
1628
  setState: (id, state) => {
1920
- invariant3(id, void 0, {
1921
- F: __dxlog_file8,
1922
- L: 53,
1923
- S: void 0,
1924
- A: [
1925
- "id",
1926
- ""
1927
- ]
1928
- });
1629
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 31, S: void 0, A: ["id", ""] });
1929
1630
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
1930
1631
  }
1931
1632
  });
1932
1633
  var selectionState = ({ getState, setState } = {}) => {
1933
- const setStateDebounced = debounce2(setState, 1e3);
1634
+ const setStateDebounced = debounce(setState, 1e3);
1934
1635
  return [
1935
1636
  // TODO(burdon): Track scrolling (currently only updates when cursor moves).
1936
1637
  // EditorView.domEventHandlers({
@@ -1938,7 +1639,7 @@ var selectionState = ({ getState, setState } = {}) => {
1938
1639
  // setStateDebounced(id, {});
1939
1640
  // },
1940
1641
  // }),
1941
- EditorView10.updateListener.of(({ view, transactions }) => {
1642
+ EditorView8.updateListener.of(({ view, transactions }) => {
1942
1643
  const id = view.state.facet(documentId);
1943
1644
  if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
1944
1645
  return;
@@ -1978,9 +1679,9 @@ var selectionState = ({ getState, setState } = {}) => {
1978
1679
 
1979
1680
  // src/extensions/comments.ts
1980
1681
  var __dxlog_file9 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
1981
- var setComments = StateEffect5.define();
1982
- var setSelection = StateEffect5.define();
1983
- var setCommentState = StateEffect5.define();
1682
+ var setComments = StateEffect3.define();
1683
+ var setSelection = StateEffect3.define();
1684
+ var setCommentState = StateEffect3.define();
1984
1685
  var commentsState = StateField3.define({
1985
1686
  create: (state) => ({
1986
1687
  id: state.facet(documentId),
@@ -2019,17 +1720,17 @@ var commentsState = StateField3.define({
2019
1720
  return value;
2020
1721
  }
2021
1722
  });
2022
- var styles2 = EditorView11.theme({
1723
+ var styles2 = EditorView9.theme({
2023
1724
  ".cm-comment, .cm-comment-current": {
2024
1725
  padding: "3px 0",
2025
- color: "var(--dx-cmCommentText)",
2026
- backgroundColor: "var(--dx-cmCommentSurface)"
1726
+ color: "var(--color-cm-comment-text)",
1727
+ backgroundColor: "var(--color-cm-comment-surface)"
2027
1728
  },
2028
1729
  ".cm-comment > span, .cm-comment-current > span": {
2029
1730
  boxDecorationBreak: "clone",
2030
- boxShadow: "0 0 1px 3px var(--dx-cmCommentSurface)",
2031
- backgroundColor: "var(--dx-cmCommentSurface)",
2032
- color: "var(--dx-cmCommentText)",
1731
+ boxShadow: "0 0 1px 3px var(--color-cm-comment-surface)",
1732
+ backgroundColor: "var(--color-cm-comment-surface)",
1733
+ color: "var(--color-cm-comment-text)",
2033
1734
  cursor: "pointer"
2034
1735
  }
2035
1736
  });
@@ -2040,19 +1741,14 @@ var createCommentMark = (id, isCurrent) => Decoration7.mark({
2040
1741
  "data-comment-id": id
2041
1742
  }
2042
1743
  });
2043
- var commentsDecorations = EditorView11.decorations.compute([
1744
+ var commentsDecorations = EditorView9.decorations.compute([
2044
1745
  commentsState
2045
1746
  ], (state) => {
2046
1747
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
2047
1748
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2048
1749
  const range = comment.range;
2049
1750
  if (!range) {
2050
- log6.warn("Invalid range:", range, {
2051
- F: __dxlog_file9,
2052
- L: 140,
2053
- S: void 0,
2054
- C: (f, a) => f(...a)
2055
- });
1751
+ log6.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 93, S: void 0 });
2056
1752
  return void 0;
2057
1753
  } else if (range.from === range.to) {
2058
1754
  return void 0;
@@ -2062,8 +1758,8 @@ var commentsDecorations = EditorView11.decorations.compute([
2062
1758
  }).filter(isNonNullable);
2063
1759
  return Decoration7.set(decorations2);
2064
1760
  });
2065
- var commentClickedEffect = StateEffect5.define();
2066
- var handleCommentClick = EditorView11.domEventHandlers({
1761
+ var commentClickedEffect = StateEffect3.define();
1762
+ var handleCommentClick = EditorView9.domEventHandlers({
2067
1763
  click: (event, view) => {
2068
1764
  let target = event.target;
2069
1765
  const editorRoot = view.dom;
@@ -2102,7 +1798,7 @@ var trackPastedComments = (onUpdate) => {
2102
1798
  }
2103
1799
  };
2104
1800
  return [
2105
- EditorView11.domEventHandlers({
1801
+ EditorView9.domEventHandlers({
2106
1802
  cut: handleTrack,
2107
1803
  copy: handleTrack
2108
1804
  }),
@@ -2124,7 +1820,7 @@ var trackPastedComments = (onUpdate) => {
2124
1820
  return effects;
2125
1821
  }),
2126
1822
  // Handle paste or the undo of comment deletion.
2127
- EditorView11.updateListener.of((update2) => {
1823
+ EditorView9.updateListener.of((update2) => {
2128
1824
  const restore = [];
2129
1825
  for (let i = 0; i < update2.transactions.length; i++) {
2130
1826
  const tr = update2.transactions[i];
@@ -2165,10 +1861,10 @@ var trackPastedComments = (onUpdate) => {
2165
1861
  const { comments: comments2 } = update2.startState.field(commentsState);
2166
1862
  const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
2167
1863
  if (!exists) {
2168
- const cursor2 = Cursor.getCursorFromRange(update2.state, comment);
1864
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
2169
1865
  onUpdate({
2170
1866
  id: comment.id,
2171
- cursor: cursor2
1867
+ cursor
2172
1868
  });
2173
1869
  }
2174
1870
  }
@@ -2180,7 +1876,7 @@ var mapTrackedComment = (comment, changes) => ({
2180
1876
  from: changes.mapPos(comment.from, 1),
2181
1877
  to: changes.mapPos(comment.to, 1)
2182
1878
  });
2183
- var restoreCommentEffect = StateEffect5.define({
1879
+ var restoreCommentEffect = StateEffect3.define({
2184
1880
  map: mapTrackedComment
2185
1881
  });
2186
1882
  var createComment = (view) => {
@@ -2197,13 +1893,13 @@ var createComment = (view) => {
2197
1893
  }
2198
1894
  });
2199
1895
  }
2200
- const cursor2 = Cursor.getCursorFromRange(view.state, {
1896
+ const cursor = Cursor.getCursorFromRange(view.state, {
2201
1897
  from,
2202
1898
  to
2203
1899
  });
2204
- if (cursor2) {
1900
+ if (cursor) {
2205
1901
  options.onCreate?.({
2206
- cursor: cursor2,
1902
+ cursor,
2207
1903
  from,
2208
1904
  location: view.coordsAtPos(from)
2209
1905
  });
@@ -2214,7 +1910,7 @@ var createComment = (view) => {
2214
1910
  var optionsFacet = singleValueFacet();
2215
1911
  var comments = (options = {}) => {
2216
1912
  const { key: shortcut = "meta-'" } = options;
2217
- const handleSelect = debounce3((state) => options.onSelect?.(state), 200);
1913
+ const handleSelect = debounce2((state) => options.onSelect?.(state), 200);
2218
1914
  return [
2219
1915
  optionsFacet.of(options),
2220
1916
  options.id ? documentId.of(options.id) : void 0,
@@ -2266,7 +1962,7 @@ var comments = (options = {}) => {
2266
1962
  //
2267
1963
  // Track deleted ranges and update ranges for decorations.
2268
1964
  //
2269
- EditorView11.updateListener.of(({ view, state, changes }) => {
1965
+ EditorView9.updateListener.of(({ view, state, changes }) => {
2270
1966
  let mod = false;
2271
1967
  const { comments: comments2, ...value } = state.field(commentsState);
2272
1968
  changes.iterChanges((from, to, from2, to2) => {
@@ -2298,7 +1994,7 @@ var comments = (options = {}) => {
2298
1994
  //
2299
1995
  // Track selection/proximity.
2300
1996
  //
2301
- EditorView11.updateListener.of(({ view, state }) => {
1997
+ EditorView9.updateListener.of(({ view, state }) => {
2302
1998
  let min = Infinity;
2303
1999
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
2304
2000
  const { head } = state.selection.main;
@@ -2352,7 +2048,7 @@ var scrollThreadIntoView = (view, id, center = true) => {
2352
2048
  anchor: range.from
2353
2049
  } : void 0,
2354
2050
  effects: [
2355
- needsScroll ? EditorView11.scrollIntoView(range.from, center ? {
2051
+ needsScroll ? EditorView9.scrollIntoView(range.from, center ? {
2356
2052
  y: "center"
2357
2053
  } : void 0) : [],
2358
2054
  needsSelectionUpdate ? setSelection.of({
@@ -2383,7 +2079,7 @@ var ExternalCommentSync = class {
2383
2079
  this.unsubscribe();
2384
2080
  };
2385
2081
  };
2386
- var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin10.fromClass(class {
2082
+ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin8.fromClass(class {
2387
2083
  constructor(view) {
2388
2084
  return new ExternalCommentSync(view, id, subscribe, getComments);
2389
2085
  }
@@ -2392,9 +2088,9 @@ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin10.fro
2392
2088
  // src/extensions/debug.ts
2393
2089
  import { syntaxTree } from "@codemirror/language";
2394
2090
  import { StateField as StateField4 } from "@codemirror/state";
2395
- var debugNodeLogger = (log11 = console.log) => {
2091
+ var debugNodeLogger = (log12 = console.log) => {
2396
2092
  const logTokens = (state) => syntaxTree(state).iterate({
2397
- enter: (node) => log11(node.type)
2093
+ enter: (node) => log12(node.type)
2398
2094
  });
2399
2095
  return StateField4.define({
2400
2096
  create: (state) => logTokens(state),
@@ -2403,12 +2099,12 @@ var debugNodeLogger = (log11 = console.log) => {
2403
2099
  };
2404
2100
 
2405
2101
  // src/extensions/dnd.ts
2406
- import { EditorView as EditorView12, dropCursor } from "@codemirror/view";
2102
+ import { EditorView as EditorView10, dropCursor } from "@codemirror/view";
2407
2103
  var dropFile = (options = {}) => {
2408
2104
  return [
2409
2105
  styles3,
2410
2106
  dropCursor(),
2411
- EditorView12.domEventHandlers({
2107
+ EditorView10.domEventHandlers({
2412
2108
  drop: (event, view) => {
2413
2109
  event.preventDefault();
2414
2110
  const files = event.dataTransfer?.files;
@@ -2427,10 +2123,10 @@ var dropFile = (options = {}) => {
2427
2123
  })
2428
2124
  ];
2429
2125
  };
2430
- var styles3 = EditorView12.theme({
2126
+ var styles3 = EditorView10.theme({
2431
2127
  ".cm-dropCursor": {
2432
- borderLeft: "2px solid var(--dx-accentText)",
2433
- color: "var(--dx-accentText)",
2128
+ borderLeft: "2px solid var(--color-accent-text)",
2129
+ color: "var(--color-accent-text)",
2434
2130
  padding: "0 4px"
2435
2131
  },
2436
2132
  ".cm-dropCursor:after": {
@@ -2444,47 +2140,66 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap }
2444
2140
  import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
2445
2141
  import { searchKeymap } from "@codemirror/search";
2446
2142
  import { EditorState } from "@codemirror/state";
2447
- import { EditorView as EditorView15, ViewPlugin as ViewPlugin11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2, scrollPastEnd } from "@codemirror/view";
2143
+ import { EditorView as EditorView16, ViewPlugin as ViewPlugin12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2 } from "@codemirror/view";
2448
2144
  import { vscodeDarkStyle, vscodeLightStyle } from "@uiw/codemirror-theme-vscode";
2449
2145
  import defaultsDeep2 from "lodash.defaultsdeep";
2450
2146
  import { generateName } from "@dxos/display-name";
2451
- import { log as log7 } from "@dxos/log";
2452
- import { hexToHue, isTruthy as isTruthy2 } from "@dxos/util";
2147
+ import { log as log8 } from "@dxos/log";
2148
+ import { hexToHue, isTruthy as isTruthy3 } from "@dxos/util";
2453
2149
 
2454
- // src/styles/markdown.ts
2150
+ // src/styles/theme.ts
2151
+ import { EditorView as EditorView11 } from "@codemirror/view";
2455
2152
  import { mx as mx3 } from "@dxos/ui-theme";
2456
2153
  var headings = {
2457
- 1: "text-4xl",
2458
- 2: "text-3xl",
2459
- 3: "text-2xl",
2460
- 4: "text-xl",
2461
- 5: "text-lg",
2462
- 6: ""
2154
+ 1: {
2155
+ className: "text-3xl",
2156
+ fontSize: "var(--text-3xl)",
2157
+ lineHeight: "var(--text-4xl--line-height)"
2158
+ },
2159
+ 2: {
2160
+ className: "text-2xl",
2161
+ fontSize: "var(--text-2xl)",
2162
+ lineHeight: "var(--text-3xl--line-height)"
2163
+ },
2164
+ 3: {
2165
+ className: "text-xl",
2166
+ fontSize: "var(--text-xl)",
2167
+ lineHeight: "var(--text-2xl--line-height)"
2168
+ },
2169
+ 4: {
2170
+ className: "text-lg",
2171
+ fontSize: "var(--text-lg)",
2172
+ lineHeight: "var(--text-xl--line-height)"
2173
+ },
2174
+ 5: {
2175
+ className: "text-base",
2176
+ fontSize: "var(--text-base)",
2177
+ lineHeight: "var(--text-lg--line-height)"
2178
+ },
2179
+ 6: {
2180
+ className: "text-base",
2181
+ fontSize: "var(--text-base)",
2182
+ lineHeight: "var(--text-base--line-height)"
2183
+ }
2463
2184
  };
2185
+ var fontBody = '"Inter Variable", ui-sans-serif, system-ui, sans-serif';
2186
+ var fontMono = '"JetBrains Mono Variable", ui-monospace, "Cascadia Code", "Source Code Pro", monospace';
2464
2187
  var markdownTheme = {
2465
- code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
2466
- codeMark: "font-mono text-primary-500",
2467
- mark: "opacity-50",
2468
- heading: (level) => {
2469
- return mx3(headings[level], "dark:text-neutral-400");
2470
- }
2188
+ code: "font-mono! cm-code-inline",
2189
+ codeMark: "font-mono! cm-code-mark",
2190
+ mark: "font-mono!",
2191
+ heading: (level) => ({
2192
+ className: mx3(headings[level].className, "font-light text-(--color-cm-heading-number)"),
2193
+ color: "var(--color-cm-heading) !important",
2194
+ lineHeight: headings[level].lineHeight,
2195
+ fontSize: headings[level].fontSize,
2196
+ fontWeight: "100 !important"
2197
+ })
2471
2198
  };
2472
-
2473
- // src/styles/theme.ts
2474
- import { EditorView as EditorView13 } from "@codemirror/view";
2475
-
2476
- // src/styles/tokens.ts
2477
- import { tokens } from "@dxos/ui-theme";
2478
- import { get } from "@dxos/util";
2479
- var getToken = (path, defaultValue) => {
2480
- const value = get(tokens, path, defaultValue);
2481
- return value?.toString() ?? "";
2482
- };
2483
- var fontBody = getToken("fontFamily.body");
2484
- var fontMono = getToken("fontFamily.mono");
2485
-
2486
- // src/styles/theme.ts
2487
- var baseTheme = EditorView13.baseTheme({
2199
+ var baseTheme = EditorView11.baseTheme({
2200
+ /**
2201
+ * Outer frame.
2202
+ */
2488
2203
  "&": {},
2489
2204
  "&.cm-focused": {
2490
2205
  outline: "none"
@@ -2493,7 +2208,27 @@ var baseTheme = EditorView13.baseTheme({
2493
2208
  * Scroller
2494
2209
  */
2495
2210
  ".cm-scroller": {
2496
- overflowY: "auto"
2211
+ // Browser scroll-anchoring: see comment in `scrolling/crawler.ts`. `auto` lets the browser pin a
2212
+ // stable element near the viewport top so widget resizes (e.g. tool-block TogglePanel
2213
+ // open/close) don't jump the user's view.
2214
+ overflowAnchor: "auto"
2215
+ },
2216
+ ".cm-scroller::-webkit-scrollbar": {
2217
+ width: "var(--scrollbar-size,8px)",
2218
+ height: "var(--scrollbar-size,8px)"
2219
+ },
2220
+ ".cm-scroller::-webkit-scrollbar-corner": {
2221
+ background: "transparent"
2222
+ },
2223
+ ".cm-scroller::-webkit-scrollbar-track": {
2224
+ background: "transparent"
2225
+ },
2226
+ ".cm-scroller::-webkit-scrollbar-thumb": {
2227
+ background: "transparent",
2228
+ transition: "background 0.15s"
2229
+ },
2230
+ "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
2231
+ background: "var(--color-scrollbar-thumb)"
2497
2232
  },
2498
2233
  /**
2499
2234
  * Content
@@ -2501,7 +2236,6 @@ var baseTheme = EditorView13.baseTheme({
2501
2236
  */
2502
2237
  ".cm-content": {
2503
2238
  padding: "unset",
2504
- lineHeight: "24px",
2505
2239
  color: "unset"
2506
2240
  },
2507
2241
  /**
@@ -2515,8 +2249,8 @@ var baseTheme = EditorView13.baseTheme({
2515
2249
  ".cm-gutter": {},
2516
2250
  ".cm-gutter.cm-lineNumbers": {
2517
2251
  paddingRight: "4px",
2518
- borderRight: "1px solid var(--dx-subduedSeparator)",
2519
- color: "var(--dx-subduedText)"
2252
+ borderRight: "1px solid var(--color-subdued-separator)",
2253
+ color: "var(--color-subdued)"
2520
2254
  },
2521
2255
  ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
2522
2256
  minWidth: "40px"
@@ -2532,29 +2266,36 @@ var baseTheme = EditorView13.baseTheme({
2532
2266
  * Line.
2533
2267
  */
2534
2268
  ".cm-line": {
2535
- lineHeight: "24px",
2269
+ lineHeight: 1.5,
2536
2270
  paddingInline: 0
2537
2271
  },
2272
+ /**
2273
+ * Force all inline children to inherit line-height to prevent monospace font metrics
2274
+ * (JetBrains Mono ascent/descent) from inflating the line box beyond 24px.
2275
+ */
2276
+ ".cm-line *": {
2277
+ lineHeight: "inherit"
2278
+ },
2538
2279
  ".cm-activeLine": {
2539
- background: "var(--dx-cmActiveLine)"
2280
+ background: "var(--color-cm-active-line)"
2540
2281
  },
2541
2282
  /**
2542
2283
  * Cursor (layer).
2543
2284
  */
2544
2285
  ".cm-cursor, .cm-dropCursor": {
2545
- borderLeft: "2px solid var(--dx-cmCursor)"
2286
+ borderLeft: "2px solid var(--color-cm-cursor)"
2546
2287
  },
2547
2288
  ".cm-placeholder": {
2548
- color: "var(--dx-placeholder)"
2289
+ color: "var(--color-placeholder)"
2549
2290
  },
2550
2291
  /**
2551
2292
  * Selection (layer).
2552
2293
  */
2553
2294
  ".cm-selectionBackground": {
2554
- background: "var(--dx-cmSelection)"
2295
+ background: "var(--color-cm-selection)"
2555
2296
  },
2556
2297
  "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
2557
- background: "var(--dx-cmFocusedSelection)"
2298
+ background: "var(--color-cm-focused-selection)"
2558
2299
  },
2559
2300
  /**
2560
2301
  * Search.
@@ -2564,8 +2305,8 @@ var baseTheme = EditorView13.baseTheme({
2564
2305
  margin: "0 -3px",
2565
2306
  padding: "3px",
2566
2307
  borderRadius: "3px",
2567
- background: "var(--dx-cmHighlightSurface)",
2568
- color: "var(--dx-cmHighlight)"
2308
+ background: "var(--color-cm-highlight-surface)",
2309
+ color: "var(--color-cm-highlight)"
2569
2310
  },
2570
2311
  ".cm-searchMatch-selected": {
2571
2312
  textDecoration: "underline"
@@ -2576,20 +2317,30 @@ var baseTheme = EditorView13.baseTheme({
2576
2317
  ".cm-link": {
2577
2318
  textDecorationLine: "underline",
2578
2319
  textDecorationThickness: "1px",
2579
- textDecorationColor: "var(--dx-separator)",
2320
+ textDecorationColor: "var(--color-separator)",
2580
2321
  textUnderlineOffset: "2px",
2581
- borderRadius: ".125rem"
2322
+ borderRadius: ".125rem",
2323
+ cursor: "pointer"
2582
2324
  },
2583
2325
  ".cm-link > span": {
2584
- color: "var(--dx-accentText)"
2326
+ color: "var(--color-accent-text)"
2327
+ },
2328
+ ".cm-link > span:hover": {
2329
+ color: "var(--color-accent-text-hover)"
2585
2330
  },
2586
2331
  /**
2587
2332
  * Tooltip.
2588
2333
  */
2589
2334
  ".cm-tooltip": {
2590
- background: "var(--dx-baseSurface)"
2335
+ background: "var(--color-modal-surface)"
2591
2336
  },
2592
2337
  ".cm-tooltip-below": {},
2338
+ ".cm-tooltip-hover": {
2339
+ background: "var(--color-modal-surface)",
2340
+ border: "1px solid var(--color-separator)",
2341
+ borderRadius: "4px",
2342
+ overflow: "hidden"
2343
+ },
2593
2344
  /**
2594
2345
  * Autocomplete.
2595
2346
  * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
@@ -2597,7 +2348,7 @@ var baseTheme = EditorView13.baseTheme({
2597
2348
  ".cm-tooltip.cm-tooltip-autocomplete": {
2598
2349
  marginTop: "6px",
2599
2350
  marginLeft: "-10px",
2600
- border: "2px solid var(--dx-separator)",
2351
+ border: "2px solid var(--color-separator)",
2601
2352
  borderRadius: "4px"
2602
2353
  },
2603
2354
  ".cm-tooltip.cm-tooltip-autocomplete > ul": {
@@ -2607,12 +2358,12 @@ var baseTheme = EditorView13.baseTheme({
2607
2358
  padding: "4px"
2608
2359
  },
2609
2360
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2610
- background: "var(--dx-activeSurface)",
2611
- color: "var(--dx-activeSurfaceText)"
2361
+ background: "var(--color-current-surface)",
2362
+ color: "var(--color-base-foreground)"
2612
2363
  },
2613
2364
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2614
2365
  paddingLeft: "4px !important",
2615
- color: "var(--dx-hoverSurfaceText)"
2366
+ color: "var(--color-base-foreground)"
2616
2367
  },
2617
2368
  /**
2618
2369
  * Completion info.
@@ -2621,17 +2372,17 @@ var baseTheme = EditorView13.baseTheme({
2621
2372
  width: "360px !important",
2622
2373
  margin: "-10px 1px 0 1px",
2623
2374
  padding: "8px !important",
2624
- borderColor: "var(--dx-separator)"
2375
+ borderColor: "var(--color-separator)"
2625
2376
  },
2626
2377
  ".cm-completionIcon": {
2627
2378
  display: "none"
2628
2379
  },
2629
2380
  ".cm-completionLabel": {
2630
- color: "var(--dx-description)",
2381
+ color: "var(--color-description)",
2631
2382
  padding: "0 4px"
2632
2383
  },
2633
2384
  ".cm-completionMatchedText": {
2634
- color: "var(--dx-baseText)",
2385
+ color: "var(--color-base-foreground)",
2635
2386
  textDecoration: "none !important"
2636
2387
  },
2637
2388
  /**
@@ -2655,7 +2406,7 @@ var baseTheme = EditorView13.baseTheme({
2655
2406
  backgroundColor: "var(--surface-bg)"
2656
2407
  },
2657
2408
  ".cm-panel input, .cm-panel button, .cm-panel label": {
2658
- color: "var(--dx-subdued)",
2409
+ color: "var(--color-subdued)",
2659
2410
  fontSize: "14px",
2660
2411
  all: "unset",
2661
2412
  margin: "3px !important",
@@ -2663,10 +2414,10 @@ var baseTheme = EditorView13.baseTheme({
2663
2414
  outline: "1px solid transparent"
2664
2415
  },
2665
2416
  ".cm-panel input, .cm-panel button": {
2666
- backgroundColor: "var(--dx-inputSurface)"
2417
+ backgroundColor: "var(--color-input-surface)"
2667
2418
  },
2668
2419
  ".cm-panel input:focus, .cm-panel button:focus": {
2669
- outline: "1px solid var(--dx-neutralFocusIndicator)"
2420
+ outline: "1px solid var(--color-focus-ring-subtle)"
2670
2421
  },
2671
2422
  ".cm-panel label": {
2672
2423
  display: "inline-flex",
@@ -2679,33 +2430,33 @@ var baseTheme = EditorView13.baseTheme({
2679
2430
  height: "8px",
2680
2431
  marginRight: "6px !important",
2681
2432
  padding: "2px !important",
2682
- color: "var(--dx-neutralFocusIndicator)"
2433
+ color: "var(--color-focus-ring-subtle)"
2683
2434
  },
2684
2435
  ".cm-panel button": {
2685
2436
  "&:hover": {
2686
- backgroundColor: "var(--dx-accentSurfaceHover) !important"
2437
+ // TODO(burdon): Replace with layer and @apply bg-accent-surface-hover
2438
+ backgroundColor: "var(--color-accent-surface-hover) !important"
2687
2439
  },
2688
2440
  "&:active": {
2689
- backgroundColor: "var(--dx-accentSurfaceHover)"
2441
+ backgroundColor: "var(--color-accent-surface-hover)"
2690
2442
  }
2691
2443
  },
2692
2444
  ".cm-panel.cm-search": {
2693
2445
  padding: "4px",
2694
- borderTop: "1px solid var(--dx-separator)"
2446
+ borderTop: "1px solid var(--color-separator)"
2695
2447
  }
2696
2448
  });
2697
- var editorGutter = EditorView13.theme({
2449
+ var editorGutter = EditorView11.theme({
2698
2450
  ".cm-gutters": {
2699
2451
  // NOTE: Non-transparent background required to cover content if scrolling horizontally.
2700
- background: "var(--dx-baseSurface) !important",
2452
+ background: "var(--color-base-surface) !important",
2701
2453
  paddingRight: "1rem"
2702
2454
  }
2703
2455
  });
2704
- var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2705
- // Set metrics on the scroller (this is often what CM uses for layout).
2456
+ var createFontTheme = ({ monospace } = {}) => EditorView11.theme({
2457
+ // Main content.
2706
2458
  ".cm-scroller": {
2707
- fontFamily: monospace ? fontMono : fontBody,
2708
- fontSize: "16px"
2459
+ fontFamily: monospace ? fontMono : fontBody
2709
2460
  },
2710
2461
  // Maintain defaults for UI components.
2711
2462
  ".cm-content, .cm-gutters, .cm-panel": {
@@ -2715,9 +2466,9 @@ var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2715
2466
  });
2716
2467
 
2717
2468
  // src/extensions/focus.ts
2718
- import { StateEffect as StateEffect6, StateField as StateField5 } from "@codemirror/state";
2719
- import { EditorView as EditorView14 } from "@codemirror/view";
2720
- var focusEffect = StateEffect6.define();
2469
+ import { StateEffect as StateEffect4, StateField as StateField5 } from "@codemirror/state";
2470
+ import { EditorView as EditorView12 } from "@codemirror/view";
2471
+ var focusEffect = StateEffect4.define();
2721
2472
  var focusField = StateField5.define({
2722
2473
  create: () => false,
2723
2474
  update: (value, tr) => {
@@ -2731,7 +2482,7 @@ var focusField = StateField5.define({
2731
2482
  });
2732
2483
  var focus = [
2733
2484
  focusField,
2734
- EditorView14.domEventHandlers({
2485
+ EditorView12.domEventHandlers({
2735
2486
  focus: (_event, view) => {
2736
2487
  requestAnimationFrame(() => view.dispatch({
2737
2488
  effects: focusEffect.of(true)
@@ -2745,77 +2496,451 @@ var focus = [
2745
2496
  })
2746
2497
  ];
2747
2498
 
2748
- // src/extensions/factories.ts
2749
- var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2750
- var tabbable = EditorView15.contentAttributes.of({
2751
- tabindex: "0"
2752
- });
2753
- var filterChars = (chars) => {
2754
- return EditorState.transactionFilter.of((transaction) => {
2755
- if (!transaction.docChanged) {
2756
- return transaction;
2499
+ // src/extensions/scrolling/auto-scroll.ts
2500
+ import { StateEffect as StateEffect6 } from "@codemirror/state";
2501
+ import { EditorView as EditorView14, ViewPlugin as ViewPlugin10 } from "@codemirror/view";
2502
+ import { addEventListener, combine, throttle as throttle2 } from "@dxos/async";
2503
+ import { Domino } from "@dxos/ui";
2504
+ import { getSize } from "@dxos/ui-theme";
2505
+
2506
+ // src/extensions/scrolling/crawler.ts
2507
+ import { StateEffect as StateEffect5 } from "@codemirror/state";
2508
+ import { EditorView as EditorView13, ViewPlugin as ViewPlugin9 } from "@codemirror/view";
2509
+ import { log as log7 } from "@dxos/log";
2510
+ var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scrolling/crawler.ts";
2511
+ var crawlerLineEffect = StateEffect5.define();
2512
+ var crawlerActiveEffect = StateEffect5.define();
2513
+ var scrollToLine = (view, options) => {
2514
+ view.dispatch({
2515
+ effects: crawlerLineEffect.of(options)
2516
+ });
2517
+ };
2518
+ var crawler = ({ overScroll = 0 } = {}) => {
2519
+ const crawlerPlugin = ViewPlugin9.fromClass(class CrawlerPlugin {
2520
+ view;
2521
+ crawler;
2522
+ constructor(view) {
2523
+ this.view = view;
2524
+ this.crawler = createCrawler(this.view);
2757
2525
  }
2758
- const changes = [];
2759
- transaction.changes.iterChanges((fromA, toA, fromB, toB, text) => {
2760
- const inserted = text.toString();
2761
- const filtered = inserted.replace(chars, "");
2762
- if (inserted !== filtered) {
2763
- changes.push({
2764
- from: fromB,
2765
- to: toB,
2766
- insert: filtered
2767
- });
2526
+ // No-op.
2527
+ destroy() {
2528
+ this.crawler.cancel();
2529
+ }
2530
+ cancel() {
2531
+ this.crawler.cancel();
2532
+ }
2533
+ crawl(start = false) {
2534
+ if (start) {
2535
+ this.crawler.scroll();
2536
+ } else {
2537
+ this.crawler.cancel();
2768
2538
  }
2769
- });
2770
- if (changes.length) {
2771
- return [
2772
- transaction,
2773
- {
2774
- changes,
2775
- sequential: true
2539
+ }
2540
+ scroll({ line, offset = 0, position, behavior = "instant" }) {
2541
+ const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
2542
+ const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
2543
+ const doc = this.view.state.doc;
2544
+ let targetScrollTop = scrollHeight - clientHeight + offset;
2545
+ if (line >= 0 && line <= doc.lines - 1) {
2546
+ const lineStart = doc.line(line + 1).from;
2547
+ const coords = this.view.coordsAtPos(lineStart);
2548
+ if (coords) {
2549
+ const currentScrollTop = scrollTop;
2550
+ const maxScrollTop = scrollHeight - clientHeight;
2551
+ if (position === "end") {
2552
+ targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
2553
+ } else {
2554
+ targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
2555
+ }
2556
+ targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
2776
2557
  }
2777
- ];
2558
+ }
2559
+ requestAnimationFrame(() => {
2560
+ this.view.scrollDOM.scrollTo({
2561
+ top: targetScrollTop
2562
+ });
2563
+ });
2778
2564
  }
2779
- return transaction;
2780
2565
  });
2781
- };
2782
- var defaultBasicOptions = {
2783
- allowMultipleSelections: true,
2784
- bracketMatching: true,
2785
- closeBrackets: true,
2786
- drawSelection: true,
2787
- focus: true,
2788
- history: true,
2789
- keymap: "standard",
2790
- lineWrapping: true,
2791
- search: false
2792
- };
2793
- var keymaps = {
2794
- // https://codemirror.net/docs/ref/#commands.standardKeymap
2795
- standard: standardKeymap,
2796
- // https://codemirror.net/docs/ref/#commands.defaultKeymap
2797
- default: defaultKeymap
2798
- };
2799
- var createBasicExtensions = (propsProp) => {
2800
- const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2801
2566
  return [
2802
- // NOTE: Doesn't catch errors in keymap functions.
2803
- EditorView15.exceptionSink.of((err) => {
2804
- log7.catch(err, void 0, {
2805
- F: __dxlog_file10,
2806
- L: 132,
2807
- S: void 0,
2808
- C: (f, a) => f(...a)
2809
- });
2810
- }),
2811
- props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2567
+ crawlerPlugin,
2568
+ // Listen for effect.
2569
+ EditorView13.updateListener.of((update2) => {
2570
+ update2.transactions.forEach((transaction) => {
2571
+ try {
2572
+ const plugin = update2.view.plugin(crawlerPlugin);
2573
+ if (plugin) {
2574
+ for (const effect of transaction.effects) {
2575
+ if (effect.is(crawlerActiveEffect)) {
2576
+ plugin.crawl(effect.value);
2577
+ } else if (effect.is(crawlerLineEffect)) {
2578
+ plugin.scroll(effect.value);
2579
+ }
2580
+ }
2581
+ }
2582
+ } catch (err) {
2583
+ log7.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 98, S: void 0 });
2584
+ }
2585
+ });
2586
+ }),
2587
+ // Styles.
2588
+ EditorView13.theme({
2589
+ ".cm-scroller": {
2590
+ overflowY: "scroll",
2591
+ // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks
2592
+ // expanding their TogglePanel), the browser picks a stable element near the viewport
2593
+ // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning
2594
+ // logic still has the final word when pinned (forces scrollTop to scrollHeight).
2595
+ overflowAnchor: "auto"
2596
+ },
2597
+ ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
2598
+ display: "none"
2599
+ },
2600
+ ".cm-scroller::-webkit-scrollbar-thumb": {
2601
+ background: "transparent",
2602
+ transition: "background 0.15s"
2603
+ },
2604
+ "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
2605
+ background: "var(--color-scrollbar-thumb)"
2606
+ },
2607
+ // Spacer below the last text line. Implemented as a real block pseudo-element
2608
+ // (rather than `padding-bottom` on `.cm-content`) so it materializes in the
2609
+ // scroller's `scrollHeight` regardless of how `padding` is reset by the base
2610
+ // theme or downstream classes — this is what gives auto-scroll its head-room
2611
+ // so the last line stays `overScroll` px above the viewport bottom.
2612
+ ".cm-content::after": {
2613
+ content: '""',
2614
+ display: "block",
2615
+ height: `${overScroll}px`
2616
+ },
2617
+ ".cm-scroll-button": {
2618
+ position: "absolute",
2619
+ bottom: "0.5rem",
2620
+ right: "1rem"
2621
+ }
2622
+ })
2623
+ ];
2624
+ };
2625
+ function createCrawler(view, omega = 5, snapThreshold = 5, snapVelocity = 50) {
2626
+ const el = view.scrollDOM;
2627
+ let currentTop = 0;
2628
+ let velocity = 0;
2629
+ let rafId = null;
2630
+ let lastTime = 0;
2631
+ function frame(now) {
2632
+ const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1e3);
2633
+ lastTime = now;
2634
+ const targetTop = el.scrollHeight - el.clientHeight;
2635
+ const delta = targetTop - currentTop;
2636
+ if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {
2637
+ el.scrollTop = targetTop;
2638
+ currentTop = targetTop;
2639
+ velocity = 0;
2640
+ rafId = null;
2641
+ lastTime = 0;
2642
+ return;
2643
+ }
2644
+ const accel = omega * omega * delta - 2 * omega * velocity;
2645
+ velocity += accel * dt;
2646
+ currentTop += velocity * dt;
2647
+ el.scrollTop = currentTop;
2648
+ rafId = requestAnimationFrame(frame);
2649
+ }
2650
+ return {
2651
+ scroll: () => {
2652
+ if (rafId === null) {
2653
+ currentTop = el.scrollTop;
2654
+ lastTime = 0;
2655
+ rafId = requestAnimationFrame(frame);
2656
+ }
2657
+ },
2658
+ cancel: () => {
2659
+ if (rafId !== null) {
2660
+ cancelAnimationFrame(rafId);
2661
+ velocity = 0;
2662
+ lastTime = 0;
2663
+ rafId = null;
2664
+ }
2665
+ }
2666
+ };
2667
+ }
2668
+
2669
+ // src/extensions/scrolling/auto-scroll.ts
2670
+ var autoScrollEffect = StateEffect6.define();
2671
+ var autoScroll = ({ scrollOnResize = true } = {}) => {
2672
+ let buttonContainer;
2673
+ let isPinned = true;
2674
+ let jumpPending = false;
2675
+ let enabled = true;
2676
+ let firstUpdate = true;
2677
+ const setPinned = (pinned) => {
2678
+ buttonContainer?.classList.toggle("opacity-0", pinned);
2679
+ isPinned = pinned;
2680
+ };
2681
+ return [
2682
+ // Update listener for scrolling when content changes.
2683
+ EditorView14.updateListener.of((update2) => {
2684
+ const { view, heightChanged, state, startState } = update2;
2685
+ for (const tr of update2.transactions) {
2686
+ for (const effect of tr.effects) {
2687
+ if (effect.is(autoScrollEffect)) {
2688
+ enabled = effect.value;
2689
+ if (enabled) {
2690
+ setPinned(true);
2691
+ view.dispatch({
2692
+ effects: crawlerActiveEffect.of(true)
2693
+ });
2694
+ } else {
2695
+ view.dispatch({
2696
+ effects: crawlerActiveEffect.of(false)
2697
+ });
2698
+ }
2699
+ }
2700
+ }
2701
+ }
2702
+ if (!enabled) {
2703
+ return;
2704
+ }
2705
+ if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
2706
+ firstUpdate = false;
2707
+ jumpPending = true;
2708
+ requestAnimationFrame(() => {
2709
+ view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
2710
+ jumpPending = false;
2711
+ });
2712
+ return;
2713
+ }
2714
+ firstUpdate = false;
2715
+ if (jumpPending) {
2716
+ return;
2717
+ }
2718
+ if (heightChanged) {
2719
+ if (isPinned) {
2720
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
2721
+ const delta = scrollHeight - scrollTop - clientHeight;
2722
+ if (delta > 0) {
2723
+ setPinned(true);
2724
+ view.dispatch({
2725
+ effects: crawlerActiveEffect.of(true)
2726
+ });
2727
+ } else if (delta < -1) {
2728
+ setPinned(false);
2729
+ }
2730
+ } else {
2731
+ if (state.doc.length === 0) {
2732
+ setPinned(true);
2733
+ }
2734
+ }
2735
+ }
2736
+ }),
2737
+ // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,
2738
+ // window resize). Doc-driven height changes are handled by the updateListener above; this
2739
+ // observer covers the case where the viewport changes while the doc length is unchanged.
2740
+ scrollOnResize ? ViewPlugin10.fromClass(class {
2741
+ observer;
2742
+ firstObservation = true;
2743
+ destroyed = false;
2744
+ constructor(view) {
2745
+ const onResize = throttle2(() => {
2746
+ if (this.destroyed || !enabled) {
2747
+ return;
2748
+ }
2749
+ setPinned(true);
2750
+ requestAnimationFrame(() => {
2751
+ if (this.destroyed) {
2752
+ return;
2753
+ }
2754
+ view.scrollDOM.scrollTo({
2755
+ top: view.scrollDOM.scrollHeight,
2756
+ behavior: "instant"
2757
+ });
2758
+ view.dispatch({
2759
+ effects: crawlerActiveEffect.of(false)
2760
+ });
2761
+ });
2762
+ }, 50);
2763
+ this.observer = new ResizeObserver(() => {
2764
+ if (this.firstObservation) {
2765
+ this.firstObservation = false;
2766
+ return;
2767
+ }
2768
+ onResize();
2769
+ });
2770
+ this.observer.observe(view.scrollDOM);
2771
+ }
2772
+ destroy() {
2773
+ this.destroyed = true;
2774
+ this.observer.disconnect();
2775
+ }
2776
+ }) : [],
2777
+ // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
2778
+ ViewPlugin10.fromClass(class {
2779
+ cleanup;
2780
+ constructor(view) {
2781
+ const onUserScroll = throttle2(() => {
2782
+ requestAnimationFrame(() => {
2783
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
2784
+ const delta = scrollHeight - scrollTop - clientHeight;
2785
+ const pinned = Math.abs(delta) <= 1;
2786
+ setPinned(pinned);
2787
+ if (!pinned) {
2788
+ view.dispatch({
2789
+ effects: crawlerActiveEffect.of(false)
2790
+ });
2791
+ }
2792
+ });
2793
+ }, 500);
2794
+ this.cleanup = createUserScrollDetector(view.scrollDOM, () => {
2795
+ if (isPinned) {
2796
+ setPinned(false);
2797
+ view.dispatch({
2798
+ effects: crawlerActiveEffect.of(false)
2799
+ });
2800
+ }
2801
+ onUserScroll();
2802
+ });
2803
+ }
2804
+ destroy() {
2805
+ this.cleanup();
2806
+ }
2807
+ }),
2808
+ // Scroll button.
2809
+ ViewPlugin10.fromClass(class {
2810
+ constructor(view) {
2811
+ const icon = Domino.of("dx-icon").classNames(getSize(4)).attributes({
2812
+ icon: "ph--arrow-down--regular"
2813
+ });
2814
+ const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
2815
+ "data-density": "fine"
2816
+ }).append(icon).on("click", () => {
2817
+ setPinned(true);
2818
+ view.dispatch({
2819
+ effects: crawlerLineEffect.of({
2820
+ line: -1,
2821
+ position: "end",
2822
+ behavior: "smooth"
2823
+ })
2824
+ });
2825
+ });
2826
+ buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").append(button).root;
2827
+ view.scrollDOM.parentElement.appendChild(buttonContainer);
2828
+ }
2829
+ })
2830
+ ];
2831
+ };
2832
+ function createUserScrollDetector(element, onUserScroll) {
2833
+ return combine(addEventListener(element, "wheel", () => onUserScroll(), {
2834
+ passive: true
2835
+ }), addEventListener(element, "pointerdown", (event) => {
2836
+ if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
2837
+ onUserScroll();
2838
+ }
2839
+ }));
2840
+ }
2841
+
2842
+ // src/extensions/scrolling/scroll-past-end.ts
2843
+ import { EditorView as EditorView15, ViewPlugin as ViewPlugin11 } from "@codemirror/view";
2844
+ var scrollPastEndPlugin = ViewPlugin11.fromClass(class {
2845
+ _height = 1e3;
2846
+ _attrs = {
2847
+ style: `padding-bottom: ${this._height}px`
2848
+ };
2849
+ update({ view }) {
2850
+ const lastLineBlock = view.lineBlockAt(view.state.doc.length);
2851
+ const height = view.dom.clientHeight - lastLineBlock.height - view.documentPadding.top - 0.5;
2852
+ if (height >= 0 && height !== this._height) {
2853
+ this._height = height;
2854
+ this._attrs = {
2855
+ style: `padding-bottom: ${height}px`
2856
+ };
2857
+ }
2858
+ }
2859
+ });
2860
+ var scrollPastEnd = () => [
2861
+ scrollPastEndPlugin,
2862
+ EditorView15.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?._attrs ?? null)
2863
+ ];
2864
+
2865
+ // src/extensions/scrolling/scroller.ts
2866
+ import { isTruthy as isTruthy2 } from "@dxos/util";
2867
+ var scroller = ({ overScroll, scrollOnResize, autoScroll: autoScroll2 = true } = {}) => {
2868
+ return [
2869
+ crawler({
2870
+ overScroll
2871
+ }),
2872
+ autoScroll2 && autoScroll({
2873
+ scrollOnResize
2874
+ })
2875
+ ].filter(isTruthy2);
2876
+ };
2877
+
2878
+ // src/extensions/factories.ts
2879
+ var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2880
+ var tabbable = EditorView16.contentAttributes.of({
2881
+ tabindex: "0"
2882
+ });
2883
+ var filterChars = (chars) => {
2884
+ return EditorState.transactionFilter.of((transaction) => {
2885
+ if (!transaction.docChanged) {
2886
+ return transaction;
2887
+ }
2888
+ const changes = [];
2889
+ transaction.changes.iterChanges((fromA, toA, fromB, toB, text) => {
2890
+ const inserted = text.toString();
2891
+ const filtered = inserted.replace(chars, "");
2892
+ if (inserted !== filtered) {
2893
+ changes.push({
2894
+ from: fromB,
2895
+ to: toB,
2896
+ insert: filtered
2897
+ });
2898
+ }
2899
+ });
2900
+ if (changes.length) {
2901
+ return [
2902
+ transaction,
2903
+ {
2904
+ changes,
2905
+ sequential: true
2906
+ }
2907
+ ];
2908
+ }
2909
+ return transaction;
2910
+ });
2911
+ };
2912
+ var defaultBasicOptions = {
2913
+ allowMultipleSelections: true,
2914
+ bracketMatching: true,
2915
+ closeBrackets: true,
2916
+ drawSelection: true,
2917
+ focus: true,
2918
+ history: true,
2919
+ keymap: "standard",
2920
+ lineWrapping: true,
2921
+ search: false
2922
+ };
2923
+ var keymaps = {
2924
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
2925
+ standard: standardKeymap,
2926
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
2927
+ default: defaultKeymap
2928
+ };
2929
+ var createBasicExtensions = (propsProp) => {
2930
+ const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2931
+ return [
2932
+ // NOTE: Doesn't catch errors in keymap functions.
2933
+ EditorView16.exceptionSink.of((err) => {
2934
+ log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
2935
+ }),
2936
+ props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2812
2937
  props.bracketMatching && bracketMatching(),
2813
2938
  props.closeBrackets && closeBrackets(),
2814
2939
  props.dropCursor && dropCursor2(),
2815
2940
  props.drawSelection && drawSelection({
2816
2941
  cursorBlinkRate: 1200
2817
2942
  }),
2818
- props.editable !== void 0 && EditorView15.editable.of(props.editable),
2943
+ props.editable !== void 0 && EditorView16.editable.of(props.editable),
2819
2944
  props.focus && focus,
2820
2945
  props.highlightActiveLine && highlightActiveLine(),
2821
2946
  props.history && history(),
@@ -2823,9 +2948,16 @@ var createBasicExtensions = (propsProp) => {
2823
2948
  lineNumbers(),
2824
2949
  editorGutter
2825
2950
  ],
2826
- props.lineWrapping && EditorView15.lineWrapping,
2951
+ props.lineWrapping && EditorView16.lineWrapping,
2827
2952
  props.placeholder && placeholder2(props.placeholder),
2828
2953
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2954
+ // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
2955
+ // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that
2956
+ // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits
2957
+ // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)
2958
+ // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers
2959
+ // depend on being able to populate the doc themselves.
2960
+ props.readOnly && EditorState.transactionFilter.of((tr) => tr.docChanged && (tr.isUserEvent("input") || tr.isUserEvent("delete") || tr.isUserEvent("undo") || tr.isUserEvent("redo")) ? [] : tr),
2829
2961
  props.scrollPastEnd && scrollPastEnd(),
2830
2962
  props.tabbable && tabbable,
2831
2963
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -2850,17 +2982,17 @@ var createBasicExtensions = (propsProp) => {
2850
2982
  preventDefault: true,
2851
2983
  run: () => true
2852
2984
  }
2853
- ].filter(isTruthy2))
2854
- ].filter(isTruthy2);
2985
+ ].filter(isTruthy3))
2986
+ ].filter(isTruthy3);
2855
2987
  };
2856
2988
  var grow = {
2857
2989
  editor: {
2858
- className: "bs-full is-full"
2990
+ className: "h-full w-full"
2859
2991
  }
2860
2992
  };
2861
2993
  var fullWidth = {
2862
2994
  editor: {
2863
- className: "is-full"
2995
+ className: "w-full"
2864
2996
  }
2865
2997
  };
2866
2998
  var defaultThemeSlots = grow;
@@ -2868,27 +3000,32 @@ var defaultStyles = {
2868
3000
  dark: vscodeDarkStyle,
2869
3001
  light: vscodeLightStyle
2870
3002
  };
2871
- var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp } = {}) => {
3003
+ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp, themeMode } = {}) => {
2872
3004
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
2873
3005
  return [
2874
3006
  baseTheme,
2875
- EditorView15.darkTheme.of(themeMode === "dark"),
3007
+ EditorView16.darkTheme.of(themeMode === "dark"),
2876
3008
  createFontTheme({
2877
3009
  monospace
2878
3010
  }),
2879
3011
  syntaxHighlightingProp && syntaxHighlighting(HighlightStyle.define(themeMode === "dark" ? defaultStyles.dark : defaultStyles.light)),
2880
- slots.editor?.className && EditorView15.editorAttributes.of({
3012
+ slots.editor?.className && EditorView16.editorAttributes.of({
2881
3013
  class: slots.editor.className
2882
3014
  }),
2883
- slots.content?.className && EditorView15.contentAttributes.of({
3015
+ slots.content?.className && EditorView16.contentAttributes.of({
2884
3016
  class: slots.content.className
2885
3017
  }),
2886
- slots.scroll?.className && ViewPlugin11.fromClass(class {
3018
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
2887
3019
  constructor(view) {
2888
- view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
3020
+ if (slots.scroller?.className) {
3021
+ view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
3022
+ }
3023
+ if (scrollbarThin) {
3024
+ view.scrollDOM.style.setProperty("--scrollbar-size", "4px");
3025
+ }
2889
3026
  }
2890
3027
  })
2891
- ].filter(isTruthy2);
3028
+ ].filter(isTruthy3);
2892
3029
  };
2893
3030
  var createDataExtensions = ({ id, text, messenger, identity }) => {
2894
3031
  const extensions = [];
@@ -2903,8 +3040,8 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2903
3040
  channel: `awareness.${id}`,
2904
3041
  peerId: identity.identityKey.toHex(),
2905
3042
  info: {
2906
- darkColor: `var(--dx-${hue}Cursor)`,
2907
- lightColor: `var(--dx-${hue}Cursor)`,
3043
+ darkColor: `var(--color-${hue}-border)`,
3044
+ lightColor: `var(--color-${hue}-border)`,
2908
3045
  displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex())
2909
3046
  }
2910
3047
  })));
@@ -2914,7 +3051,7 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2914
3051
 
2915
3052
  // src/extensions/folding.ts
2916
3053
  import { codeFolding, foldGutter } from "@codemirror/language";
2917
- import { EditorView as EditorView16 } from "@codemirror/view";
3054
+ import { EditorView as EditorView17 } from "@codemirror/view";
2918
3055
  import { Domino as Domino2, mx as mx4 } from "@dxos/ui";
2919
3056
  var folding = () => {
2920
3057
  return [
@@ -2922,13 +3059,14 @@ var folding = () => {
2922
3059
  placeholderDOM: () => Domino2.of("span").root
2923
3060
  }),
2924
3061
  foldGutter({
3062
+ // NOTE: We can't animate since the element is remounted on state change.
2925
3063
  markerDOM: (open) => {
2926
- 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({
3064
+ 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({
2927
3065
  href: Domino2.icon("ph--caret-right--regular")
2928
3066
  }))).root;
2929
3067
  }
2930
3068
  }),
2931
- EditorView16.theme({
3069
+ EditorView17.theme({
2932
3070
  ".cm-foldGutter": {
2933
3071
  opacity: 0.3,
2934
3072
  transition: "opacity 0.3s",
@@ -2942,7 +3080,7 @@ var folding = () => {
2942
3080
  };
2943
3081
 
2944
3082
  // src/extensions/hashtag.ts
2945
- import { Decoration as Decoration8, EditorView as EditorView17, MatchDecorator, ViewPlugin as ViewPlugin12, WidgetType as WidgetType4 } from "@codemirror/view";
3083
+ import { Decoration as Decoration8, EditorView as EditorView18, MatchDecorator, ViewPlugin as ViewPlugin13, WidgetType as WidgetType4 } from "@codemirror/view";
2946
3084
  import { getHashStyles, mx as mx5 } from "@dxos/ui-theme";
2947
3085
  var TagWidget = class extends WidgetType4 {
2948
3086
  _text;
@@ -2963,7 +3101,7 @@ var tagMatcher = new MatchDecorator({
2963
3101
  })
2964
3102
  });
2965
3103
  var hashtag = () => [
2966
- ViewPlugin12.fromClass(class {
3104
+ ViewPlugin13.fromClass(class {
2967
3105
  tags;
2968
3106
  constructor(view) {
2969
3107
  this.tags = tagMatcher.createDeco(view);
@@ -2973,11 +3111,11 @@ var hashtag = () => [
2973
3111
  }
2974
3112
  }, {
2975
3113
  decorations: (instance) => instance.tags,
2976
- provide: (plugin) => EditorView17.atomicRanges.of((view) => {
3114
+ provide: (plugin) => EditorView18.atomicRanges.of((view) => {
2977
3115
  return view.plugin(plugin)?.tags || Decoration8.none;
2978
3116
  })
2979
3117
  }),
2980
- EditorView17.theme({
3118
+ EditorView18.theme({
2981
3119
  ".cm-tag": {
2982
3120
  borderRadius: "4px",
2983
3121
  marginRight: "6px",
@@ -3032,18 +3170,18 @@ var schemaLinter = (validate) => (view) => {
3032
3170
  };
3033
3171
 
3034
3172
  // src/extensions/listener.ts
3035
- import { EditorView as EditorView18 } from "@codemirror/view";
3173
+ import { EditorView as EditorView19 } from "@codemirror/view";
3036
3174
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
3037
3175
  var listener = ({ onFocus, onChange }) => {
3038
3176
  return [
3039
- onFocus && EditorView18.focusChangeEffect.of((state, focusing) => {
3177
+ onFocus && EditorView19.focusChangeEffect.of((state, focusing) => {
3040
3178
  onFocus({
3041
3179
  id: state.facet(documentId),
3042
3180
  focusing
3043
3181
  });
3044
3182
  return null;
3045
3183
  }),
3046
- onChange && EditorView18.updateListener.of(({ state, docChanged }) => {
3184
+ onChange && EditorView19.updateListener.of(({ state, docChanged }) => {
3047
3185
  if (docChanged) {
3048
3186
  onChange({
3049
3187
  id: state.facet(documentId),
@@ -3058,7 +3196,7 @@ var listener = ({ onFocus, onChange }) => {
3058
3196
  import { snippet } from "@codemirror/autocomplete";
3059
3197
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3060
3198
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
3061
- import { EditorView as EditorView19, keymap as keymap8 } from "@codemirror/view";
3199
+ import { EditorView as EditorView20, keymap as keymap8 } from "@codemirror/view";
3062
3200
  import { debounceAndThrottle } from "@dxos/async";
3063
3201
  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;
3064
3202
  var Inline = /* @__PURE__ */ (function(Inline2) {
@@ -4147,7 +4285,7 @@ var getFormatting = (state) => {
4147
4285
  };
4148
4286
  };
4149
4287
  var formattingListener = (onStateChange, delay = 100) => {
4150
- return EditorView19.updateListener.of(debounceAndThrottle((update2) => {
4288
+ return EditorView20.updateListener.of(debounceAndThrottle((update2) => {
4151
4289
  if (update2.docChanged || update2.selectionSet) {
4152
4290
  onStateChange(getFormatting(update2.state));
4153
4291
  }
@@ -4208,11 +4346,10 @@ import { completionKeymap } from "@codemirror/autocomplete";
4208
4346
  import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
4209
4347
  import { jsonLanguage } from "@codemirror/lang-json";
4210
4348
  import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
4211
- import { xml } from "@codemirror/lang-xml";
4212
- import { LanguageDescription, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4349
+ import { foldNodeProp, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4213
4350
  import { languages } from "@codemirror/language-data";
4214
4351
  import { keymap as keymap9 } from "@codemirror/view";
4215
- import { isTruthy as isTruthy3 } from "@dxos/util";
4352
+ import { isTruthy as isTruthy4 } from "@dxos/util";
4216
4353
 
4217
4354
  // src/extensions/markdown/highlight.ts
4218
4355
  import { markdownLanguage } from "@codemirror/lang-markdown";
@@ -4330,30 +4467,38 @@ var markdownHighlightStyle = (_options = {}) => {
4330
4467
  ],
4331
4468
  class: "font-mono"
4332
4469
  },
4333
- // Headings.
4470
+ // Headings — use CSS properties only (no class:) so CodeMirror generates scoped CSS via
4471
+ // StyleModule that overrides vscodeDarkStyle's t.heading rule. When class: is present,
4472
+ // HighlightStyle silently ignores all other CSS properties (they're mutually exclusive).
4473
+ // Font sizes use Tailwind v4 CSS variables so nothing is hardcoded.
4474
+ {
4475
+ tag: tags.heading,
4476
+ color: "var(--color-cm-heading) !important",
4477
+ fontWeight: "300"
4478
+ },
4334
4479
  {
4335
4480
  tag: tags.heading1,
4336
- class: markdownTheme.heading(1)
4481
+ ...markdownTheme.heading(1)
4337
4482
  },
4338
4483
  {
4339
4484
  tag: tags.heading2,
4340
- class: markdownTheme.heading(2)
4485
+ ...markdownTheme.heading(2)
4341
4486
  },
4342
4487
  {
4343
4488
  tag: tags.heading3,
4344
- class: markdownTheme.heading(3)
4489
+ ...markdownTheme.heading(3)
4345
4490
  },
4346
4491
  {
4347
4492
  tag: tags.heading4,
4348
- class: markdownTheme.heading(4)
4493
+ ...markdownTheme.heading(4)
4349
4494
  },
4350
4495
  {
4351
4496
  tag: tags.heading5,
4352
- class: markdownTheme.heading(5)
4497
+ ...markdownTheme.heading(5)
4353
4498
  },
4354
4499
  {
4355
4500
  tag: tags.heading6,
4356
- class: markdownTheme.heading(6)
4501
+ ...markdownTheme.heading(6)
4357
4502
  },
4358
4503
  // Emphasis.
4359
4504
  {
@@ -4408,15 +4553,23 @@ var createMarkdownExtensions = (options = {}) => {
4408
4553
  // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
4409
4554
  base: markdownLanguage2,
4410
4555
  // Languages for syntax highlighting fenced code blocks.
4556
+ // Caller-supplied languages are checked first so they can override defaults.
4411
4557
  defaultCodeLanguage: jsonLanguage,
4412
- codeLanguages: languages,
4558
+ codeLanguages: [
4559
+ ...options.codeLanguages ?? [],
4560
+ ...languages
4561
+ ],
4413
4562
  // Don't complete HTML tags.
4414
4563
  completeHTMLTags: false,
4415
4564
  // Parser extensions.
4416
4565
  extensions: [
4417
4566
  // GFM provided by default.
4418
4567
  markdownTagsExtensions,
4419
- ...options.extensions ?? defaultExtensions()
4568
+ ...options.extensions ?? defaultExtensions(),
4569
+ // Disable folding for fenced code blocks by overriding foldNodeProp.
4570
+ // Note: returning null from foldService does not prevent syntaxFolding fallback,
4571
+ // so we must override the node prop directly on the FencedCode node type.
4572
+ noFencedCodeFolding
4420
4573
  ]
4421
4574
  }),
4422
4575
  // Custom styles.
@@ -4428,21 +4581,16 @@ var createMarkdownExtensions = (options = {}) => {
4428
4581
  ...defaultKeymap2,
4429
4582
  // TODO(burdon): Remove?
4430
4583
  ...completionKeymap
4431
- ].filter(isTruthy3))
4584
+ ].filter(isTruthy4))
4432
4585
  ];
4433
4586
  };
4434
- var xmlLanguageDesc = LanguageDescription.of({
4435
- name: "xml",
4436
- alias: [
4437
- "html",
4438
- "xhtml"
4439
- ],
4440
- extensions: [
4441
- "xml",
4442
- "xhtml"
4443
- ],
4444
- load: async () => xml()
4445
- });
4587
+ var noFencedCodeFolding = {
4588
+ props: [
4589
+ foldNodeProp.add({
4590
+ FencedCode: () => null
4591
+ })
4592
+ ]
4593
+ };
4446
4594
  var defaultExtensions = () => [
4447
4595
  noSetExtHeading,
4448
4596
  noHtml
@@ -4462,19 +4610,19 @@ var debugTree = (cb) => StateField6.define({
4462
4610
  update: (value, tr) => cb(convertTreeToJson(tr.state))
4463
4611
  });
4464
4612
  var convertTreeToJson = (state) => {
4465
- const treeToJson = (cursor2) => {
4613
+ const treeToJson = (cursor) => {
4466
4614
  const node = {
4467
- type: cursor2.type.name,
4468
- from: cursor2.from,
4469
- to: cursor2.to,
4470
- text: state.doc.slice(cursor2.from, cursor2.to).toString(),
4615
+ type: cursor.type.name,
4616
+ from: cursor.from,
4617
+ to: cursor.to,
4618
+ text: state.doc.slice(cursor.from, cursor.to).toString(),
4471
4619
  children: []
4472
4620
  };
4473
- if (cursor2.firstChild()) {
4621
+ if (cursor.firstChild()) {
4474
4622
  do {
4475
- node.children.push(treeToJson(cursor2));
4476
- } while (cursor2.nextSibling());
4477
- cursor2.parent();
4623
+ node.children.push(treeToJson(cursor));
4624
+ } while (cursor.nextSibling());
4625
+ cursor.parent();
4478
4626
  }
4479
4627
  return node;
4480
4628
  };
@@ -4484,16 +4632,15 @@ var convertTreeToJson = (state) => {
4484
4632
  // src/extensions/markdown/decorate.ts
4485
4633
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4486
4634
  import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect7 } from "@codemirror/state";
4487
- import { Decoration as Decoration11, EditorView as EditorView23, ViewPlugin as ViewPlugin14, WidgetType as WidgetType7 } from "@codemirror/view";
4635
+ import { Decoration as Decoration11, EditorView as EditorView24, ViewPlugin as ViewPlugin15, WidgetType as WidgetType7 } from "@codemirror/view";
4488
4636
  import { invariant as invariant4 } from "@dxos/invariant";
4489
- import { mx as mx6 } from "@dxos/ui-theme";
4490
4637
 
4491
4638
  // src/extensions/markdown/changes.ts
4492
4639
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4493
4640
  import { Transaction as Transaction4 } from "@codemirror/state";
4494
- import { ViewPlugin as ViewPlugin13 } from "@codemirror/view";
4641
+ import { ViewPlugin as ViewPlugin14 } from "@codemirror/view";
4495
4642
  var adjustChanges = () => {
4496
- return ViewPlugin13.fromClass(class {
4643
+ return ViewPlugin14.fromClass(class {
4497
4644
  update(update2) {
4498
4645
  const tree = syntaxTree4(update2.state);
4499
4646
  const adjustments = [];
@@ -4635,7 +4782,7 @@ var getValidUrl = (str) => {
4635
4782
  // src/extensions/markdown/image.ts
4636
4783
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4637
4784
  import { StateField as StateField7 } from "@codemirror/state";
4638
- import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType5 } from "@codemirror/view";
4785
+ import { Decoration as Decoration9, EditorView as EditorView21, WidgetType as WidgetType5 } from "@codemirror/view";
4639
4786
  var image = (_options = {}) => {
4640
4787
  return [
4641
4788
  StateField7.define({
@@ -4646,10 +4793,10 @@ var image = (_options = {}) => {
4646
4793
  if (!tr.docChanged && !tr.selection) {
4647
4794
  return value;
4648
4795
  }
4649
- const cursor2 = tr.state.selection.main.head;
4796
+ const cursor = tr.state.selection.main.head;
4650
4797
  const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
4651
- let from = Math.min(cursor2, oldCursor);
4652
- let to = Math.max(cursor2, oldCursor);
4798
+ let from = Math.min(cursor, oldCursor);
4799
+ let to = Math.max(cursor, oldCursor);
4653
4800
  tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
4654
4801
  from = Math.min(from, fromB);
4655
4802
  to = Math.max(to, toB);
@@ -4663,19 +4810,19 @@ var image = (_options = {}) => {
4663
4810
  add: buildDecorations(tr.state, from, to)
4664
4811
  });
4665
4812
  },
4666
- provide: (field) => EditorView20.decorations.from(field)
4813
+ provide: (field) => EditorView21.decorations.from(field)
4667
4814
  })
4668
4815
  ];
4669
4816
  };
4670
4817
  var buildDecorations = (state, from, to) => {
4671
4818
  const decorations2 = [];
4672
- const cursor2 = state.selection.main.head;
4819
+ const cursor = state.selection.main.head;
4673
4820
  syntaxTree5(state).iterate({
4674
4821
  enter: (node) => {
4675
4822
  if (node.name === "Image") {
4676
4823
  const urlNode = node.node.getChild("URL");
4677
4824
  if (urlNode) {
4678
- const hide2 = state.readOnly || cursor2 < node.from || cursor2 > node.to || !state.field(focusField);
4825
+ const hide2 = state.readOnly || cursor < node.from || cursor > node.to || !state.field(focusField);
4679
4826
  const url = state.sliceDoc(urlNode.from, urlNode.to);
4680
4827
  if (url.match(/^https?:\/\//) === null && url.match(/^file?:\/\//) === null) {
4681
4828
  return;
@@ -4723,10 +4870,10 @@ var ImageWidget = class extends WidgetType5 {
4723
4870
  };
4724
4871
 
4725
4872
  // src/extensions/markdown/styles.ts
4726
- import { EditorView as EditorView21 } from "@codemirror/view";
4873
+ import { EditorView as EditorView22 } from "@codemirror/view";
4727
4874
  var bulletListIndentationWidth = 24;
4728
4875
  var orderedListIndentationWidth = 36;
4729
- var formattingStyles = EditorView21.theme({
4876
+ var formattingStyles = EditorView22.theme({
4730
4877
  /**
4731
4878
  * Horizontal rule.
4732
4879
  */
@@ -4735,7 +4882,7 @@ var formattingStyles = EditorView21.theme({
4735
4882
  width: "100%",
4736
4883
  height: "0",
4737
4884
  verticalAlign: "middle",
4738
- borderTop: "1px solid var(--dx-cmSeparator)",
4885
+ borderTop: "1px solid var(--color-cm-separator)",
4739
4886
  opacity: 0.5
4740
4887
  },
4741
4888
  /**
@@ -4758,19 +4905,44 @@ var formattingStyles = EditorView21.theme({
4758
4905
  * Blockquote.
4759
4906
  */
4760
4907
  "& .cm-blockquote": {
4761
- background: "var(--dx-cmCodeblock)",
4762
- borderLeft: "2px solid var(--dx-cmSeparator)",
4908
+ background: "var(--color-cm-codeblock)",
4909
+ borderLeft: "2px solid var(--color-cm-separator)",
4763
4910
  paddingLeft: "1rem",
4764
- margin: "0"
4911
+ margin: 0
4765
4912
  },
4766
4913
  /**
4767
4914
  * Code and codeblocks.
4768
4915
  */
4916
+ "& code": {
4917
+ fontFamily: fontMono,
4918
+ color: "var(--color-cm-code)",
4919
+ whiteSpace: "nowrap"
4920
+ },
4769
4921
  "& .cm-code": {
4770
- fontFamily: fontMono
4922
+ fontFamily: fontMono,
4923
+ color: "var(--color-cm-code)"
4924
+ },
4925
+ // Inline code spans (triggered by backticks) use `cm-code-inline` + `font-mono`.
4926
+ // Different monospace font metrics can slightly overflow the fixed CodeMirror line box,
4927
+ // so constrain them to the target 24px height.
4928
+ "& .cm-code-inline": {
4929
+ fontFamily: fontMono,
4930
+ height: "24px",
4931
+ // display: 'inline-flex',
4932
+ alignItems: "center",
4933
+ overflow: "hidden",
4934
+ whiteSpace: "nowrap",
4935
+ color: "var(--color-cm-code-inline)"
4936
+ },
4937
+ "& .cm-code-mark": {
4938
+ fontFamily: fontMono,
4939
+ height: "24px",
4940
+ display: "inline-flex",
4941
+ alignItems: "center",
4942
+ overflow: "hidden"
4771
4943
  },
4772
4944
  "& .cm-codeblock-line": {
4773
- background: "var(--dx-cmCodeblock)",
4945
+ background: "var(--color-cm-codeblock)",
4774
4946
  paddingInline: "1rem !important"
4775
4947
  },
4776
4948
  "& .cm-codeblock-start": {
@@ -4799,16 +4971,24 @@ var formattingStyles = EditorView21.theme({
4799
4971
  */
4800
4972
  ".cm-table *": {
4801
4973
  fontFamily: fontMono,
4974
+ lineHeight: 1.5,
4802
4975
  textDecoration: "none !important"
4803
4976
  },
4804
4977
  ".cm-table-head": {
4805
4978
  padding: "2px 16px 2px 0px",
4979
+ overflowWrap: "break-word",
4980
+ whiteSpace: "pre-wrap",
4981
+ wordBreak: "keep-all",
4806
4982
  textAlign: "left",
4807
- borderBottom: "1px solid var(--dx-cmSeparator)",
4808
- color: "var(--dx-subdued)"
4983
+ color: "var(--color-subdued)",
4984
+ borderBottom: "1px solid var(--color-cm-separator)"
4809
4985
  },
4810
4986
  ".cm-table-cell": {
4811
- padding: "2px 16px 2px 0px"
4987
+ padding: "2px 16px 2px 0px",
4988
+ overflowWrap: "break-word",
4989
+ whiteSpace: "pre-wrap",
4990
+ wordBreak: "keep-all",
4991
+ verticalAlign: "top"
4812
4992
  },
4813
4993
  /**
4814
4994
  * Image.
@@ -4824,12 +5004,12 @@ var formattingStyles = EditorView21.theme({
4824
5004
  },
4825
5005
  ".cm-image-with-loader": {
4826
5006
  display: "block",
4827
- opacity: "0",
5007
+ opacity: 0,
4828
5008
  transitionDuration: "350ms",
4829
5009
  transitionProperty: "opacity"
4830
5010
  },
4831
5011
  ".cm-image-with-loader.cm-loaded-image": {
4832
- opacity: "1"
5012
+ opacity: 1
4833
5013
  },
4834
5014
  ".cm-image-wrapper": {
4835
5015
  "grid-template-columns": "1fr",
@@ -4848,17 +5028,17 @@ var formattingStyles = EditorView21.theme({
4848
5028
  // src/extensions/markdown/table.ts
4849
5029
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4850
5030
  import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4851
- import { Decoration as Decoration10, EditorView as EditorView22, WidgetType as WidgetType6 } from "@codemirror/view";
5031
+ import { Decoration as Decoration10, EditorView as EditorView23, WidgetType as WidgetType6 } from "@codemirror/view";
4852
5032
  var table = (options = {}) => {
4853
5033
  return StateField8.define({
4854
5034
  create: (state) => update(state, options),
4855
5035
  update: (_, tr) => update(tr.state, options),
4856
- provide: (field) => EditorView22.decorations.from(field)
5036
+ provide: (field) => EditorView23.decorations.from(field)
4857
5037
  });
4858
5038
  };
4859
5039
  var update = (state, _options) => {
4860
5040
  const builder = new RangeSetBuilder4();
4861
- const cursor2 = state.selection.main.head;
5041
+ const cursor = state.selection.main.head;
4862
5042
  const tables = [];
4863
5043
  const getTable = () => tables[tables.length - 1];
4864
5044
  const getRow = () => {
@@ -4896,7 +5076,7 @@ var update = (state, _options) => {
4896
5076
  }
4897
5077
  });
4898
5078
  tables.forEach((table2) => {
4899
- const replace = state.readOnly || cursor2 < table2.from || cursor2 > table2.to;
5079
+ const replace = state.readOnly || cursor < table2.from || cursor > table2.to;
4900
5080
  if (replace) {
4901
5081
  builder.add(table2.from, table2.to, Decoration10.replace({
4902
5082
  block: true,
@@ -4910,6 +5090,26 @@ var update = (state, _options) => {
4910
5090
  });
4911
5091
  return builder.finish();
4912
5092
  };
5093
+ var renderCellContent = (el, text) => {
5094
+ const parts = text.split(/(`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_)/);
5095
+ for (const part of parts) {
5096
+ if (part.length > 2 && part.startsWith("`") && part.endsWith("`")) {
5097
+ const code = document.createElement("code");
5098
+ code.textContent = part.slice(1, -1);
5099
+ el.appendChild(code);
5100
+ } else if (part.startsWith("**") && part.endsWith("**") || part.startsWith("__") && part.endsWith("__")) {
5101
+ const strong = document.createElement("strong");
5102
+ strong.textContent = part.slice(2, -2);
5103
+ el.appendChild(strong);
5104
+ } else if (part.startsWith("*") && part.endsWith("*") || part.startsWith("_") && part.endsWith("_")) {
5105
+ const em = document.createElement("em");
5106
+ em.textContent = part.slice(1, -1);
5107
+ el.appendChild(em);
5108
+ } else {
5109
+ el.appendChild(document.createTextNode(part));
5110
+ }
5111
+ }
5112
+ };
4913
5113
  var TableWidget = class extends WidgetType6 {
4914
5114
  _table;
4915
5115
  constructor(_table) {
@@ -4929,7 +5129,7 @@ var TableWidget = class extends WidgetType6 {
4929
5129
  this._table.header?.forEach((cell) => {
4930
5130
  const th = document.createElement("th");
4931
5131
  th.setAttribute("class", "cm-table-head");
4932
- tr.appendChild(th).textContent = cell;
5132
+ renderCellContent(tr.appendChild(th), cell);
4933
5133
  });
4934
5134
  const body = table2.appendChild(document.createElement("tbody"));
4935
5135
  this._table.rows?.forEach((row) => {
@@ -4937,7 +5137,7 @@ var TableWidget = class extends WidgetType6 {
4937
5137
  row.forEach((cell) => {
4938
5138
  const td = document.createElement("td");
4939
5139
  td.setAttribute("class", "cm-table-cell");
4940
- tr2.appendChild(td).textContent = cell;
5140
+ renderCellContent(tr2.appendChild(td), cell);
4941
5141
  });
4942
5142
  });
4943
5143
  return div;
@@ -4945,7 +5145,7 @@ var TableWidget = class extends WidgetType6 {
4945
5145
  };
4946
5146
 
4947
5147
  // src/extensions/markdown/decorate.ts
4948
- var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/markdown/decorate.ts";
5148
+ var __dxlog_file12 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/markdown/decorate.ts";
4949
5149
  var Unicode = {
4950
5150
  emDash: "\u2014",
4951
5151
  bullet: "\u2022",
@@ -4968,7 +5168,6 @@ var LinkButton = class extends WidgetType7 {
4968
5168
  eq(other) {
4969
5169
  return this.url === other.url;
4970
5170
  }
4971
- // TODO(burdon): Create icon and link directly without react?
4972
5171
  toDOM(view) {
4973
5172
  const el = document.createElement("span");
4974
5173
  this.render(el, {
@@ -5045,10 +5244,10 @@ var fencedCodeLine = Decoration11.line({
5045
5244
  class: "cm-code cm-codeblock-line"
5046
5245
  });
5047
5246
  var fencedCodeLineFirst = Decoration11.line({
5048
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-start")
5247
+ class: "cm-code cm-codeblock-line cm-codeblock-start"
5049
5248
  });
5050
5249
  var fencedCodeLineLast = Decoration11.line({
5051
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-end")
5250
+ class: "cm-code cm-codeblock-line cm-codeblock-end"
5052
5251
  });
5053
5252
  var commentBlockLine = fencedCodeLine;
5054
5253
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -5080,15 +5279,7 @@ var buildDecorations2 = (view, options, focus2) => {
5080
5279
  const { state } = view;
5081
5280
  const headerLevels = [];
5082
5281
  const getHeaderLevels = (node, level) => {
5083
- invariant4(level > 0, void 0, {
5084
- F: __dxlog_file11,
5085
- L: 180,
5086
- S: void 0,
5087
- A: [
5088
- "level > 0",
5089
- ""
5090
- ]
5091
- });
5282
+ invariant4(level > 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 160, S: void 0, A: ["level > 0", ""] });
5092
5283
  if (level > headerLevels.length) {
5093
5284
  const len = headerLevels.length;
5094
5285
  headerLevels.length = level;
@@ -5119,15 +5310,7 @@ var buildDecorations2 = (view, options, focus2) => {
5119
5310
  listLevels.pop();
5120
5311
  };
5121
5312
  const getCurrentListLevel = () => {
5122
- invariant4(listLevels.length, void 0, {
5123
- F: __dxlog_file11,
5124
- L: 202,
5125
- S: void 0,
5126
- A: [
5127
- "listLevels.length",
5128
- ""
5129
- ]
5130
- });
5313
+ invariant4(listLevels.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 192, S: void 0, A: ["listLevels.length", ""] });
5131
5314
  return listLevels[listLevels.length - 1];
5132
5315
  };
5133
5316
  const enterNode = (node) => {
@@ -5165,13 +5348,13 @@ var buildDecorations2 = (view, options, focus2) => {
5165
5348
  deco: hide
5166
5349
  });
5167
5350
  } else {
5168
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5351
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + "). ";
5169
5352
  if (num.length) {
5170
5353
  atomicDecoRanges.push({
5171
5354
  from: mark.from,
5172
5355
  to: mark.from + len,
5173
5356
  deco: Decoration11.replace({
5174
- widget: new TextWidget(num, markdownTheme.heading(level))
5357
+ widget: new TextWidget(num, markdownTheme.heading(level).className)
5175
5358
  })
5176
5359
  });
5177
5360
  }
@@ -5348,11 +5531,11 @@ var buildDecorations2 = (view, options, focus2) => {
5348
5531
  }
5349
5532
  decoRanges.push({
5350
5533
  from: marks[0].to,
5351
- to: marks[1].from,
5534
+ to: !editing && options.renderLinkButton ? node.to : marks[1].from,
5352
5535
  deco: Decoration11.mark({
5353
5536
  tagName: "a",
5354
5537
  attributes: {
5355
- class: "cm-link",
5538
+ class: options.renderLinkButton ? "cm-link cm-link-with-button" : "cm-link",
5356
5539
  href: url,
5357
5540
  rel: "noreferrer",
5358
5541
  target: "_blank"
@@ -5430,8 +5613,11 @@ var buildDecorations2 = (view, options, focus2) => {
5430
5613
  deco.add(from, to, d);
5431
5614
  }
5432
5615
  const atomicDeco = new RangeSetBuilder5();
5433
- for (const { from, to, deco: d } of atomicDecoRanges) {
5434
- atomicDeco.add(from, to, d);
5616
+ for (const { from, to, deco: deco2 } of atomicDecoRanges) {
5617
+ if (from < to && state.doc.lineAt(from).number !== state.doc.lineAt(to).number) {
5618
+ continue;
5619
+ }
5620
+ atomicDeco.add(from, to, deco2);
5435
5621
  }
5436
5622
  return {
5437
5623
  deco: deco.finish(),
@@ -5441,7 +5627,7 @@ var buildDecorations2 = (view, options, focus2) => {
5441
5627
  var forceUpdate = StateEffect7.define();
5442
5628
  var decorateMarkdown = (options = {}) => {
5443
5629
  return [
5444
- ViewPlugin14.fromClass(class {
5630
+ ViewPlugin15.fromClass(class {
5445
5631
  deco;
5446
5632
  atomicDeco;
5447
5633
  pendingUpdate;
@@ -5476,9 +5662,9 @@ var decorateMarkdown = (options = {}) => {
5476
5662
  }
5477
5663
  }, {
5478
5664
  provide: (plugin) => [
5479
- Prec4.low(EditorView23.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5480
- EditorView23.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5481
- EditorView23.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5665
+ Prec4.low(EditorView24.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5666
+ EditorView24.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5667
+ EditorView24.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5482
5668
  ]
5483
5669
  }),
5484
5670
  image(),
@@ -5510,8 +5696,7 @@ var linkTooltip = (renderTooltip) => {
5510
5696
  return {
5511
5697
  pos: link.from,
5512
5698
  end: link.to,
5513
- // NOTE: Forcing above causes the tooltip to flicker.
5514
- // above: true,
5699
+ above: true,
5515
5700
  create: () => {
5516
5701
  const el = document.createElement("div");
5517
5702
  el.className = tooltipContent({});
@@ -5527,16 +5712,13 @@ var linkTooltip = (renderTooltip) => {
5527
5712
  };
5528
5713
  }
5529
5714
  };
5530
- }, {
5531
- // NOTE: 0 = default of 300ms.
5532
- hoverTime: 1
5533
5715
  });
5534
5716
  };
5535
5717
 
5536
5718
  // src/extensions/mention.ts
5537
5719
  import { autocompletion } from "@codemirror/autocomplete";
5538
- import { log as log8 } from "@dxos/log";
5539
- var __dxlog_file12 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/mention.ts";
5720
+ import { log as log9 } from "@dxos/log";
5721
+ var __dxlog_file13 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/mention.ts";
5540
5722
  var mention = ({ debug, onSearch }) => {
5541
5723
  return autocompletion({
5542
5724
  // TODO(burdon): Not working.
@@ -5548,14 +5730,9 @@ var mention = ({ debug, onSearch }) => {
5548
5730
  icons: false,
5549
5731
  override: [
5550
5732
  (context) => {
5551
- log8.info("completion context", {
5733
+ log9.info("completion context", {
5552
5734
  context
5553
- }, {
5554
- F: __dxlog_file12,
5555
- L: 27,
5556
- S: void 0,
5557
- C: (f, a) => f(...a)
5558
- });
5735
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 18, S: void 0 });
5559
5736
  const match = context.matchBefore(/@(\w+)?/);
5560
5737
  if (!match || match.from === match.to && !context.explicit) {
5561
5738
  return null;
@@ -5634,7 +5811,7 @@ import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5634
5811
  import { StateField as StateField10 } from "@codemirror/state";
5635
5812
  import { Facet as Facet2 } from "@codemirror/state";
5636
5813
  import { invariant as invariant5 } from "@dxos/invariant";
5637
- var __dxlog_file13 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/tree.ts";
5814
+ var __dxlog_file14 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/tree.ts";
5638
5815
  var itemToJSON = ({ type, index, level, lineRange, contentRange, children }) => {
5639
5816
  return {
5640
5817
  type,
@@ -5788,15 +5965,7 @@ var outlinerTree = (_options = {}) => {
5788
5965
  break;
5789
5966
  }
5790
5967
  case "BulletList": {
5791
- invariant5(current, void 0, {
5792
- F: __dxlog_file13,
5793
- L: 219,
5794
- S: void 0,
5795
- A: [
5796
- "current",
5797
- ""
5798
- ]
5799
- });
5968
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 169, S: void 0, A: ["current", ""] });
5800
5969
  parent = current;
5801
5970
  if (current) {
5802
5971
  current.lineRange.to = current.node.from;
@@ -5805,15 +5974,7 @@ var outlinerTree = (_options = {}) => {
5805
5974
  break;
5806
5975
  }
5807
5976
  case "ListItem": {
5808
- invariant5(parent, void 0, {
5809
- F: __dxlog_file13,
5810
- L: 228,
5811
- S: void 0,
5812
- A: [
5813
- "parent",
5814
- ""
5815
- ]
5816
- });
5977
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 179, S: void 0, A: ["parent", ""] });
5817
5978
  const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
5818
5979
  const docRange = {
5819
5980
  from: state.doc.lineAt(node.from).from,
@@ -5847,42 +6008,18 @@ var outlinerTree = (_options = {}) => {
5847
6008
  break;
5848
6009
  }
5849
6010
  case "ListMark": {
5850
- invariant5(current, void 0, {
5851
- F: __dxlog_file13,
5852
- L: 272,
5853
- S: void 0,
5854
- A: [
5855
- "current",
5856
- ""
5857
- ]
5858
- });
6011
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 219, S: void 0, A: ["current", ""] });
5859
6012
  current.type = "bullet";
5860
6013
  current.contentRange.from = node.from + "- ".length;
5861
6014
  break;
5862
6015
  }
5863
6016
  case "Task": {
5864
- invariant5(current, void 0, {
5865
- F: __dxlog_file13,
5866
- L: 278,
5867
- S: void 0,
5868
- A: [
5869
- "current",
5870
- ""
5871
- ]
5872
- });
6017
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 226, S: void 0, A: ["current", ""] });
5873
6018
  current.type = "task";
5874
6019
  break;
5875
6020
  }
5876
6021
  case "TaskMarker": {
5877
- invariant5(current, void 0, {
5878
- F: __dxlog_file13,
5879
- L: 283,
5880
- S: void 0,
5881
- A: [
5882
- "current",
5883
- ""
5884
- ]
5885
- });
6022
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 232, S: void 0, A: ["current", ""] });
5886
6023
  current.contentRange.from = node.from + "[ ] ".length;
5887
6024
  break;
5888
6025
  }
@@ -5890,29 +6027,13 @@ var outlinerTree = (_options = {}) => {
5890
6027
  },
5891
6028
  leave: (node) => {
5892
6029
  if (node.name === "BulletList") {
5893
- invariant5(parent, void 0, {
5894
- F: __dxlog_file13,
5895
- L: 291,
5896
- S: void 0,
5897
- A: [
5898
- "parent",
5899
- ""
5900
- ]
5901
- });
6030
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 240, S: void 0, A: ["parent", ""] });
5902
6031
  prevSiblings[level--] = void 0;
5903
6032
  parent = parent.parent;
5904
6033
  }
5905
6034
  }
5906
6035
  });
5907
- invariant5(tree, void 0, {
5908
- F: __dxlog_file13,
5909
- L: 298,
5910
- S: void 0,
5911
- A: [
5912
- "tree",
5913
- ""
5914
- ]
5915
- });
6036
+ invariant5(tree, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 246, S: void 0, A: ["tree", ""] });
5916
6037
  return tree;
5917
6038
  };
5918
6039
  return [
@@ -6197,17 +6318,17 @@ var commands = () => keymap11.of([
6197
6318
 
6198
6319
  // src/extensions/outliner/outliner.ts
6199
6320
  import { Prec as Prec5 } from "@codemirror/state";
6200
- import { Decoration as Decoration12, EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6201
- import { mx as mx7 } from "@dxos/ui-theme";
6321
+ import { Decoration as Decoration12, EditorView as EditorView26, ViewPlugin as ViewPlugin18 } from "@codemirror/view";
6322
+ import { mx as mx6 } from "@dxos/ui-theme";
6202
6323
 
6203
6324
  // src/extensions/outliner/editor.ts
6204
6325
  import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
6205
- import { ViewPlugin as ViewPlugin15 } from "@codemirror/view";
6206
- import { log as log9 } from "@dxos/log";
6207
- var __dxlog_file14 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6326
+ import { ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6327
+ import { log as log10 } from "@dxos/log";
6328
+ var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6208
6329
  var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
6209
6330
  var initialize = () => {
6210
- return ViewPlugin15.fromClass(class {
6331
+ return ViewPlugin16.fromClass(class {
6211
6332
  constructor(view) {
6212
6333
  const first = view.state.doc.lineAt(0);
6213
6334
  const text = view.state.sliceDoc(first.from, first.to);
@@ -6336,7 +6457,7 @@ var editor = () => [
6336
6457
  cancel = true;
6337
6458
  return;
6338
6459
  }
6339
- log9("change", {
6460
+ log10("change", {
6340
6461
  item,
6341
6462
  line: {
6342
6463
  from: line.from,
@@ -6354,35 +6475,20 @@ var editor = () => [
6354
6475
  text: insert.toString(),
6355
6476
  length: insert.length
6356
6477
  }
6357
- }, {
6358
- F: __dxlog_file14,
6359
- L: 164,
6360
- S: void 0,
6361
- C: (f, a) => f(...a)
6362
- });
6478
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 174, S: void 0 });
6363
6479
  }
6364
6480
  });
6365
6481
  if (changes.length > 0) {
6366
- log9("modified,", {
6482
+ log10("modified,", {
6367
6483
  changes
6368
- }, {
6369
- F: __dxlog_file14,
6370
- L: 175,
6371
- S: void 0,
6372
- C: (f, a) => f(...a)
6373
- });
6484
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 196, S: void 0 });
6374
6485
  return [
6375
6486
  {
6376
6487
  changes
6377
6488
  }
6378
6489
  ];
6379
6490
  } else if (cancel) {
6380
- log9("cancel", void 0, {
6381
- F: __dxlog_file14,
6382
- L: 178,
6383
- S: void 0,
6384
- C: (f, a) => f(...a)
6385
- });
6491
+ log10("cancel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 205, S: void 0 });
6386
6492
  return [];
6387
6493
  }
6388
6494
  return tr;
@@ -6390,10 +6496,10 @@ var editor = () => [
6390
6496
  ];
6391
6497
 
6392
6498
  // src/extensions/outliner/menu.ts
6393
- import { EditorView as EditorView24, ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6394
- import { addEventListener } from "@dxos/async";
6499
+ import { EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6500
+ import { addEventListener as addEventListener2 } from "@dxos/async";
6395
6501
  var menu = (options = {}) => [
6396
- ViewPlugin16.fromClass(class {
6502
+ ViewPlugin17.fromClass(class {
6397
6503
  view;
6398
6504
  tag;
6399
6505
  rafId;
@@ -6413,7 +6519,7 @@ var menu = (options = {}) => [
6413
6519
  }
6414
6520
  container.appendChild(this.tag);
6415
6521
  const handler = () => this.scheduleUpdate();
6416
- this.cleanup = addEventListener(container, "scroll", handler);
6522
+ this.cleanup = addEventListener2(container, "scroll", handler);
6417
6523
  this.scheduleUpdate();
6418
6524
  }
6419
6525
  destroy() {
@@ -6455,7 +6561,7 @@ var menu = (options = {}) => [
6455
6561
  this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
6456
6562
  }
6457
6563
  }),
6458
- EditorView24.theme({
6564
+ EditorView25.theme({
6459
6565
  ".cm-popover-trigger": {
6460
6566
  position: "fixed",
6461
6567
  padding: "0",
@@ -6491,12 +6597,12 @@ var outliner = (_options = {}) => [
6491
6597
  listPaddingLeft: 8
6492
6598
  }),
6493
6599
  // Researve space for menu.
6494
- EditorView25.contentAttributes.of({
6495
- class: "is-full !mr-[3rem]"
6600
+ EditorView26.contentAttributes.of({
6601
+ class: "w-full !mr-[3rem]"
6496
6602
  })
6497
6603
  ];
6498
6604
  var decorations = () => [
6499
- ViewPlugin17.fromClass(class {
6605
+ ViewPlugin18.fromClass(class {
6500
6606
  decorations = Decoration12.none;
6501
6607
  constructor(view) {
6502
6608
  this.updateDecorations(view.state, view);
@@ -6521,7 +6627,7 @@ var decorations = () => [
6521
6627
  const lineTo = doc.lineAt(item.contentRange.to);
6522
6628
  const isSelected = selection.includes(item.index) || item === current;
6523
6629
  decorations2.push(Decoration12.line({
6524
- 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"))
6630
+ 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"))
6525
6631
  }).range(line.from, line.from));
6526
6632
  }
6527
6633
  }
@@ -6531,7 +6637,7 @@ var decorations = () => [
6531
6637
  decorations: (v) => v.decorations
6532
6638
  }),
6533
6639
  // Theme.
6534
- EditorView25.theme(Object.assign({
6640
+ EditorView26.theme(Object.assign({
6535
6641
  ".cm-list-item": {
6536
6642
  borderLeftWidth: "1px",
6537
6643
  borderRightWidth: "1px",
@@ -6556,38 +6662,67 @@ var decorations = () => [
6556
6662
  marginBottom: "2px"
6557
6663
  },
6558
6664
  ".cm-list-item-focused": {
6559
- borderColor: "var(--dx-neutralFocusIndicator)"
6665
+ borderColor: "var(--color-focus-ring-subtle)"
6560
6666
  },
6561
6667
  "&:focus-within .cm-list-item-selected": {
6562
- borderColor: "var(--dx-separator)"
6668
+ borderColor: "var(--color-separator)"
6563
6669
  }
6564
6670
  }))
6565
6671
  ];
6566
6672
 
6567
6673
  // src/extensions/preview/preview.ts
6568
6674
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
6569
- import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField11 } from "@codemirror/state";
6570
- import { Decoration as Decoration13, EditorView as EditorView26, WidgetType as WidgetType8 } from "@codemirror/view";
6675
+ import { RangeSetBuilder as RangeSetBuilder6, StateEffect as StateEffect9, StateField as StateField11 } from "@codemirror/state";
6676
+ import { Decoration as Decoration13, EditorView as EditorView27, ViewPlugin as ViewPlugin19, WidgetType as WidgetType8 } from "@codemirror/view";
6677
+ import { DXN, Entity } from "@dxos/echo";
6678
+ var labelResolvedEffect = StateEffect9.define();
6571
6679
  var preview = (options = {}) => {
6680
+ const viewRef = {
6681
+ current: void 0
6682
+ };
6572
6683
  return [
6573
6684
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
6574
6685
  // "Block decorations may not be specified via plugins".
6575
6686
  StateField11.define({
6576
- create: (state) => buildDecorations3(state, options),
6687
+ create: (state) => buildDecorations3(state, options, viewRef),
6577
6688
  update: (decorations2, tr) => {
6578
- if (tr.docChanged) {
6579
- return buildDecorations3(tr.state, options);
6689
+ if (tr.docChanged || tr.effects.some((effect) => effect.is(labelResolvedEffect))) {
6690
+ return buildDecorations3(tr.state, options, viewRef);
6580
6691
  }
6581
6692
  return decorations2.map(tr.changes);
6582
6693
  },
6583
6694
  provide: (field) => [
6584
- EditorView26.decorations.from(field),
6585
- EditorView26.atomicRanges.of((view) => view.state.field(field))
6695
+ EditorView27.decorations.from(field),
6696
+ EditorView27.atomicRanges.of((view) => view.state.field(field))
6586
6697
  ]
6698
+ }),
6699
+ ViewPlugin19.define((view) => {
6700
+ viewRef.current = view;
6701
+ return {
6702
+ destroy() {
6703
+ viewRef.current = void 0;
6704
+ }
6705
+ };
6587
6706
  })
6588
6707
  ];
6589
6708
  };
6590
- var buildDecorations3 = (state, options) => {
6709
+ var resolveLabel = (db, dxnStr, viewRef) => {
6710
+ const dxn = DXN.tryParse(dxnStr);
6711
+ if (!dxn) {
6712
+ return;
6713
+ }
6714
+ const ref = db.makeRef(dxn);
6715
+ const target = ref.target;
6716
+ if (target) {
6717
+ return Entity.getLabel(target);
6718
+ }
6719
+ void ref.tryLoad().then(() => {
6720
+ viewRef.current?.dispatch({
6721
+ effects: labelResolvedEffect.of(void 0)
6722
+ });
6723
+ });
6724
+ };
6725
+ var buildDecorations3 = (state, options, viewRef) => {
6591
6726
  const builder = new RangeSetBuilder6();
6592
6727
  syntaxTree10(state).iterate({
6593
6728
  enter: (node) => {
@@ -6599,8 +6734,13 @@ var buildDecorations3 = (state, options) => {
6599
6734
  case "Link": {
6600
6735
  const link = getLinkRef(state, node.node);
6601
6736
  if (link) {
6737
+ const resolved = options.db ? resolveLabel(options.db, link.dxn, viewRef) : void 0;
6738
+ const displayLink = resolved ? {
6739
+ ...link,
6740
+ label: resolved
6741
+ } : link;
6602
6742
  builder.add(node.from, node.to, Decoration13.replace({
6603
- widget: new PreviewInlineWidget(options, link),
6743
+ widget: new PreviewInlineWidget(options, displayLink),
6604
6744
  side: 1
6605
6745
  }));
6606
6746
  }
@@ -6676,7 +6816,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6676
6816
  }
6677
6817
  toDOM(_view) {
6678
6818
  const root = document.createElement("div");
6679
- root.classList.add("cm-preview-block", "density-fine");
6819
+ root.classList.add("cm-preview-block", "dx-density-fine");
6680
6820
  this._options.addBlockContainer?.({
6681
6821
  link: this._link,
6682
6822
  el: root
@@ -6692,7 +6832,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6692
6832
  };
6693
6833
 
6694
6834
  // src/extensions/replacer.ts
6695
- import { EditorView as EditorView27 } from "@codemirror/view";
6835
+ import { EditorView as EditorView28 } from "@codemirror/view";
6696
6836
  var defaultReplacements = [
6697
6837
  {
6698
6838
  input: "--",
@@ -6755,7 +6895,7 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6755
6895
  const sortedReplacements = [
6756
6896
  ...replacements
6757
6897
  ].sort((a, b) => b.input.length - a.input.length);
6758
- return EditorView27.inputHandler.of((view, from, to, insert) => {
6898
+ return EditorView28.inputHandler.of((view, from, to, insert) => {
6759
6899
  if (insert.length !== 1) {
6760
6900
  return false;
6761
6901
  }
@@ -6789,12 +6929,69 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6789
6929
  });
6790
6930
  };
6791
6931
 
6932
+ // src/extensions/snippets.ts
6933
+ import { keymap as keymap12 } from "@codemirror/view";
6934
+ var defaultItems = [
6935
+ "hello world!",
6936
+ "this is a test.",
6937
+ "this is [DXOS](https://dxos.org)"
6938
+ ];
6939
+ var snippets2 = ({ delay = 75, items = defaultItems } = {}) => {
6940
+ let timer;
6941
+ let index = 0;
6942
+ return [
6943
+ keymap12.of([
6944
+ {
6945
+ // Reset.
6946
+ key: "alt-meta-'",
6947
+ run: () => {
6948
+ clearTimeout(timer);
6949
+ index = 0;
6950
+ return true;
6951
+ }
6952
+ },
6953
+ {
6954
+ // Next snippet.
6955
+ // TODO(burdon): Press 1-9 to select snippet?
6956
+ key: "Shift-Meta-'",
6957
+ run: (view) => {
6958
+ clearTimeout(timer);
6959
+ const text = items[index++];
6960
+ if (index === items?.length) {
6961
+ index = 0;
6962
+ }
6963
+ let offset = 0;
6964
+ const insert = (delayMs = 0) => {
6965
+ timer = setTimeout(() => {
6966
+ const pos = view.state.selection.main.head;
6967
+ view.dispatch({
6968
+ changes: {
6969
+ from: pos,
6970
+ insert: text[offset++]
6971
+ },
6972
+ selection: {
6973
+ anchor: pos + 1
6974
+ }
6975
+ });
6976
+ if (offset < text.length) {
6977
+ insert(Math.random() * delay * (text[offset] === " " ? 2 : 1));
6978
+ }
6979
+ }, delayMs);
6980
+ };
6981
+ insert();
6982
+ return true;
6983
+ }
6984
+ }
6985
+ ])
6986
+ ];
6987
+ };
6988
+
6792
6989
  // src/extensions/submit.ts
6793
6990
  import { Prec as Prec6 } from "@codemirror/state";
6794
- import { keymap as keymap12 } from "@codemirror/view";
6991
+ import { keymap as keymap13 } from "@codemirror/view";
6795
6992
  var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6796
6993
  return [
6797
- Prec6.highest(keymap12.of([
6994
+ Prec6.highest(keymap13.of([
6798
6995
  {
6799
6996
  key: "Enter",
6800
6997
  preventDefault: true,
@@ -6839,6 +7036,7 @@ var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6839
7036
  // src/extensions/tags/extended-markdown.ts
6840
7037
  import { xmlLanguage } from "@codemirror/lang-xml";
6841
7038
  import { parseMixed } from "@lezer/common";
7039
+ var escapeRegExpSource = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6842
7040
  var extendedMarkdown = ({ registry } = {}) => {
6843
7041
  return [
6844
7042
  createMarkdownExtensions({
@@ -6850,13 +7048,65 @@ var extendedMarkdown = ({ registry } = {}) => {
6850
7048
  {
6851
7049
  name: "SetextHeading",
6852
7050
  parse: () => false
6853
- }
7051
+ },
7052
+ // Custom XML block parser that keeps registered tags as a single HTMLBlock
7053
+ // even when their content contains blank lines.
7054
+ ...xmlBlockParsers(registry)
6854
7055
  ]
6855
7056
  }
6856
7057
  ]
6857
7058
  })
6858
7059
  ];
6859
7060
  };
7061
+ var xmlBlockParsers = (registry) => {
7062
+ const customTags = Object.keys(registry ?? {});
7063
+ if (customTags.length === 0) {
7064
+ return [];
7065
+ }
7066
+ const tagPattern = customTags.map(escapeRegExpSource).join("|");
7067
+ const selfClosePattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/>\\s*$`);
7068
+ const openPattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/?>`);
7069
+ return [
7070
+ {
7071
+ name: "XMLBlock",
7072
+ before: "HTMLBlock",
7073
+ parse: (cx, line) => {
7074
+ const match = openPattern.exec(line.text);
7075
+ if (!match) {
7076
+ return false;
7077
+ }
7078
+ if (selfClosePattern.test(line.text)) {
7079
+ const end2 = cx.lineStart + line.text.length;
7080
+ cx.addElement(cx.elt("HTMLBlock", cx.lineStart, end2));
7081
+ cx.nextLine();
7082
+ return true;
7083
+ }
7084
+ if (match[0].trimEnd().endsWith("/>")) {
7085
+ return false;
7086
+ }
7087
+ const tagName = match[1];
7088
+ const closeTag = `</${tagName}>`;
7089
+ const start = cx.lineStart;
7090
+ if (line.text.includes(closeTag)) {
7091
+ cx.addElement(cx.elt("HTMLBlock", start, start + line.text.length));
7092
+ cx.nextLine();
7093
+ return true;
7094
+ }
7095
+ let end = cx.lineStart + line.text.length;
7096
+ while (cx.nextLine()) {
7097
+ end = cx.lineStart + line.text.length;
7098
+ if (line.text.includes(closeTag)) {
7099
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7100
+ cx.nextLine();
7101
+ return true;
7102
+ }
7103
+ }
7104
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7105
+ return true;
7106
+ }
7107
+ }
7108
+ ];
7109
+ };
6860
7110
  var mixedParser = (registry) => {
6861
7111
  const customTags = Object.keys(registry ?? {});
6862
7112
  const tagPattern = new RegExp(`<(${customTags.join("|")})`);
@@ -6890,219 +7140,793 @@ var mixedParser = (registry) => {
6890
7140
  });
6891
7141
  };
6892
7142
 
6893
- // src/extensions/tags/streamer.ts
6894
- import { StateEffect as StateEffect9, StateField as StateField12 } from "@codemirror/state";
6895
- import { Decoration as Decoration14, EditorView as EditorView28, ViewPlugin as ViewPlugin18, WidgetType as WidgetType9 } from "@codemirror/view";
6896
- import { Domino as Domino3 } from "@dxos/ui";
6897
- import { isTruthy as isTruthy4 } from "@dxos/util";
6898
- var BLINK_RATE = 2e3;
6899
- var streamer = (options = {}) => {
7143
+ // src/extensions/tags/fader.ts
7144
+ import { StateEffect as StateEffect10, StateField as StateField12 } from "@codemirror/state";
7145
+ import { Decoration as Decoration14, EditorView as EditorView29, ViewPlugin as ViewPlugin20 } from "@codemirror/view";
7146
+ var DEFAULT_REMOVAL_DELAY = 5e3;
7147
+ var DEFAULT_COALESCE_WINDOW = 100;
7148
+ var CLEANUP_INTERVAL = 1e3;
7149
+ var fader = (options = {}) => {
7150
+ const removalDelay = DEFAULT_REMOVAL_DELAY;
7151
+ const coalesceWindow = options.coalesce ?? DEFAULT_COALESCE_WINDOW;
7152
+ let lastCount = -1;
7153
+ const log12 = (expiries) => {
7154
+ if (expiries.length !== lastCount) {
7155
+ lastCount = expiries.length;
7156
+ }
7157
+ };
7158
+ const dequeue = StateEffect10.define();
7159
+ const fadeField = StateField12.define({
7160
+ create: () => ({
7161
+ decorations: Decoration14.none,
7162
+ expiries: [],
7163
+ batchStart: 0
7164
+ }),
7165
+ update: ({ decorations: decorations2, expiries, batchStart }, tr) => {
7166
+ for (const effect of tr.effects) {
7167
+ if (effect.is(dequeue)) {
7168
+ const now2 = effect.value;
7169
+ let removeCount = 0;
7170
+ while (removeCount < expiries.length && expiries[removeCount] <= now2) {
7171
+ removeCount++;
7172
+ }
7173
+ if (removeCount > 0) {
7174
+ expiries = expiries.slice(removeCount);
7175
+ let skipped = 0;
7176
+ decorations2 = decorations2.update({
7177
+ filter: () => {
7178
+ if (skipped < removeCount) {
7179
+ skipped++;
7180
+ return false;
7181
+ }
7182
+ return true;
7183
+ }
7184
+ });
7185
+ }
7186
+ }
7187
+ }
7188
+ if (!tr.docChanged) {
7189
+ log12(expiries);
7190
+ return {
7191
+ decorations: decorations2,
7192
+ expiries,
7193
+ batchStart
7194
+ };
7195
+ }
7196
+ let isReset = tr.state.doc.length === 0;
7197
+ if (!isReset && tr.startState.doc.length > 0) {
7198
+ tr.changes.iterChanges((fromA, toA) => {
7199
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7200
+ isReset = true;
7201
+ }
7202
+ });
7203
+ }
7204
+ if (isReset) {
7205
+ log12([]);
7206
+ return {
7207
+ decorations: Decoration14.none,
7208
+ expiries: [],
7209
+ batchStart: 0
7210
+ };
7211
+ }
7212
+ const now = Date.now();
7213
+ const add = [];
7214
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7215
+ if (toA === tr.startState.doc.length && inserted.length > 0) {
7216
+ add.push({
7217
+ from: fromB,
7218
+ to: toB
7219
+ });
7220
+ }
7221
+ });
7222
+ if (add.length > 0) {
7223
+ const canCoalesce = expiries.length > 0 && batchStart > 0 && now - batchStart < coalesceWindow;
7224
+ if (canCoalesce) {
7225
+ let lastFrom = -1;
7226
+ let lastTo = -1;
7227
+ decorations2.between(0, tr.state.doc.length, (from, to) => {
7228
+ lastFrom = from;
7229
+ lastTo = to;
7230
+ });
7231
+ if (lastFrom >= 0) {
7232
+ decorations2 = decorations2.update({
7233
+ filter: (from, to) => !(from === lastFrom && to === lastTo)
7234
+ });
7235
+ const mergedFrom = Math.min(lastFrom, add[0].from);
7236
+ const mergedTo = add[add.length - 1].to;
7237
+ decorations2 = decorations2.update({
7238
+ add: [
7239
+ Decoration14.mark({
7240
+ class: "cm-fader"
7241
+ }).range(mergedFrom, mergedTo)
7242
+ ]
7243
+ });
7244
+ expiries = [
7245
+ ...expiries.slice(0, -1),
7246
+ now + removalDelay
7247
+ ];
7248
+ }
7249
+ } else {
7250
+ batchStart = now;
7251
+ expiries = [
7252
+ ...expiries,
7253
+ now + removalDelay
7254
+ ];
7255
+ decorations2 = decorations2.update({
7256
+ add: add.map(({ from, to }) => Decoration14.mark({
7257
+ class: "cm-fader"
7258
+ }).range(from, to))
7259
+ });
7260
+ }
7261
+ }
7262
+ log12(expiries);
7263
+ return {
7264
+ decorations: decorations2,
7265
+ expiries,
7266
+ batchStart
7267
+ };
7268
+ },
7269
+ provide: (f) => EditorView29.decorations.from(f, (value) => value.decorations)
7270
+ });
7271
+ const cleanup = ViewPlugin20.fromClass(class {
7272
+ view;
7273
+ #timer;
7274
+ constructor(view) {
7275
+ this.view = view;
7276
+ this.#schedule();
7277
+ }
7278
+ update() {
7279
+ this.#schedule();
7280
+ }
7281
+ #schedule() {
7282
+ const { expiries } = this.view.state.field(fadeField);
7283
+ if (expiries.length === 0) {
7284
+ clearTimeout(this.#timer);
7285
+ this.#timer = void 0;
7286
+ return;
7287
+ }
7288
+ if (this.#timer !== void 0) {
7289
+ return;
7290
+ }
7291
+ const delay = Math.max(CLEANUP_INTERVAL, expiries[0] - Date.now());
7292
+ this.#timer = setTimeout(() => {
7293
+ this.#timer = void 0;
7294
+ this.view.dispatch({
7295
+ effects: dequeue.of(Date.now())
7296
+ });
7297
+ }, delay);
7298
+ }
7299
+ destroy() {
7300
+ clearTimeout(this.#timer);
7301
+ }
7302
+ });
6900
7303
  return [
6901
- options.cursor && cursor(),
6902
- options.fadeIn && fadeIn(typeof options.fadeIn === "object" ? options.fadeIn : {})
6903
- ].filter(isTruthy4);
6904
- };
6905
- var cursor = () => {
6906
- const hideCursor = StateEffect9.define();
6907
- const showCursor = StateField12.define({
6908
- create: () => true,
7304
+ fadeField,
7305
+ cleanup,
7306
+ EditorView29.theme({
7307
+ ".cm-fader": {
7308
+ animation: "fader 1s ease-out forwards"
7309
+ },
7310
+ "@keyframes fader": {
7311
+ "0%": {
7312
+ textShadow: "0 0 16px rgba(100, 200, 255, 1), 0 0 32px rgba(100, 200, 255, 0.6)"
7313
+ },
7314
+ "100%": {}
7315
+ }
7316
+ })
7317
+ ];
7318
+ };
7319
+
7320
+ // src/extensions/tags/typewriter.ts
7321
+ import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7322
+ import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7323
+ import { Domino as Domino3 } from "@dxos/ui";
7324
+ var typewriterBypass = Annotation3.define();
7325
+ var typewriterDrainingEffect = StateEffect11.define();
7326
+ var CURSOR_LINGER = 3e3;
7327
+ var FRAME_BUDGET_MS = 4;
7328
+ var CHARS_PER_FRAME = 5;
7329
+ var FLUSH_THRESHOLD = 2e3;
7330
+ var COMPACT_HEAD_THRESHOLD = 4096;
7331
+ var typewriter = (options = {}) => {
7332
+ const streamingTags = options.streamingTags ?? /* @__PURE__ */ new Set();
7333
+ const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;
7334
+ const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;
7335
+ const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;
7336
+ const suppressAppend = StateEffect11.define();
7337
+ const insertChunk = StateEffect11.define();
7338
+ const bufferField = StateField13.define({
7339
+ create: () => ({
7340
+ text: "",
7341
+ head: 0,
7342
+ insertAt: 0
7343
+ }),
6909
7344
  update: (value, tr) => {
7345
+ let { text, head, insertAt } = value;
6910
7346
  for (const effect of tr.effects) {
6911
- if (effect.is(hideCursor)) {
6912
- return false;
7347
+ if (effect.is(suppressAppend)) {
7348
+ if (text.length === head) {
7349
+ insertAt = effect.value.from;
7350
+ }
7351
+ text += effect.value.text;
7352
+ }
7353
+ if (effect.is(insertChunk)) {
7354
+ head += effect.value.text.length;
7355
+ insertAt = effect.value.from + effect.value.text.length;
7356
+ if (head >= COMPACT_HEAD_THRESHOLD || head > 0 && head * 2 >= text.length) {
7357
+ text = text.slice(head);
7358
+ head = 0;
7359
+ }
6913
7360
  }
6914
7361
  }
6915
7362
  if (tr.docChanged) {
6916
- return true;
7363
+ let isReset = tr.state.doc.length === 0;
7364
+ if (!isReset && tr.startState.doc.length > 0) {
7365
+ tr.changes.iterChanges((fromA, toA) => {
7366
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7367
+ isReset = true;
7368
+ }
7369
+ });
7370
+ }
7371
+ if (isReset) {
7372
+ return {
7373
+ text: "",
7374
+ head: 0,
7375
+ insertAt: 0
7376
+ };
7377
+ }
7378
+ if (!tr.effects.some((effect) => effect.is(insertChunk))) {
7379
+ insertAt = tr.changes.mapPos(Math.min(insertAt, tr.startState.doc.length));
7380
+ }
6917
7381
  }
6918
- return value;
7382
+ return {
7383
+ text,
7384
+ head,
7385
+ insertAt
7386
+ };
7387
+ }
7388
+ });
7389
+ const filter = EditorState3.transactionFilter.of((tr) => {
7390
+ if (!tr.docChanged) {
7391
+ return tr;
6919
7392
  }
7393
+ if (tr.annotation(typewriterBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7394
+ return tr;
7395
+ }
7396
+ let appendedText = "";
7397
+ let appendFrom = -1;
7398
+ let isAppendOnly = true;
7399
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
7400
+ if (toA === tr.startState.doc.length && fromA === toA && inserted.length > 0) {
7401
+ appendedText += inserted.sliceString(0);
7402
+ if (appendFrom === -1) {
7403
+ appendFrom = fromA;
7404
+ }
7405
+ } else {
7406
+ isAppendOnly = false;
7407
+ }
7408
+ });
7409
+ if (!isAppendOnly || appendedText.length === 0) {
7410
+ return tr;
7411
+ }
7412
+ return {
7413
+ changes: ChangeSet2.empty(tr.startState.doc.length),
7414
+ effects: suppressAppend.of({
7415
+ from: appendFrom,
7416
+ text: appendedText
7417
+ })
7418
+ };
6920
7419
  });
6921
- const timerPlugin = ViewPlugin18.fromClass(class {
7420
+ const drainPlugin = ViewPlugin21.fromClass(class {
6922
7421
  view;
6923
- timer;
7422
+ _raf;
7423
+ _activeStreamTag = null;
6924
7424
  constructor(view) {
6925
7425
  this.view = view;
6926
7426
  }
6927
7427
  update(update2) {
6928
- if (update2.docChanged) {
6929
- clearTimeout(this.timer);
6930
- this.timer = setTimeout(() => {
6931
- this.view.dispatch({
6932
- effects: hideCursor.of(null)
6933
- });
6934
- }, BLINK_RATE);
7428
+ const { text, head } = update2.state.field(bufferField);
7429
+ const pending = text.length - head;
7430
+ if (pending === 0) {
7431
+ this._activeStreamTag = null;
7432
+ }
7433
+ if (pending > 0 && this._raf === void 0) {
7434
+ this._start();
6935
7435
  }
6936
7436
  }
7437
+ _start() {
7438
+ queueMicrotask(() => {
7439
+ this.view.dispatch({
7440
+ effects: typewriterDrainingEffect.of(true),
7441
+ annotations: typewriterBypass.of(true)
7442
+ });
7443
+ });
7444
+ this._raf = requestAnimationFrame(this._tick);
7445
+ }
7446
+ _tick = () => {
7447
+ const { text, head, insertAt } = this.view.state.field(bufferField);
7448
+ const pending = text.length - head;
7449
+ if (pending === 0) {
7450
+ this.view.dispatch({
7451
+ effects: typewriterDrainingEffect.of(false),
7452
+ annotations: typewriterBypass.of(true)
7453
+ });
7454
+ this._raf = void 0;
7455
+ return;
7456
+ }
7457
+ if (pending > flushThreshold) {
7458
+ const chunk = text.slice(head);
7459
+ this._activeStreamTag = null;
7460
+ this.view.dispatch({
7461
+ changes: {
7462
+ from: insertAt,
7463
+ insert: chunk
7464
+ },
7465
+ effects: insertChunk.of({
7466
+ from: insertAt,
7467
+ text: chunk
7468
+ })
7469
+ });
7470
+ this._raf = requestAnimationFrame(this._tick);
7471
+ return;
7472
+ }
7473
+ const startTime = performance.now();
7474
+ let pos = head;
7475
+ let activeTag = this._activeStreamTag;
7476
+ let charsEmitted = 0;
7477
+ while (pos < text.length && performance.now() - startTime < frameBudgetMs) {
7478
+ const result = flushable(text, pos, streamingTags, activeTag);
7479
+ if (result.count === 0) {
7480
+ break;
7481
+ }
7482
+ if (charsEmitted > 0 && charsEmitted + result.count > charsPerFrame) {
7483
+ break;
7484
+ }
7485
+ if (result.enterTag) {
7486
+ activeTag = result.enterTag;
7487
+ }
7488
+ if (result.exitTag) {
7489
+ activeTag = null;
7490
+ }
7491
+ pos += result.count;
7492
+ charsEmitted += result.count;
7493
+ }
7494
+ const totalCount = pos - head;
7495
+ if (totalCount > 0) {
7496
+ const chunk = text.slice(head, head + totalCount);
7497
+ this._activeStreamTag = activeTag;
7498
+ this.view.dispatch({
7499
+ changes: {
7500
+ from: insertAt,
7501
+ insert: chunk
7502
+ },
7503
+ effects: insertChunk.of({
7504
+ from: insertAt,
7505
+ text: chunk
7506
+ })
7507
+ });
7508
+ }
7509
+ this._raf = requestAnimationFrame(this._tick);
7510
+ };
6937
7511
  destroy() {
6938
- clearTimeout(this.timer);
7512
+ if (this._raf !== void 0) {
7513
+ cancelAnimationFrame(this._raf);
7514
+ }
6939
7515
  }
6940
7516
  });
6941
- const cursorDecoration = StateField12.define({
6942
- create: () => Decoration14.none,
6943
- update: (_decorations, tr) => {
6944
- const show = tr.state.field(showCursor);
6945
- if (!show) {
6946
- return Decoration14.none;
7517
+ return [
7518
+ bufferField,
7519
+ filter,
7520
+ drainPlugin,
7521
+ options.cursor && typewriterCursor(bufferField)
7522
+ ].filter(Boolean);
7523
+ };
7524
+ var typewriterCursor = (bufferField) => {
7525
+ const hideCursor = StateEffect11.define();
7526
+ const visibilityField = StateField13.define({
7527
+ create: () => ({
7528
+ visible: false,
7529
+ insertAt: 0,
7530
+ lastNonWsAt: 0
7531
+ }),
7532
+ update: (value, tr) => {
7533
+ const { text, head, insertAt } = tr.state.field(bufferField);
7534
+ const pending = text.length - head;
7535
+ if (pending > 0) {
7536
+ let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));
7537
+ if (tr.docChanged) {
7538
+ tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
7539
+ const chunk = inserted.sliceString(0);
7540
+ if (chunk.trim().length > 0) {
7541
+ lastNonWsAt = _fromB + chunk.length;
7542
+ }
7543
+ });
7544
+ }
7545
+ return {
7546
+ visible: true,
7547
+ insertAt,
7548
+ lastNonWsAt
7549
+ };
7550
+ }
7551
+ for (const effect of tr.effects) {
7552
+ if (effect.is(hideCursor)) {
7553
+ return {
7554
+ ...value,
7555
+ visible: false
7556
+ };
7557
+ }
6947
7558
  }
6948
- const endPos = tr.state.doc.length;
6949
- return Decoration14.set([
6950
- Decoration14.widget({
7559
+ return value;
7560
+ }
7561
+ });
7562
+ const decorationField = StateField13.define({
7563
+ create: () => Decoration15.none,
7564
+ update: (_decorations, tr) => {
7565
+ const { visible, insertAt, lastNonWsAt } = tr.state.field(visibilityField);
7566
+ if (!visible) {
7567
+ return Decoration15.none;
7568
+ }
7569
+ const { text, head } = tr.state.field(bufferField);
7570
+ const cursorAt = text.length > head ? insertAt : lastNonWsAt;
7571
+ const pos = Math.min(cursorAt, tr.state.doc.length);
7572
+ return Decoration15.set([
7573
+ Decoration15.widget({
6951
7574
  widget: new CursorWidget(),
6952
7575
  side: 1
6953
- }).range(endPos)
7576
+ }).range(pos)
6954
7577
  ]);
6955
7578
  },
6956
- provide: (f) => EditorView28.decorations.from(f)
7579
+ provide: (field) => EditorView30.decorations.from(field)
7580
+ });
7581
+ const timerPlugin = ViewPlugin21.fromClass(class {
7582
+ view;
7583
+ _timer;
7584
+ constructor(view) {
7585
+ this.view = view;
7586
+ }
7587
+ update(update2) {
7588
+ const { text, head } = update2.state.field(bufferField);
7589
+ const { visible } = update2.state.field(visibilityField);
7590
+ const pending = text.length - head;
7591
+ if (pending > 0) {
7592
+ clearTimeout(this._timer);
7593
+ this._timer = void 0;
7594
+ } else if (visible && this._timer === void 0) {
7595
+ this._timer = setTimeout(() => {
7596
+ this.view.dispatch({
7597
+ effects: hideCursor.of(null)
7598
+ });
7599
+ this._timer = void 0;
7600
+ }, CURSOR_LINGER);
7601
+ }
7602
+ }
7603
+ destroy() {
7604
+ clearTimeout(this._timer);
7605
+ }
6957
7606
  });
6958
7607
  return [
6959
- showCursor,
6960
- timerPlugin,
6961
- cursorDecoration
7608
+ visibilityField,
7609
+ decorationField,
7610
+ timerPlugin
6962
7611
  ];
6963
7612
  };
6964
- var CursorWidget = class extends WidgetType9 {
7613
+ var CursorWidget = class _CursorWidget extends WidgetType9 {
7614
+ // All instances are interchangeable — let CM reuse the existing DOM across drips so
7615
+ // the blink animation isn't restarted on every transaction.
7616
+ eq(other) {
7617
+ return other instanceof _CursorWidget;
7618
+ }
6965
7619
  toDOM() {
6966
- const inner = Domino3.of("span").text("\u258F").style({
6967
- animation: "blink 2s infinite"
7620
+ const inner = Domino3.of("span").text("\u2217").style({
7621
+ animation: "blink 1s infinite",
7622
+ animationDelay: "250ms"
6968
7623
  });
6969
7624
  return Domino3.of("span").style({
6970
7625
  opacity: "0.8"
6971
- }).children(inner).root;
7626
+ }).append(inner).root;
6972
7627
  }
6973
7628
  };
6974
- var fadeIn = (options = {}) => {
6975
- const FADE_IN_DURATION = 1e3;
6976
- const DEFAULT_REMOVAL_DELAY = 5e3;
6977
- const removalDelay = options.removalDelay ?? DEFAULT_REMOVAL_DELAY;
6978
- const removeDecoration = StateEffect9.define();
6979
- const fadeField = StateField12.define({
6980
- create: () => Decoration14.none,
6981
- update: (decorations2, tr) => {
6982
- let next = decorations2;
6983
- for (const effect of tr.effects) {
6984
- if (effect.is(removeDecoration)) {
6985
- const target = effect.value;
6986
- next = next.update({
6987
- filter: (from, to) => !(from === target.from && to === target.to)
6988
- });
6989
- }
6990
- }
6991
- if (!tr.docChanged) {
6992
- return next;
7629
+ var OPENING_TAG_NAME = /^<([a-zA-Z][\w-]*)/;
7630
+ var TAG_NAME_PROBE = 64;
7631
+ var escapeRegExpSource2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7632
+ var flushable = (buffer, start, streamingTags, activeStreamTag) => {
7633
+ if (start >= buffer.length) {
7634
+ return {
7635
+ count: 0
7636
+ };
7637
+ }
7638
+ if (activeStreamTag) {
7639
+ const closeTag = `</${activeStreamTag}>`;
7640
+ if (buffer.startsWith(closeTag, start)) {
7641
+ return {
7642
+ count: closeTag.length,
7643
+ exitTag: true
7644
+ };
7645
+ }
7646
+ if (buffer[start] === "<") {
7647
+ return {
7648
+ count: xmlElementLength(buffer, start)
7649
+ };
7650
+ }
7651
+ return {
7652
+ count: 1
7653
+ };
7654
+ }
7655
+ const ch = buffer[start];
7656
+ if (ch === "<") {
7657
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7658
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7659
+ if (nameMatch && streamingTags.has(nameMatch[1])) {
7660
+ const close = buffer.indexOf(">", start);
7661
+ if (close === -1) {
7662
+ return {
7663
+ count: 0
7664
+ };
6993
7665
  }
6994
- let isReset = tr.state.doc.length === 0;
6995
- if (!isReset) {
6996
- tr.changes.iterChanges((fromA, toA) => {
6997
- if (fromA === 0 && toA === tr.startState.doc.length) {
6998
- isReset = true;
6999
- }
7000
- });
7666
+ if (buffer[close - 1] === "/") {
7667
+ return {
7668
+ count: close + 1 - start
7669
+ };
7001
7670
  }
7002
- if (isReset) {
7003
- return Decoration14.none;
7671
+ return {
7672
+ count: close + 1 - start,
7673
+ enterTag: nameMatch[1]
7674
+ };
7675
+ }
7676
+ return {
7677
+ count: xmlElementLength(buffer, start)
7678
+ };
7679
+ }
7680
+ if (ch === "!" && buffer.length > start + 1 && buffer[start + 1] === "[") {
7681
+ return {
7682
+ count: linkLength(buffer, start, start + 1)
7683
+ };
7684
+ }
7685
+ if (ch === "[") {
7686
+ return {
7687
+ count: linkLength(buffer, start, start)
7688
+ };
7689
+ }
7690
+ return {
7691
+ count: 1
7692
+ };
7693
+ };
7694
+ var xmlElementLength = (buffer, start = 0) => {
7695
+ const close = buffer.indexOf(">", start);
7696
+ if (close === -1) {
7697
+ return 0;
7698
+ }
7699
+ if (buffer[close - 1] === "/") {
7700
+ return close + 1 - start;
7701
+ }
7702
+ if (buffer[start + 1] === "/") {
7703
+ return close + 1 - start;
7704
+ }
7705
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7706
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7707
+ if (!nameMatch) {
7708
+ return 1;
7709
+ }
7710
+ const tagName = nameMatch[1];
7711
+ let depth = 0;
7712
+ const tagPattern = new RegExp(`<(/?)${escapeRegExpSource2(tagName)}(\\s[^>]*)?>`, "g");
7713
+ tagPattern.lastIndex = start;
7714
+ let match;
7715
+ while ((match = tagPattern.exec(buffer)) !== null) {
7716
+ const isSelfClosing = match[0].endsWith("/>");
7717
+ const isClosing = match[1] === "/";
7718
+ if (isSelfClosing) {
7719
+ if (depth === 0) {
7720
+ return match.index + match[0].length - start;
7721
+ }
7722
+ } else if (isClosing) {
7723
+ depth--;
7724
+ if (depth === 0) {
7725
+ return match.index + match[0].length - start;
7004
7726
  }
7005
- const add = [];
7006
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7007
- if (fromB === 0 && toB === inserted.length) {
7727
+ } else {
7728
+ depth++;
7729
+ }
7730
+ }
7731
+ return 0;
7732
+ };
7733
+ var linkLength = (buffer, start, bracketAt) => {
7734
+ const bracketClose = buffer.indexOf("]", bracketAt + 1);
7735
+ if (bracketClose === -1) {
7736
+ return 0;
7737
+ }
7738
+ if (bracketClose + 1 >= buffer.length) {
7739
+ return 0;
7740
+ }
7741
+ if (buffer[bracketClose + 1] !== "(") {
7742
+ return 1;
7743
+ }
7744
+ const parenClose = buffer.indexOf(")", bracketClose + 2);
7745
+ if (parenClose === -1) {
7746
+ return 0;
7747
+ }
7748
+ return parenClose + 1 - start;
7749
+ };
7750
+
7751
+ // src/extensions/tags/xml-block-decoration.ts
7752
+ import { xmlLanguage as xmlLanguage2 } from "@codemirror/lang-xml";
7753
+ import { Decoration as Decoration16, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7754
+ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7755
+ const lineDecoration = lineClass ? Decoration16.line({
7756
+ class: lineClass
7757
+ }) : void 0;
7758
+ const contentDecoration = contentClass ? Decoration16.mark({
7759
+ class: contentClass
7760
+ }) : void 0;
7761
+ const hideDecoration = hideTags ? Decoration16.replace({}) : void 0;
7762
+ const buildDecorations5 = (view) => {
7763
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7764
+ if (!text.includes(`<${tag}`)) {
7765
+ return Decoration16.none;
7766
+ }
7767
+ const tree = xmlLanguage2.parser.parse(text);
7768
+ const ranges = [];
7769
+ tree.iterate({
7770
+ enter: (node) => {
7771
+ if (node.type.name !== "Element") {
7008
7772
  return;
7009
7773
  }
7010
- if (toA === tr.startState.doc.length && inserted.length > 0) {
7011
- add.push(Decoration14.mark({
7012
- class: "cm-fade-in"
7013
- }).range(fromB, toB));
7774
+ const openTag = node.node.getChild("OpenTag");
7775
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7776
+ const tagNameNode = openTag?.getChild("TagName");
7777
+ if (!openTag || !tagNameNode) {
7778
+ return;
7014
7779
  }
7015
- });
7016
- return next.update({
7017
- add
7018
- });
7019
- },
7020
- provide: (f) => EditorView28.decorations.from(f)
7021
- });
7022
- const timerPlugin = ViewPlugin18.fromClass(class {
7023
- view;
7024
- // Map a simple key "from-to" to timer id.
7025
- _timers = /* @__PURE__ */ new Map();
7780
+ if (text.slice(tagNameNode.from, tagNameNode.to) !== tag) {
7781
+ return;
7782
+ }
7783
+ const contentFrom = openTag.to;
7784
+ const contentTo = closeTag?.from ?? node.node.to;
7785
+ if (hideDecoration) {
7786
+ ranges.push(hideDecoration.range(openTag.from, openTag.to));
7787
+ if (closeTag) {
7788
+ ranges.push(hideDecoration.range(closeTag.from, closeTag.to));
7789
+ }
7790
+ }
7791
+ if (contentDecoration && contentFrom < contentTo) {
7792
+ ranges.push(contentDecoration.range(contentFrom, contentTo));
7793
+ }
7794
+ if (lineDecoration && contentFrom <= view.state.doc.length) {
7795
+ let pos = contentFrom;
7796
+ while (pos <= contentTo && pos <= view.state.doc.length) {
7797
+ const line = view.state.doc.lineAt(pos);
7798
+ ranges.push(lineDecoration.range(line.from));
7799
+ if (line.to >= contentTo) {
7800
+ break;
7801
+ }
7802
+ pos = line.to + 1;
7803
+ }
7804
+ }
7805
+ }
7806
+ });
7807
+ return Decoration16.set(ranges, true);
7808
+ };
7809
+ return ViewPlugin22.fromClass(class {
7810
+ decorations;
7026
7811
  constructor(view) {
7027
- this.view = view;
7812
+ this.decorations = buildDecorations5(view);
7028
7813
  }
7029
7814
  update(update2) {
7030
- if (!update2.docChanged) {
7031
- return;
7815
+ if (update2.docChanged) {
7816
+ this.decorations = buildDecorations5(update2.view);
7032
7817
  }
7033
- update2.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7034
- if (toA !== update2.startState.doc.length || inserted.length === 0) {
7818
+ }
7819
+ }, {
7820
+ decorations: (instance) => instance.decorations
7821
+ });
7822
+ };
7823
+
7824
+ // src/extensions/tags/xml-formatting.ts
7825
+ import { xmlLanguage as xmlLanguage3 } from "@codemirror/lang-xml";
7826
+ import { Decoration as Decoration17, EditorView as EditorView31, ViewPlugin as ViewPlugin23 } from "@codemirror/view";
7827
+ var XML_TAG_NODES = /* @__PURE__ */ new Set([
7828
+ "OpenTag",
7829
+ "CloseTag",
7830
+ "SelfClosingTag",
7831
+ "MismatchedCloseTag"
7832
+ ]);
7833
+ var xmlElementMark = Decoration17.mark({
7834
+ class: "cm-xml-element"
7835
+ });
7836
+ var xmlTagMark = Decoration17.mark({
7837
+ class: "cm-xml-tag"
7838
+ });
7839
+ var xmlContentMark = Decoration17.mark({
7840
+ class: "cm-xml-content"
7841
+ });
7842
+ var xmlFormatting = ({ skip } = {}) => {
7843
+ const skipSet = skip && skip.length > 0 ? new Set(skip) : void 0;
7844
+ const buildDecorations5 = (view) => {
7845
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7846
+ if (!text.includes("<")) {
7847
+ return Decoration17.none;
7848
+ }
7849
+ const tagNameAt = (node) => text.slice(node.from, node.to);
7850
+ const tree = xmlLanguage3.parser.parse(text);
7851
+ const ranges = [];
7852
+ tree.iterate({
7853
+ enter: (node) => {
7854
+ const name = node.type.name;
7855
+ if (name === "SelfClosingTag" && node.from < node.to) {
7856
+ if (skipSet) {
7857
+ const tagNameNode = node.node.getChild("TagName");
7858
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7859
+ return false;
7860
+ }
7861
+ }
7862
+ ranges.push(xmlElementMark.range(node.from, node.to));
7863
+ ranges.push(xmlTagMark.range(node.from, node.to));
7035
7864
  return;
7036
7865
  }
7037
- const key = `${fromB}-${toB}`;
7038
- if (this._timers.has(key)) {
7039
- clearTimeout(this._timers.get(key));
7866
+ if (XML_TAG_NODES.has(name) && node.from < node.to) {
7867
+ ranges.push(xmlTagMark.range(node.from, node.to));
7868
+ return;
7869
+ }
7870
+ if (name === "Element" && node.from < node.to) {
7871
+ const openTag = node.node.getChild("OpenTag");
7872
+ if (openTag && skipSet) {
7873
+ const tagNameNode = openTag.getChild("TagName");
7874
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7875
+ return false;
7876
+ }
7877
+ }
7878
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7879
+ ranges.push(xmlElementMark.range(node.from, node.to));
7880
+ if (openTag && closeTag && openTag.to < closeTag.from) {
7881
+ ranges.push(xmlContentMark.range(openTag.to, closeTag.from));
7882
+ }
7040
7883
  }
7041
- const totalDelay = FADE_IN_DURATION + removalDelay;
7042
- const id = setTimeout(() => {
7043
- this.view.dispatch({
7044
- effects: removeDecoration.of({
7045
- from: fromB,
7046
- to: toB
7047
- })
7048
- });
7049
- this._timers.delete(key);
7050
- }, totalDelay);
7051
- this._timers.set(key, id);
7052
- });
7053
- }
7054
- destroy() {
7055
- for (const id of this._timers.values()) {
7056
- clearTimeout(id);
7057
7884
  }
7058
- this._timers.clear();
7059
- }
7060
- });
7885
+ });
7886
+ return Decoration17.set(ranges, true);
7887
+ };
7061
7888
  return [
7062
- fadeField,
7063
- timerPlugin,
7064
- EditorView28.theme({
7065
- ".cm-line > span": {
7066
- opacity: "0.8"
7067
- },
7068
- ".cm-fade-in": {
7069
- animation: "fade-in 3s ease-out forwards"
7070
- },
7071
- "@keyframes fade-in": {
7072
- "0%": {
7073
- opacity: "0"
7074
- },
7075
- "80%": {
7076
- opacity: "1"
7077
- },
7078
- "100%": {
7079
- opacity: "0.8"
7889
+ ViewPlugin23.fromClass(class {
7890
+ decorations;
7891
+ constructor(view) {
7892
+ this.decorations = buildDecorations5(view);
7893
+ }
7894
+ update(update2) {
7895
+ if (update2.docChanged) {
7896
+ this.decorations = buildDecorations5(update2.view);
7080
7897
  }
7081
7898
  }
7899
+ }, {
7900
+ decorations: (instance) => instance.decorations
7901
+ }),
7902
+ EditorView31.baseTheme({
7903
+ ".cm-xml-element": {
7904
+ backgroundColor: "var(--color-current-surface)",
7905
+ borderRadius: "0.25rem",
7906
+ padding: "0.25rem"
7907
+ },
7908
+ ".cm-xml-tag": {
7909
+ color: "var(--color-blue-500)",
7910
+ fontFamily: "var(--font-mono)"
7911
+ },
7912
+ ".cm-xml-content": {}
7082
7913
  })
7083
7914
  ];
7084
7915
  };
7085
7916
 
7086
7917
  // src/extensions/tags/xml-tags.ts
7087
7918
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7088
- import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect10, StateField as StateField13 } from "@codemirror/state";
7089
- import { Decoration as Decoration15, EditorView as EditorView29, ViewPlugin as ViewPlugin19, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7919
+ import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7920
+ import { Decoration as Decoration18, EditorView as EditorView32, ViewPlugin as ViewPlugin24, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
7090
7921
  import { invariant as invariant7 } from "@dxos/invariant";
7091
- import { log as log10 } from "@dxos/log";
7922
+ import { log as log11 } from "@dxos/log";
7923
+ import { Domino as Domino4 } from "@dxos/ui";
7092
7924
 
7093
7925
  // src/extensions/tags/xml-util.ts
7094
7926
  import { invariant as invariant6 } from "@dxos/invariant";
7095
- var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7927
+ var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7096
7928
  var nodeToJson = (state, node) => {
7097
- invariant6(node.type.name === "Element", "Node is not an Element", {
7098
- F: __dxlog_file15,
7099
- L: 18,
7100
- S: void 0,
7101
- A: [
7102
- "node.type.name === 'Element'",
7103
- "'Node is not an Element'"
7104
- ]
7105
- });
7929
+ 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'"] });
7106
7930
  const openTag = node.node.getChild("OpenTag") || node.node.getChild("SelfClosingTag");
7107
7931
  if (openTag) {
7108
7932
  const tagName = openTag.getChild("TagName");
@@ -7134,13 +7958,23 @@ var nodeToJson = (state, node) => {
7134
7958
  if (node.type.name === "Element" && openTag.type.name !== "SelfClosingTag") {
7135
7959
  const children = [];
7136
7960
  let child = node.node.firstChild;
7961
+ const appendText = (raw) => {
7962
+ if (raw.length === 0) {
7963
+ return;
7964
+ }
7965
+ const last = children[children.length - 1];
7966
+ if (typeof last === "string") {
7967
+ children[children.length - 1] = last + raw;
7968
+ } else {
7969
+ children.push(raw);
7970
+ }
7971
+ };
7137
7972
  while (child) {
7138
7973
  if (child.type.name !== "OpenTag" && child.type.name !== "CloseTag") {
7139
7974
  if (child.type.name === "Text") {
7140
- const text = state.doc.sliceString(child.from, child.to).trim();
7141
- if (text) {
7142
- children.push(text);
7143
- }
7975
+ appendText(state.doc.sliceString(child.from, child.to));
7976
+ } else if (child.type.name === "EntityReference" || child.type.name === "CharacterReference") {
7977
+ appendText(decodeXmlEntity(state.doc.sliceString(child.from, child.to)));
7144
7978
  } else if (child.type.name === "Element") {
7145
7979
  const data = nodeToJson(state, child);
7146
7980
  if (data) {
@@ -7150,26 +7984,63 @@ var nodeToJson = (state, node) => {
7150
7984
  }
7151
7985
  child = child.nextSibling;
7152
7986
  }
7987
+ if (children.length > 0 && typeof children[0] === "string") {
7988
+ children[0] = children[0].trimStart();
7989
+ }
7153
7990
  if (children.length > 0) {
7154
- tag.children = children;
7991
+ const lastIndex = children.length - 1;
7992
+ const last = children[lastIndex];
7993
+ if (typeof last === "string") {
7994
+ children[lastIndex] = last.trimEnd();
7995
+ }
7996
+ }
7997
+ const trimmed = children.filter((value) => typeof value !== "string" || value.length > 0);
7998
+ if (trimmed.length > 0) {
7999
+ tag.children = trimmed;
7155
8000
  }
7156
8001
  }
7157
8002
  return tag;
7158
8003
  }
7159
8004
  };
8005
+ var XML_NAMED_ENTITIES = {
8006
+ "&lt;": "<",
8007
+ "&gt;": ">",
8008
+ "&amp;": "&",
8009
+ "&quot;": '"',
8010
+ "&apos;": "'"
8011
+ };
8012
+ var decodeXmlEntity = (raw) => {
8013
+ const named = XML_NAMED_ENTITIES[raw];
8014
+ if (named !== void 0) {
8015
+ return named;
8016
+ }
8017
+ const numeric = /^&#(x?)([0-9a-fA-F]+);$/.exec(raw);
8018
+ if (numeric) {
8019
+ const code = parseInt(numeric[2], numeric[1] ? 16 : 10);
8020
+ if (Number.isFinite(code)) {
8021
+ try {
8022
+ return String.fromCodePoint(code);
8023
+ } catch {
8024
+ }
8025
+ }
8026
+ }
8027
+ return raw;
8028
+ };
7160
8029
 
7161
8030
  // src/extensions/tags/xml-tags.ts
7162
- var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7163
- var navigatePreviousEffect = StateEffect10.define();
7164
- var navigateNextEffect = StateEffect10.define();
8031
+ var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
8032
+ var navigatePreviousEffect = StateEffect12.define();
8033
+ var navigateNextEffect = StateEffect12.define();
7165
8034
  var getXmlTextChild = (children) => {
7166
8035
  const child = children?.[0];
7167
8036
  return typeof child === "string" ? child : null;
7168
8037
  };
7169
- var xmlTagContextEffect = StateEffect10.define();
7170
- var xmlTagResetEffect = StateEffect10.define();
7171
- var xmlTagUpdateEffect = StateEffect10.define();
7172
- var widgetContextStateField = StateField13.define({
8038
+ var xmlWidgetId = (explicit, fallback) => typeof explicit === "string" && explicit.length > 0 ? explicit : fallback;
8039
+ var escapeRegExpSource3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8040
+ var xmlTagContextEffect = StateEffect12.define();
8041
+ var xmlTagResetEffect = StateEffect12.define();
8042
+ var xmlTagUpdateEffect = StateEffect12.define();
8043
+ var widgetContextStateField = StateField14.define({
7173
8044
  create: () => void 0,
7174
8045
  update: (value, tr) => {
7175
8046
  for (const effect of tr.effects) {
@@ -7180,7 +8051,7 @@ var widgetContextStateField = StateField13.define({
7180
8051
  return value;
7181
8052
  }
7182
8053
  });
7183
- var widgetStateMapStateField = StateField13.define({
8054
+ var widgetStateMapStateField = StateField14.define({
7184
8055
  create: () => ({}),
7185
8056
  update: (map, tr) => {
7186
8057
  for (const effect of tr.effects) {
@@ -7189,15 +8060,10 @@ var widgetStateMapStateField = StateField13.define({
7189
8060
  }
7190
8061
  if (effect.is(xmlTagUpdateEffect)) {
7191
8062
  const { id, value } = effect.value;
7192
- log10("widget updated", {
8063
+ log11("widget updated", {
7193
8064
  id,
7194
8065
  value
7195
- }, {
7196
- F: __dxlog_file16,
7197
- L: 153,
7198
- S: void 0,
7199
- C: (f, a) => f(...a)
7200
- });
8066
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 59, S: void 0 });
7201
8067
  const state = typeof value === "function" ? value(map[id]) : value;
7202
8068
  return {
7203
8069
  ...map,
@@ -7224,15 +8090,10 @@ var createWidgetMap = (setWidgets) => {
7224
8090
  const widgets = /* @__PURE__ */ new Map();
7225
8091
  const notifier = {
7226
8092
  mounted: (state) => {
7227
- log10("widget mounted", {
8093
+ log11("widget mounted", {
7228
8094
  id: state.id,
7229
8095
  tag: state.props._tag
7230
- }, {
7231
- F: __dxlog_file16,
7232
- L: 206,
7233
- S: void 0,
7234
- C: (f, a) => f(...a)
7235
- });
8096
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 101, S: void 0 });
7236
8097
  widgets.set(state.id, state);
7237
8098
  setWidgets?.([
7238
8099
  ...widgets.values()
@@ -7240,15 +8101,10 @@ var createWidgetMap = (setWidgets) => {
7240
8101
  },
7241
8102
  unmounted: (id) => {
7242
8103
  const state = widgets.get(id);
7243
- log10("widget unmounted", {
8104
+ log11("widget unmounted", {
7244
8105
  id,
7245
8106
  tag: state?.props._tag
7246
- }, {
7247
- F: __dxlog_file16,
7248
- L: 212,
7249
- S: void 0,
7250
- C: (f, a) => f(...a)
7251
- });
8107
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 112, S: void 0 });
7252
8108
  widgets.delete(id);
7253
8109
  setWidgets?.([
7254
8110
  ...widgets.values()
@@ -7257,7 +8113,7 @@ var createWidgetMap = (setWidgets) => {
7257
8113
  };
7258
8114
  return notifier;
7259
8115
  };
7260
- var keyHandlers = keymap13.of([
8116
+ var keyHandlers = keymap14.of([
7261
8117
  {
7262
8118
  key: "Mod-ArrowUp",
7263
8119
  run: (view) => {
@@ -7278,7 +8134,7 @@ var keyHandlers = keymap13.of([
7278
8134
  }
7279
8135
  ]);
7280
8136
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7281
- return EditorView29.updateListener.of((update2) => {
8137
+ return EditorView32.updateListener.of((update2) => {
7282
8138
  update2.transactions.forEach((transaction) => {
7283
8139
  for (const effect of transaction.effects) {
7284
8140
  if (effect.is(navigatePreviousEffect)) {
@@ -7306,11 +8162,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7306
8162
  anchor: line.from,
7307
8163
  head: line.from
7308
8164
  },
7309
- effects: scrollToLineEffect.of({
7310
- line: line.number,
7311
- options: {
7312
- offset: -16
7313
- }
8165
+ effects: crawlerLineEffect.of({
8166
+ line: line.number - 1,
8167
+ offset: -16
7314
8168
  })
7315
8169
  });
7316
8170
  continue;
@@ -7341,11 +8195,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7341
8195
  anchor: line.to,
7342
8196
  head: line.to
7343
8197
  },
7344
- effects: scrollToLineEffect.of({
7345
- line: line.number,
7346
- options: {
7347
- offset: -16
7348
- }
8198
+ effects: crawlerLineEffect.of({
8199
+ line: line.number - 1,
8200
+ offset: -16
7349
8201
  })
7350
8202
  });
7351
8203
  } else {
@@ -7355,11 +8207,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7355
8207
  anchor: line.to,
7356
8208
  head: line.to
7357
8209
  },
7358
- effects: scrollToLineEffect.of({
7359
- line: line.number,
7360
- options: {
7361
- position: "end"
7362
- }
8210
+ effects: crawlerLineEffect.of({
8211
+ line: line.number - 1,
8212
+ position: "end"
7363
8213
  })
7364
8214
  });
7365
8215
  }
@@ -7369,7 +8219,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7369
8219
  });
7370
8220
  });
7371
8221
  };
7372
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin19.fromClass(class {
8222
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
7373
8223
  update(update2) {
7374
8224
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
7375
8225
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -7396,19 +8246,25 @@ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin1
7396
8246
  }
7397
8247
  }
7398
8248
  });
7399
- var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.define({
8249
+ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.define({
7400
8250
  create: (state) => {
7401
8251
  return buildDecorations4(state, {
7402
8252
  from: 0,
7403
8253
  to: state.doc.length
7404
8254
  }, registry, notifier);
7405
8255
  },
7406
- update: ({ from, decorations: decorations2 }, tr) => {
8256
+ update: ({ from, streamingFrom, decorations: decorations2 }, tr) => {
7407
8257
  for (const effect of tr.effects) {
7408
8258
  if (effect.is(xmlTagResetEffect)) {
8259
+ if (tr.docChanged) {
8260
+ return buildDecorations4(tr.state, {
8261
+ from: 0,
8262
+ to: tr.state.doc.length
8263
+ }, registry, notifier);
8264
+ }
7409
8265
  return {
7410
8266
  from: 0,
7411
- decorations: Decoration15.none
8267
+ decorations: Decoration18.none
7412
8268
  };
7413
8269
  }
7414
8270
  }
@@ -7416,27 +8272,26 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7416
8272
  const { state } = tr;
7417
8273
  const reset = tr.changes.touchesRange(0, from);
7418
8274
  if (reset) {
7419
- log10("document reset", {
8275
+ log11("document reset", {
7420
8276
  from,
7421
8277
  to: state.doc.length
7422
- }, {
7423
- F: __dxlog_file16,
7424
- L: 371,
7425
- S: void 0,
7426
- C: (f, a) => f(...a)
7427
- });
8278
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 298, S: void 0 });
7428
8279
  return buildDecorations4(state, {
7429
8280
  from: 0,
7430
8281
  to: state.doc.length
7431
8282
  }, registry, notifier);
7432
8283
  } else {
8284
+ const rebuildFrom = streamingFrom ?? from;
7433
8285
  const result = buildDecorations4(state, {
7434
- from,
8286
+ from: rebuildFrom,
7435
8287
  to: state.doc.length
7436
8288
  }, registry, notifier);
7437
8289
  return {
7438
8290
  from: result.from,
8291
+ streamingFrom: result.streamingFrom,
7439
8292
  decorations: decorations2.update({
8293
+ // Remove old streaming decorations — they are rebuilt each tick.
8294
+ filter: (_f, _t, deco) => !deco.spec.streaming,
7440
8295
  add: decorationSetToArray(result.decorations)
7441
8296
  })
7442
8297
  };
@@ -7444,12 +8299,13 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7444
8299
  }
7445
8300
  return {
7446
8301
  from,
8302
+ streamingFrom,
7447
8303
  decorations: decorations2
7448
8304
  };
7449
8305
  },
7450
8306
  provide: (field) => [
7451
- EditorView29.decorations.from(field, (v) => v.decorations),
7452
- EditorView29.atomicRanges.of((view) => view.state.field(field).decorations || Decoration15.none)
8307
+ EditorView32.decorations.from(field, (v) => v.decorations),
8308
+ EditorView32.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
7453
8309
  ]
7454
8310
  });
7455
8311
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -7460,10 +8316,11 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7460
8316
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
7461
8317
  return {
7462
8318
  from: range.from,
7463
- decorations: Decoration15.none
8319
+ decorations: Decoration18.none
7464
8320
  };
7465
8321
  }
7466
8322
  let last = range.from;
8323
+ let streamingFrom;
7467
8324
  tree.iterate({
7468
8325
  from: range.from,
7469
8326
  to: range.to,
@@ -7476,21 +8333,26 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7476
8333
  if (args) {
7477
8334
  const def = registry[args._tag];
7478
8335
  if (def) {
8336
+ if (def.streaming && !node.node.getChild("CloseTag")) {
8337
+ return false;
8338
+ }
7479
8339
  const { block, factory, Component } = def;
7480
- const widgetState = args.id ? widgetStateMap[args.id] : void 0;
7481
8340
  const nodeRange = {
7482
8341
  from: node.node.from,
7483
8342
  to: node.node.to
7484
8343
  };
8344
+ const widgetId = xmlWidgetId(args.id, def.streaming ? `cm-xml-${nodeRange.from}` : `cm-xml-${nodeRange.from}-${nodeRange.to}`);
8345
+ const widgetState = widgetStateMap[widgetId];
7485
8346
  const props = {
7486
- context,
8347
+ id: widgetId,
7487
8348
  range: nodeRange,
8349
+ context,
7488
8350
  ...args,
7489
8351
  ...widgetState
7490
8352
  };
7491
- const widget = factory ? factory(props) : Component ? args.id && new PlaceholderWidget2(args.id, Component, props, notifier) : void 0;
8353
+ const widget = factory ? factory(props) ?? void 0 : Component ? new PlaceholderWidget2(widgetId, Component, props, notifier) : void 0;
7492
8354
  if (widget) {
7493
- builder.add(nodeRange.from, nodeRange.to, Decoration15.replace({
8355
+ builder.add(nodeRange.from, nodeRange.to, Decoration18.replace({
7494
8356
  widget,
7495
8357
  block,
7496
8358
  atomic: true,
@@ -7502,20 +8364,72 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7502
8364
  }
7503
8365
  }
7504
8366
  } catch (err) {
7505
- log10.catch(err, void 0, {
7506
- F: __dxlog_file16,
7507
- L: 456,
7508
- S: void 0,
7509
- C: (f, a) => f(...a)
7510
- });
8367
+ log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 401, S: void 0 });
7511
8368
  }
7512
8369
  return false;
7513
8370
  }
7514
8371
  }
7515
8372
  }
7516
8373
  });
8374
+ const streamingTagNames = Object.entries(registry).filter(([, def]) => def.streaming).map(([name]) => name).sort((a, b) => b.length - a.length);
8375
+ if (streamingTagNames.length > 0) {
8376
+ const tailText = state.sliceDoc(range.from, range.to);
8377
+ const streamingPattern = streamingTagNames.map(escapeRegExpSource3).join("|");
8378
+ const tagPattern = new RegExp(`<(${streamingPattern})(\\s[^>]*)?>`, "g");
8379
+ let match;
8380
+ while ((match = tagPattern.exec(tailText)) !== null) {
8381
+ const tagName = match[1];
8382
+ const closeTag = `</${tagName}>`;
8383
+ const afterOpen = match.index + match[0].length;
8384
+ if (tailText.indexOf(closeTag, afterOpen) === -1) {
8385
+ const absoluteFrom = range.from + match.index;
8386
+ const contentFrom = range.from + afterOpen;
8387
+ const innerText = state.sliceDoc(contentFrom, range.to).trim();
8388
+ const def = registry[tagName];
8389
+ const props = {
8390
+ _tag: tagName,
8391
+ context,
8392
+ range: {
8393
+ from: absoluteFrom,
8394
+ to: range.to
8395
+ },
8396
+ children: innerText ? [
8397
+ innerText
8398
+ ] : void 0
8399
+ };
8400
+ const attrPattern = /(\w+)="([^"]*)"/g;
8401
+ let attrMatch;
8402
+ while ((attrMatch = attrPattern.exec(match[0])) !== null) {
8403
+ props[attrMatch[1]] = attrMatch[2];
8404
+ }
8405
+ const widgetId = xmlWidgetId(props.id, `cm-xml-${absoluteFrom}`);
8406
+ const widgetState = widgetStateMap[widgetId];
8407
+ const mergedProps = {
8408
+ ...props,
8409
+ id: widgetId,
8410
+ ...widgetState
8411
+ };
8412
+ const widget = def.factory ? def.factory(mergedProps) ?? void 0 : def.Component ? new PlaceholderWidget2(widgetId, def.Component, mergedProps, notifier, true) : void 0;
8413
+ if (widget) {
8414
+ builder.add(absoluteFrom, range.to, Decoration18.replace({
8415
+ widget,
8416
+ block: def.block,
8417
+ atomic: true,
8418
+ inclusive: true,
8419
+ tag: tagName,
8420
+ streaming: true,
8421
+ contentFrom
8422
+ }));
8423
+ streamingFrom = absoluteFrom;
8424
+ last = absoluteFrom;
8425
+ }
8426
+ break;
8427
+ }
8428
+ }
8429
+ }
7517
8430
  return {
7518
8431
  from: last,
8432
+ streamingFrom,
7519
8433
  decorations: builder.finish()
7520
8434
  };
7521
8435
  };
@@ -7524,108 +8438,62 @@ var PlaceholderWidget2 = class extends WidgetType10 {
7524
8438
  Component;
7525
8439
  props;
7526
8440
  notifier;
7527
- _root = null;
7528
- constructor(id, Component, props, notifier) {
7529
- super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier;
7530
- invariant7(id, void 0, {
7531
- F: __dxlog_file16,
7532
- L: 482,
7533
- S: this,
7534
- A: [
7535
- "id",
7536
- ""
7537
- ]
7538
- });
8441
+ streaming;
8442
+ #root = null;
8443
+ #view;
8444
+ constructor(id, Component, props, notifier, streaming) {
8445
+ super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier, this.streaming = streaming;
8446
+ invariant7(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 488, S: this, A: ["id", ""] });
7539
8447
  }
7540
8448
  get root() {
7541
- return this._root;
8449
+ return this.#root;
7542
8450
  }
7543
8451
  eq(other) {
8452
+ if (this.streaming) {
8453
+ return false;
8454
+ }
7544
8455
  return this.id === other.id;
7545
8456
  }
7546
8457
  ignoreEvent() {
7547
8458
  return true;
7548
8459
  }
7549
- toDOM(_view) {
7550
- this._root = document.createElement("span");
8460
+ toDOM(view) {
8461
+ this.#view = view;
8462
+ this.#root = Domino4.of("div").classNames("min-h-[24px]").root;
8463
+ const props = Object.assign({}, this.props, {
8464
+ view
8465
+ });
8466
+ this.notifier.mounted({
8467
+ id: this.id,
8468
+ root: this.#root,
8469
+ props,
8470
+ Component: this.Component
8471
+ });
8472
+ return this.#root;
8473
+ }
8474
+ updateDOM(dom) {
8475
+ this.#root = dom;
8476
+ const props = Object.assign({}, this.props, {
8477
+ view: this.#view
8478
+ });
7551
8479
  this.notifier.mounted({
7552
8480
  id: this.id,
7553
- root: this._root,
7554
- props: this.props,
8481
+ root: this.#root,
8482
+ props,
7555
8483
  Component: this.Component
7556
8484
  });
7557
- return this._root;
8485
+ return true;
7558
8486
  }
7559
8487
  destroy(_dom) {
7560
8488
  this.notifier.unmounted(this.id);
7561
- this._root = null;
8489
+ this.#root = null;
8490
+ this.#view = void 0;
7562
8491
  }
7563
8492
  };
7564
-
7565
- // src/extensions/typewriter.ts
7566
- import { keymap as keymap14 } from "@codemirror/view";
7567
- var defaultItems = [
7568
- "hello world!",
7569
- "this is a test.",
7570
- "this is [DXOS](https://dxos.org)"
7571
- ];
7572
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
7573
- let t;
7574
- let idx = 0;
7575
- return [
7576
- keymap14.of([
7577
- {
7578
- // Reset.
7579
- key: "alt-meta-'",
7580
- run: () => {
7581
- clearTimeout(t);
7582
- idx = 0;
7583
- return true;
7584
- }
7585
- },
7586
- {
7587
- // Next prompt.
7588
- // TODO(burdon): Press 1-9 to select prompt?
7589
- key: "Shift-Meta-'",
7590
- run: (view) => {
7591
- clearTimeout(t);
7592
- const text = items[idx++];
7593
- if (idx === items?.length) {
7594
- idx = 0;
7595
- }
7596
- let i = 0;
7597
- const insert = (d = 0) => {
7598
- t = setTimeout(() => {
7599
- const pos = view.state.selection.main.head;
7600
- view.dispatch({
7601
- changes: {
7602
- from: pos,
7603
- insert: text[i++]
7604
- },
7605
- selection: {
7606
- anchor: pos + 1
7607
- }
7608
- });
7609
- if (i < text.length) {
7610
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
7611
- }
7612
- }, d);
7613
- };
7614
- insert();
7615
- return true;
7616
- }
7617
- }
7618
- ])
7619
- ];
7620
- };
7621
8493
  export {
7622
8494
  Cursor,
7623
- EditorInputMode,
7624
- EditorInputModes,
7625
- EditorState3 as EditorState,
7626
- EditorView30 as EditorView,
7627
- EditorViewMode,
7628
- EditorViewModes,
8495
+ EditorState4 as EditorState,
8496
+ EditorView33 as EditorView,
7629
8497
  Inline,
7630
8498
  InputModeExtensions,
7631
8499
  List,
@@ -7642,6 +8510,7 @@ export {
7642
8510
  addStyle,
7643
8511
  annotations,
7644
8512
  autoScroll,
8513
+ autoScrollEffect,
7645
8514
  autocomplete,
7646
8515
  automerge,
7647
8516
  awareness,
@@ -7655,9 +8524,14 @@ export {
7655
8524
  commentClickedEffect,
7656
8525
  comments,
7657
8526
  commentsState,
8527
+ compactSlots,
7658
8528
  convertTreeToJson,
8529
+ crawler,
8530
+ crawlerActiveEffect,
8531
+ crawlerLineEffect,
7659
8532
  createBasicExtensions,
7660
8533
  createComment,
8534
+ createCrawler,
7661
8535
  createDataExtensions,
7662
8536
  createEditorStateStore,
7663
8537
  createEditorStateTransaction,
@@ -7677,12 +8551,12 @@ export {
7677
8551
  defaultThemeSlots,
7678
8552
  deleteItem,
7679
8553
  documentId,
8554
+ documentSlots,
7680
8555
  dropFile,
8556
+ editorClassNames,
7681
8557
  editorInputMode,
7682
- editorSlots,
7683
- editorWidth,
7684
- editorWithToolbarLayout,
7685
8558
  extendedMarkdown,
8559
+ fader,
7686
8560
  filterChars,
7687
8561
  flattenRect,
7688
8562
  focus,
@@ -7718,6 +8592,7 @@ export {
7718
8592
  markdownTagsExtensions,
7719
8593
  matchCompletion,
7720
8594
  mention,
8595
+ mobileSlots,
7721
8596
  modalStateEffect,
7722
8597
  modalStateField,
7723
8598
  moveItemDown,
@@ -7737,10 +8612,10 @@ export {
7737
8612
  removeList,
7738
8613
  removeStyle,
7739
8614
  replacer,
8615
+ scrollPastEnd,
7740
8616
  scrollThreadIntoView,
7741
- scrollToBottomEffect,
7742
8617
  scrollToLine,
7743
- scrollToLineEffect,
8618
+ scroller,
7744
8619
  selectionState,
7745
8620
  setBlockquote,
7746
8621
  setComments,
@@ -7748,10 +8623,8 @@ export {
7748
8623
  setSelection,
7749
8624
  setStyle,
7750
8625
  singleValueFacet,
7751
- smoothScroll,
7752
- stackItemContentEditorClassNames,
8626
+ snippets2 as snippets,
7753
8627
  staticCompletion,
7754
- streamer,
7755
8628
  submit,
7756
8629
  tabbable,
7757
8630
  table,
@@ -7770,7 +8643,12 @@ export {
7770
8643
  treeFacet,
7771
8644
  typeahead,
7772
8645
  typewriter,
8646
+ typewriterBypass,
8647
+ typewriterDrainingEffect,
7773
8648
  wrapWithCatch,
8649
+ xmlBlockDecoration,
8650
+ xmlElementLength,
8651
+ xmlFormatting,
7774
8652
  xmlTagContextEffect,
7775
8653
  xmlTagResetEffect,
7776
8654
  xmlTagUpdateEffect,