@dxos/ui-editor 0.8.4-main.d05673bc65 → 0.8.4-main.dfabb4ec29

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 (152) hide show
  1. package/dist/lib/browser/index.mjs +1547 -768
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/types/index.mjs +26 -6
  5. package/dist/lib/browser/types/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/index.mjs +1548 -768
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/types/index.mjs +27 -6
  10. package/dist/lib/node-esm/types/index.mjs.map +4 -4
  11. package/dist/types/src/defaults.d.ts +4 -10
  12. package/dist/types/src/defaults.d.ts.map +1 -1
  13. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  14. package/dist/types/src/extensions/auto-scroll.d.ts +14 -2
  15. package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -1
  16. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -1
  17. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -1
  18. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +5 -2
  19. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -1
  20. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -1
  21. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  23. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  25. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  27. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  28. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  29. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  30. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  31. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  32. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  33. package/dist/types/src/extensions/factories.d.ts +4 -3
  34. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  35. package/dist/types/src/extensions/factories.test.d.ts +2 -0
  36. package/dist/types/src/extensions/factories.test.d.ts.map +1 -0
  37. package/dist/types/src/extensions/focus.d.ts +1 -1
  38. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  39. package/dist/types/src/extensions/index.d.ts +2 -1
  40. package/dist/types/src/extensions/index.d.ts.map +1 -1
  41. package/dist/types/src/extensions/json.d.ts.map +1 -1
  42. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  43. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -0
  45. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  47. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  49. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  50. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  51. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  52. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  53. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  54. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  55. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  56. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -1
  57. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  58. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  59. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  60. package/dist/types/src/extensions/preview/preview.d.ts +2 -0
  61. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  62. package/dist/types/src/extensions/replacer.d.ts.map +1 -1
  63. package/dist/types/src/extensions/scroll-past-end.d.ts +3 -0
  64. package/dist/types/src/extensions/scroll-past-end.d.ts.map +1 -0
  65. package/dist/types/src/extensions/scroller.d.ts +12 -10
  66. package/dist/types/src/extensions/scroller.d.ts.map +1 -1
  67. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  68. package/dist/types/src/extensions/snippets.d.ts +10 -0
  69. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  70. package/dist/types/src/extensions/submit.d.ts.map +1 -1
  71. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
  72. package/dist/types/src/extensions/tags/fader.d.ts +12 -0
  73. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
  74. package/dist/types/src/extensions/tags/index.d.ts +4 -1
  75. package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
  76. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  77. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  78. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  79. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  80. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  81. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  82. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  83. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  84. package/dist/types/src/extensions/tags/xml-tags.d.ts +28 -8
  85. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  86. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -1
  87. package/dist/types/src/index.d.ts +0 -1
  88. package/dist/types/src/index.d.ts.map +1 -1
  89. package/dist/types/src/styles/theme.d.ts +2 -2
  90. package/dist/types/src/styles/theme.d.ts.map +1 -1
  91. package/dist/types/src/types/types.d.ts +4 -4
  92. package/dist/types/src/types/types.d.ts.map +1 -1
  93. package/dist/types/src/util/cursor.d.ts.map +1 -1
  94. package/dist/types/src/util/debug.d.ts.map +1 -1
  95. package/dist/types/src/util/decorations.d.ts.map +1 -1
  96. package/dist/types/src/util/dom.d.ts.map +1 -1
  97. package/dist/types/src/util/facet.d.ts.map +1 -1
  98. package/dist/types/src/util/util.d.ts.map +1 -1
  99. package/dist/types/tsconfig.tsbuildinfo +1 -1
  100. package/package.json +34 -37
  101. package/src/defaults.ts +33 -20
  102. package/src/extensions/auto-scroll.ts +120 -12
  103. package/src/extensions/autocomplete/placeholder.ts +37 -18
  104. package/src/extensions/automerge/automerge.test.tsx +35 -9
  105. package/src/extensions/automerge/automerge.ts +2 -5
  106. package/src/extensions/comments.ts +0 -1
  107. package/src/extensions/factories.test.ts +88 -0
  108. package/src/extensions/factories.ts +26 -8
  109. package/src/extensions/folding.ts +3 -20
  110. package/src/extensions/index.ts +2 -1
  111. package/src/extensions/markdown/action.ts +0 -1
  112. package/src/extensions/markdown/bundle.ts +23 -9
  113. package/src/extensions/markdown/decorate.ts +11 -9
  114. package/src/extensions/markdown/highlight.ts +4 -10
  115. package/src/extensions/markdown/parser.test.ts +0 -1
  116. package/src/extensions/markdown/styles.ts +37 -4
  117. package/src/extensions/markdown/table.ts +24 -2
  118. package/src/extensions/outliner/outliner.test.ts +0 -1
  119. package/src/extensions/outliner/outliner.ts +0 -1
  120. package/src/extensions/outliner/tree.test.ts +0 -1
  121. package/src/extensions/preview/preview.ts +54 -7
  122. package/src/extensions/scroll-past-end.ts +32 -0
  123. package/src/extensions/scroller.ts +49 -25
  124. package/src/extensions/snippets.ts +67 -0
  125. package/src/extensions/tags/extended-markdown.test.ts +120 -2
  126. package/src/extensions/tags/extended-markdown.ts +80 -1
  127. package/src/extensions/tags/fader.ts +195 -0
  128. package/src/extensions/tags/index.ts +4 -1
  129. package/src/extensions/tags/testing/text.md +36 -0
  130. package/src/extensions/tags/testing/text.txt +35 -0
  131. package/src/extensions/tags/typewriter.test.ts +65 -0
  132. package/src/extensions/tags/typewriter.ts +594 -0
  133. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  134. package/src/extensions/tags/xml-formatting.ts +125 -0
  135. package/src/extensions/tags/xml-tags.ts +179 -31
  136. package/src/extensions/tags/xml-util.test.ts +199 -24
  137. package/src/extensions/tags/xml-util.ts +62 -5
  138. package/src/index.ts +0 -1
  139. package/src/styles/theme.ts +39 -25
  140. package/src/types/types.ts +10 -2
  141. package/src/typings.d.ts +8 -0
  142. package/src/util/cursor.ts +0 -1
  143. package/dist/lib/browser/chunk-HL3YF6WC.mjs +0 -22
  144. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +0 -7
  145. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs +0 -24
  146. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +0 -7
  147. package/dist/types/src/extensions/tags/streamer.d.ts +0 -12
  148. package/dist/types/src/extensions/tags/streamer.d.ts.map +0 -1
  149. package/dist/types/src/extensions/typewriter.d.ts +0 -10
  150. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  151. package/src/extensions/tags/streamer.ts +0 -243
  152. package/src/extensions/typewriter.ts +0 -68
@@ -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 = "!mx-auto w-full max-w-[min(50rem,100%-4rem)]";
17
- var editorSlots = {
18
- scroll: {
19
- className: "pt-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("dx-attention-surface p-0.5 dx-focus-ring-inset data-[toolbar=disabled]:pt-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-h-24" : "dx-container overflow-hidden");
27
40
 
28
41
  // src/extensions/annotations.ts
29
42
  import { RangeSetBuilder } from "@codemirror/state";
@@ -207,7 +220,7 @@ var singleValueFacet = (defaultValue) => Facet.define({
207
220
  var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
208
221
  var defaultCursorConverter = {
209
222
  toCursor: (position) => position.toString(),
210
- fromCursor: (cursor2) => parseInt(cursor2)
223
+ fromCursor: (cursor) => parseInt(cursor)
211
224
  };
212
225
  var Cursor = class _Cursor {
213
226
  static converter = singleValueFacet(defaultCursorConverter);
@@ -220,9 +233,9 @@ var Cursor = class _Cursor {
220
233
  to
221
234
  ].join(":");
222
235
  };
223
- static getRangeFromCursor = (state, cursor2) => {
236
+ static getRangeFromCursor = (state, cursor) => {
224
237
  const cursorConverter2 = state.facet(_Cursor.converter);
225
- const parts = cursor2.split(":");
238
+ const parts = cursor.split(":");
226
239
  const from = cursorConverter2.fromCursor(parts[0]);
227
240
  const to = cursorConverter2.fromCursor(parts[1]);
228
241
  return from !== void 0 && to !== void 0 ? {
@@ -258,12 +271,7 @@ var wrapWithCatch = (fn, label) => {
258
271
  } catch (err) {
259
272
  log.catch(err, {
260
273
  label
261
- }, {
262
- F: __dxlog_file,
263
- L: 20,
264
- S: void 0,
265
- C: (f, a) => f(...a)
266
- });
274
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 13, S: void 0 });
267
275
  }
268
276
  };
269
277
  };
@@ -289,12 +297,7 @@ var logChanges = (trs) => {
289
297
  if (changes.length) {
290
298
  log("changes", {
291
299
  changes
292
- }, {
293
- F: __dxlog_file,
294
- L: 54,
295
- S: void 0,
296
- C: (f, a) => f(...a)
297
- });
300
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 44, S: void 0 });
298
301
  }
299
302
  };
300
303
 
@@ -360,30 +363,37 @@ var insertAtLineStart = (view, from, insert) => {
360
363
  };
361
364
 
362
365
  // src/extensions/autocomplete/placeholder.ts
363
- var placeholder = ({ content, delay = 3e3 }) => {
366
+ var placeholder = ({ content, delay = 3e3, focusOnly = false }) => {
364
367
  const plugin = ViewPlugin3.fromClass(class {
365
368
  _timeout;
366
369
  _decorations = Decoration3.none;
367
370
  update(update2) {
371
+ if (!update2.docChanged && !update2.selectionSet && !update2.focusChanged) {
372
+ return;
373
+ }
368
374
  if (this._timeout) {
369
375
  window.clearTimeout(this._timeout);
370
376
  this._timeout = void 0;
371
377
  }
378
+ this._decorations = Decoration3.none;
379
+ if (focusOnly && !update2.view.hasFocus) {
380
+ return;
381
+ }
372
382
  const activeLine = update2.view.state.doc.lineAt(update2.view.state.selection.main.head);
373
- const isEmpty = activeLine.text.trim() === "";
374
- if (isEmpty) {
375
- const lineStart = activeLine.from;
376
- this._timeout = setTimeout(() => {
377
- this._decorations = Decoration3.set([
378
- Decoration3.widget({
379
- widget: new PlaceholderWidget(content),
380
- side: 1
381
- }).range(lineStart)
382
- ]);
383
- update2.view.update([]);
384
- }, delay);
383
+ if (activeLine.text.trim() !== "") {
384
+ return;
385
385
  }
386
- this._decorations = Decoration3.none;
386
+ const lineStart = activeLine.from;
387
+ const view = update2.view;
388
+ this._timeout = setTimeout(() => {
389
+ this._decorations = Decoration3.set([
390
+ Decoration3.widget({
391
+ widget: new PlaceholderWidget(content),
392
+ side: 1
393
+ }).range(lineStart)
394
+ ]);
395
+ view.update([]);
396
+ }, delay);
387
397
  }
388
398
  destroy() {
389
399
  if (this._timeout) {
@@ -502,9 +512,11 @@ var typeahead = ({ onComplete } = {}) => {
502
512
  };
503
513
 
504
514
  // src/extensions/auto-scroll.ts
515
+ import { StateEffect as StateEffect2 } from "@codemirror/state";
505
516
  import { EditorView as EditorView5, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
506
517
  import { addEventListener, combine, throttle } from "@dxos/async";
507
518
  import { Domino } from "@dxos/ui";
519
+ import { getSize } from "@dxos/ui-theme";
508
520
 
509
521
  // src/extensions/scroller.ts
510
522
  import { StateEffect } from "@codemirror/state";
@@ -561,15 +573,14 @@ var scroller = ({ overScroll = 0 } = {}) => {
561
573
  }
562
574
  requestAnimationFrame(() => {
563
575
  this.view.scrollDOM.scrollTo({
564
- top: targetScrollTop,
565
- behavior
576
+ top: targetScrollTop
566
577
  });
567
578
  });
568
579
  }
569
580
  });
570
581
  return [
571
582
  scrollPlugin,
572
- // Listen for effect.s
583
+ // Listen for effect.
573
584
  EditorView4.updateListener.of((update2) => {
574
585
  update2.transactions.forEach((transaction) => {
575
586
  try {
@@ -584,23 +595,19 @@ var scroller = ({ overScroll = 0 } = {}) => {
584
595
  }
585
596
  }
586
597
  } catch (err) {
587
- log2.catch(err, void 0, {
588
- F: __dxlog_file2,
589
- L: 145,
590
- S: void 0,
591
- C: (f, a) => f(...a)
592
- });
598
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 91, S: void 0 });
593
599
  }
594
600
  });
595
601
  }),
596
602
  // Styles.
597
603
  EditorView4.theme({
598
- ".cm-content": {
599
- paddingBottom: `${overScroll}px`
600
- },
601
604
  ".cm-scroller": {
602
- overflowAnchor: "none",
603
- paddingBottom: "0"
605
+ overflowY: "scroll",
606
+ // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks
607
+ // expanding their TogglePanel), the browser picks a stable element near the viewport
608
+ // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning
609
+ // logic still has the final word when pinned (forces scrollTop to scrollHeight).
610
+ overflowAnchor: "auto"
604
611
  },
605
612
  ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
606
613
  display: "none"
@@ -612,6 +619,16 @@ var scroller = ({ overScroll = 0 } = {}) => {
612
619
  "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
613
620
  background: "var(--color-scrollbar-thumb)"
614
621
  },
622
+ // Spacer below the last text line. Implemented as a real block pseudo-element
623
+ // (rather than `padding-bottom` on `.cm-content`) so it materializes in the
624
+ // scroller's `scrollHeight` regardless of how `padding` is reset by the base
625
+ // theme or downstream classes — this is what gives auto-scroll its head-room
626
+ // so the last line stays `overScroll` px above the viewport bottom.
627
+ ".cm-content::after": {
628
+ content: '""',
629
+ display: "block",
630
+ height: `${overScroll}px`
631
+ },
615
632
  ".cm-scroll-button": {
616
633
  position: "absolute",
617
634
  bottom: "0.5rem",
@@ -620,22 +637,28 @@ var scroller = ({ overScroll = 0 } = {}) => {
620
637
  })
621
638
  ];
622
639
  };
623
- function createCrawler(view, k = 0.3, maxStep = 2, targetDelta = 0.5) {
640
+ function createCrawler(view, omega = 5, snapThreshold = 5, snapVelocity = 50) {
624
641
  const el = view.scrollDOM;
625
642
  let currentTop = 0;
643
+ let velocity = 0;
626
644
  let rafId = null;
627
- function frame() {
645
+ let lastTime = 0;
646
+ function frame(now) {
647
+ const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1e3);
648
+ lastTime = now;
628
649
  const targetTop = el.scrollHeight - el.clientHeight;
629
650
  const delta = targetTop - currentTop;
630
- const absDelta = Math.abs(delta);
631
- if (absDelta < targetDelta) {
651
+ if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {
632
652
  el.scrollTop = targetTop;
633
653
  currentTop = targetTop;
654
+ velocity = 0;
634
655
  rafId = null;
656
+ lastTime = 0;
635
657
  return;
636
658
  }
637
- const step = Math.sign(delta) * Math.min(absDelta, Math.max(1, Math.min(absDelta * k, maxStep)));
638
- currentTop += step;
659
+ const accel = omega * omega * delta - 2 * omega * velocity;
660
+ velocity += accel * dt;
661
+ currentTop += velocity * dt;
639
662
  el.scrollTop = currentTop;
640
663
  rafId = requestAnimationFrame(frame);
641
664
  }
@@ -643,12 +666,15 @@ function createCrawler(view, k = 0.3, maxStep = 2, targetDelta = 0.5) {
643
666
  scroll: () => {
644
667
  if (rafId === null) {
645
668
  currentTop = el.scrollTop;
669
+ lastTime = 0;
646
670
  rafId = requestAnimationFrame(frame);
647
671
  }
648
672
  },
649
673
  cancel: () => {
650
674
  if (rafId !== null) {
651
675
  cancelAnimationFrame(rafId);
676
+ velocity = 0;
677
+ lastTime = 0;
652
678
  rafId = null;
653
679
  }
654
680
  }
@@ -656,26 +682,64 @@ function createCrawler(view, k = 0.3, maxStep = 2, targetDelta = 0.5) {
656
682
  }
657
683
 
658
684
  // src/extensions/auto-scroll.ts
659
- var autoScroll = (_ = {}) => {
685
+ var autoScrollEffect = StateEffect2.define();
686
+ var autoScroll = ({ scrollOnResize = true } = {}) => {
660
687
  let buttonContainer;
661
688
  let isPinned = true;
689
+ let jumpPending = false;
690
+ let enabled = true;
691
+ let firstUpdate = true;
662
692
  const setPinned = (pinned) => {
663
693
  buttonContainer?.classList.toggle("opacity-0", pinned);
664
694
  isPinned = pinned;
665
695
  };
666
696
  return [
667
- // Update listener for logging when scrolling is needed.
668
- EditorView5.updateListener.of(({ view, heightChanged, state }) => {
697
+ // Update listener for scrolling when content changes.
698
+ EditorView5.updateListener.of((update2) => {
699
+ const { view, heightChanged, state, startState } = update2;
700
+ for (const tr of update2.transactions) {
701
+ for (const effect of tr.effects) {
702
+ if (effect.is(autoScrollEffect)) {
703
+ enabled = effect.value;
704
+ if (enabled) {
705
+ setPinned(true);
706
+ view.dispatch({
707
+ effects: scrollerCrawlEffect.of(true)
708
+ });
709
+ } else {
710
+ view.dispatch({
711
+ effects: scrollerCrawlEffect.of(false)
712
+ });
713
+ }
714
+ }
715
+ }
716
+ }
717
+ if (!enabled) {
718
+ return;
719
+ }
720
+ if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
721
+ firstUpdate = false;
722
+ jumpPending = true;
723
+ requestAnimationFrame(() => {
724
+ view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
725
+ jumpPending = false;
726
+ });
727
+ return;
728
+ }
729
+ firstUpdate = false;
730
+ if (jumpPending) {
731
+ return;
732
+ }
669
733
  if (heightChanged) {
670
734
  if (isPinned) {
671
735
  const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
672
736
  const delta = scrollHeight - scrollTop - clientHeight;
673
- if (delta > 0 && scrollTop > 0) {
737
+ if (delta > 0) {
674
738
  setPinned(true);
675
739
  view.dispatch({
676
740
  effects: scrollerCrawlEffect.of(true)
677
741
  });
678
- } else if (delta < 0) {
742
+ } else if (delta < -1) {
679
743
  setPinned(false);
680
744
  }
681
745
  } else {
@@ -685,6 +749,46 @@ var autoScroll = (_ = {}) => {
685
749
  }
686
750
  }
687
751
  }),
752
+ // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,
753
+ // window resize). Doc-driven height changes are handled by the updateListener above; this
754
+ // observer covers the case where the viewport changes while the doc length is unchanged.
755
+ scrollOnResize ? ViewPlugin6.fromClass(class {
756
+ observer;
757
+ firstObservation = true;
758
+ destroyed = false;
759
+ constructor(view) {
760
+ const onResize = throttle(() => {
761
+ if (this.destroyed || !enabled) {
762
+ return;
763
+ }
764
+ setPinned(true);
765
+ requestAnimationFrame(() => {
766
+ if (this.destroyed) {
767
+ return;
768
+ }
769
+ view.scrollDOM.scrollTo({
770
+ top: view.scrollDOM.scrollHeight,
771
+ behavior: "instant"
772
+ });
773
+ view.dispatch({
774
+ effects: scrollerCrawlEffect.of(false)
775
+ });
776
+ });
777
+ }, 50);
778
+ this.observer = new ResizeObserver(() => {
779
+ if (this.firstObservation) {
780
+ this.firstObservation = false;
781
+ return;
782
+ }
783
+ onResize();
784
+ });
785
+ this.observer.observe(view.scrollDOM);
786
+ }
787
+ destroy() {
788
+ this.destroyed = true;
789
+ this.observer.disconnect();
790
+ }
791
+ }) : [],
688
792
  // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
689
793
  ViewPlugin6.fromClass(class {
690
794
  cleanup;
@@ -710,12 +814,12 @@ var autoScroll = (_ = {}) => {
710
814
  // Scroll button.
711
815
  ViewPlugin6.fromClass(class {
712
816
  constructor(view) {
713
- const icon = Domino.of("dx-icon").attributes({
817
+ const icon = Domino.of("dx-icon").classNames(getSize(4)).attributes({
714
818
  icon: "ph--arrow-down--regular"
715
819
  });
716
820
  const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
717
821
  "data-density": "fine"
718
- }).children(icon).on("click", () => {
822
+ }).append(icon).on("click", () => {
719
823
  setPinned(true);
720
824
  view.dispatch({
721
825
  effects: scrollerLineEffect.of({
@@ -725,7 +829,7 @@ var autoScroll = (_ = {}) => {
725
829
  })
726
830
  });
727
831
  });
728
- buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").children(button).root;
832
+ buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").append(button).root;
729
833
  view.scrollDOM.parentElement.appendChild(buttonContainer);
730
834
  }
731
835
  })
@@ -760,35 +864,25 @@ var cursorConverter = (accessor) => ({
760
864
  try {
761
865
  return toCursor(accessor, pos, assoc);
762
866
  } catch (err) {
763
- log3.catch(err, void 0, {
764
- F: __dxlog_file3,
765
- L: 15,
766
- S: void 0,
767
- C: (f, a) => f(...a)
768
- });
867
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 11, S: void 0 });
769
868
  return "";
770
869
  }
771
870
  },
772
- fromCursor: (cursor2) => {
871
+ fromCursor: (cursor) => {
773
872
  try {
774
- return fromCursor(accessor, cursor2);
873
+ return fromCursor(accessor, cursor);
775
874
  } catch (err) {
776
- log3.catch(err, void 0, {
777
- F: __dxlog_file3,
778
- L: 24,
779
- S: void 0,
780
- C: (f, a) => f(...a)
781
- });
875
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 19, S: void 0 });
782
876
  return 0;
783
877
  }
784
878
  }
785
879
  });
786
880
 
787
881
  // src/extensions/automerge/defs.ts
788
- import { Annotation, StateEffect as StateEffect2 } from "@codemirror/state";
882
+ import { Annotation, StateEffect as StateEffect3 } from "@codemirror/state";
789
883
  var getPath = (state, field) => state.field(field).path;
790
884
  var getLastHeads = (state, field) => state.field(field).lastHeads;
791
- var updateHeadsEffect = StateEffect2.define({});
885
+ var updateHeadsEffect = StateEffect3.define({});
792
886
  var updateHeads = (newHeads) => updateHeadsEffect.of({
793
887
  newHeads
794
888
  });
@@ -963,12 +1057,7 @@ var Syncer = class {
963
1057
  this._pending = false;
964
1058
  }
965
1059
  onEditorChange(view) {
966
- log4("onEditorChange", void 0, {
967
- F: __dxlog_file4,
968
- L: 45,
969
- S: this,
970
- C: (f, a) => f(...a)
971
- });
1060
+ log4("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 35, S: this });
972
1061
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
973
1062
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
974
1063
  if (newHeads) {
@@ -979,12 +1068,7 @@ var Syncer = class {
979
1068
  }
980
1069
  }
981
1070
  onAutomergeChange(view) {
982
- log4("onAutomergeChange", void 0, {
983
- F: __dxlog_file4,
984
- L: 60,
985
- S: this,
986
- C: (f, a) => f(...a)
987
- });
1071
+ log4("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 47, S: this });
988
1072
  const oldHeads = getLastHeads(view.state, this._state);
989
1073
  const newHeads = A2.getHeads(this._handle.doc());
990
1074
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1046,14 +1130,16 @@ var automerge = (accessor) => {
1046
1130
  const value = DocAccessor.getValue(accessor);
1047
1131
  const current = this._view.state.doc.toString();
1048
1132
  if (value !== current) {
1049
- console.warn("ENABLING INITIAL SYNC -- THIS MAY BE A REGRESSION");
1050
1133
  this._view.dispatch({
1051
1134
  changes: {
1052
1135
  from: 0,
1053
1136
  to: this._view.state.doc.length,
1054
1137
  insert: value
1055
1138
  },
1056
- annotations: initialSync
1139
+ annotations: [
1140
+ initialSync,
1141
+ reconcileAnnotation.of(true)
1142
+ ]
1057
1143
  });
1058
1144
  }
1059
1145
  });
@@ -1105,10 +1191,7 @@ var awareness = (provider = dummyProvider) => {
1105
1191
  ];
1106
1192
  };
1107
1193
  var RemoteSelectionsDecorator = class {
1108
- _ctx = new Context(void 0, {
1109
- F: __dxlog_file5,
1110
- L: 80
1111
- });
1194
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 33 });
1112
1195
  _cursorConverter;
1113
1196
  _provider;
1114
1197
  _lastAnchor;
@@ -1339,10 +1422,7 @@ var SpaceAwarenessProvider = class {
1339
1422
  this._info = info;
1340
1423
  }
1341
1424
  open() {
1342
- this._ctx = new Context2(void 0, {
1343
- F: __dxlog_file6,
1344
- L: 57
1345
- });
1425
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 28 });
1346
1426
  this._postTask = new DeferredTask(this._ctx, async () => {
1347
1427
  if (this._localState) {
1348
1428
  await this._messenger.postMessage(this._channel, {
@@ -1369,12 +1449,7 @@ var SpaceAwarenessProvider = class {
1369
1449
  }).catch((err) => {
1370
1450
  log5.debug("failed to query awareness", {
1371
1451
  err
1372
- }, {
1373
- F: __dxlog_file6,
1374
- L: 91,
1375
- S: this,
1376
- C: (f, a) => f(...a)
1377
- });
1452
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 57, S: this });
1378
1453
  });
1379
1454
  }
1380
1455
  close() {
@@ -1386,15 +1461,7 @@ var SpaceAwarenessProvider = class {
1386
1461
  return Array.from(this._remoteStates.values());
1387
1462
  }
1388
1463
  update(position) {
1389
- invariant(this._postTask, void 0, {
1390
- F: __dxlog_file6,
1391
- L: 106,
1392
- S: this,
1393
- A: [
1394
- "this._postTask",
1395
- ""
1396
- ]
1397
- });
1464
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 71, S: this, A: ["this._postTask", ""] });
1398
1465
  this._localState = {
1399
1466
  peerId: this._peerId,
1400
1467
  position,
@@ -1403,27 +1470,11 @@ var SpaceAwarenessProvider = class {
1403
1470
  this._postTask.schedule();
1404
1471
  }
1405
1472
  _handleQueryMessage() {
1406
- invariant(this._postTask, void 0, {
1407
- F: __dxlog_file6,
1408
- L: 117,
1409
- S: this,
1410
- A: [
1411
- "this._postTask",
1412
- ""
1413
- ]
1414
- });
1473
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 80, S: this, A: ["this._postTask", ""] });
1415
1474
  this._postTask.schedule();
1416
1475
  }
1417
1476
  _handlePostMessage(message) {
1418
- invariant(message.kind === "post", void 0, {
1419
- F: __dxlog_file6,
1420
- L: 122,
1421
- S: this,
1422
- A: [
1423
- "message.kind === 'post'",
1424
- ""
1425
- ]
1426
- });
1477
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1427
1478
  this._remoteStates.set(message.state.peerId, message.state);
1428
1479
  this.remoteStateChange.emit();
1429
1480
  }
@@ -1552,15 +1603,7 @@ var Blaster = class {
1552
1603
  return this._node;
1553
1604
  }
1554
1605
  initialize() {
1555
- invariant2(!this._canvas && !this._ctx, void 0, {
1556
- F: __dxlog_file7,
1557
- L: 142,
1558
- S: this,
1559
- A: [
1560
- "!this._canvas && !this._ctx",
1561
- ""
1562
- ]
1563
- });
1606
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1564
1607
  this._canvas = document.createElement("canvas");
1565
1608
  this._canvas.id = "code-blast-canvas";
1566
1609
  this._canvas.style.position = "absolute";
@@ -1589,15 +1632,7 @@ var Blaster = class {
1589
1632
  }
1590
1633
  }
1591
1634
  start() {
1592
- invariant2(this._canvas && this._ctx, void 0, {
1593
- F: __dxlog_file7,
1594
- L: 181,
1595
- S: this,
1596
- A: [
1597
- "this._canvas && this._ctx",
1598
- ""
1599
- ]
1600
- });
1635
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1601
1636
  this._running = true;
1602
1637
  this.loop();
1603
1638
  }
@@ -1832,13 +1867,13 @@ var blocks = () => [
1832
1867
  ];
1833
1868
 
1834
1869
  // src/extensions/bookmarks.ts
1835
- import { Prec as Prec3, StateEffect as StateEffect3, StateField as StateField2 } from "@codemirror/state";
1870
+ import { Prec as Prec3, StateEffect as StateEffect4, StateField as StateField2 } from "@codemirror/state";
1836
1871
  import { keymap as keymap4 } from "@codemirror/view";
1837
1872
  import { log as log6 } from "@dxos/log";
1838
1873
  var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1839
- var addBookmark = StateEffect3.define();
1840
- var removeBookmark = StateEffect3.define();
1841
- var clearBookmarks = StateEffect3.define();
1874
+ var addBookmark = StateEffect4.define();
1875
+ var removeBookmark = StateEffect4.define();
1876
+ var clearBookmarks = StateEffect4.define();
1842
1877
  var bookmarks = () => {
1843
1878
  return [
1844
1879
  bookmarksField,
@@ -1847,12 +1882,7 @@ var bookmarks = () => {
1847
1882
  key: "Mod-ArrowUp",
1848
1883
  run: (view) => {
1849
1884
  const bookmarks2 = view.state.field(bookmarksField);
1850
- log6("up", bookmarks2, {
1851
- F: __dxlog_file8,
1852
- L: 29,
1853
- S: void 0,
1854
- C: (f, a) => f(...a)
1855
- });
1885
+ log6("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 18, S: void 0 });
1856
1886
  return true;
1857
1887
  }
1858
1888
  },
@@ -1860,12 +1890,7 @@ var bookmarks = () => {
1860
1890
  key: "Mod-ArrowDown",
1861
1891
  run: (view) => {
1862
1892
  const bookmarks2 = view.state.field(bookmarksField);
1863
- log6("down", bookmarks2, {
1864
- F: __dxlog_file8,
1865
- L: 37,
1866
- S: void 0,
1867
- C: (f, a) => f(...a)
1868
- });
1893
+ log6("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0 });
1869
1894
  return true;
1870
1895
  }
1871
1896
  }
@@ -1902,7 +1927,7 @@ var bookmarksField = StateField2.define({
1902
1927
 
1903
1928
  // src/extensions/comments.ts
1904
1929
  import { invertedEffects } from "@codemirror/commands";
1905
- import { StateEffect as StateEffect4, StateField as StateField3 } from "@codemirror/state";
1930
+ import { StateEffect as StateEffect5, StateField as StateField3 } from "@codemirror/state";
1906
1931
  import { Decoration as Decoration7, EditorView as EditorView11, ViewPlugin as ViewPlugin10, hoverTooltip, keymap as keymap6 } from "@codemirror/view";
1907
1932
  import sortBy from "lodash.sortby";
1908
1933
  import { debounce as debounce2 } from "@dxos/async";
@@ -1930,28 +1955,12 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1930
1955
  };
1931
1956
  var createEditorStateStore = (keyPrefix) => ({
1932
1957
  getState: (id) => {
1933
- invariant3(id, void 0, {
1934
- F: __dxlog_file9,
1935
- L: 47,
1936
- S: void 0,
1937
- A: [
1938
- "id",
1939
- ""
1940
- ]
1941
- });
1958
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 26, S: void 0, A: ["id", ""] });
1942
1959
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
1943
1960
  return state ? JSON.parse(state) : void 0;
1944
1961
  },
1945
1962
  setState: (id, state) => {
1946
- invariant3(id, void 0, {
1947
- F: __dxlog_file9,
1948
- L: 53,
1949
- S: void 0,
1950
- A: [
1951
- "id",
1952
- ""
1953
- ]
1954
- });
1963
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 31, S: void 0, A: ["id", ""] });
1955
1964
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
1956
1965
  }
1957
1966
  });
@@ -2004,9 +2013,9 @@ var selectionState = ({ getState, setState } = {}) => {
2004
2013
 
2005
2014
  // src/extensions/comments.ts
2006
2015
  var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
2007
- var setComments = StateEffect4.define();
2008
- var setSelection = StateEffect4.define();
2009
- var setCommentState = StateEffect4.define();
2016
+ var setComments = StateEffect5.define();
2017
+ var setSelection = StateEffect5.define();
2018
+ var setCommentState = StateEffect5.define();
2010
2019
  var commentsState = StateField3.define({
2011
2020
  create: (state) => ({
2012
2021
  id: state.facet(documentId),
@@ -2073,12 +2082,7 @@ var commentsDecorations = EditorView11.decorations.compute([
2073
2082
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2074
2083
  const range = comment.range;
2075
2084
  if (!range) {
2076
- log7.warn("Invalid range:", range, {
2077
- F: __dxlog_file10,
2078
- L: 140,
2079
- S: void 0,
2080
- C: (f, a) => f(...a)
2081
- });
2085
+ log7.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 93, S: void 0 });
2082
2086
  return void 0;
2083
2087
  } else if (range.from === range.to) {
2084
2088
  return void 0;
@@ -2088,7 +2092,7 @@ var commentsDecorations = EditorView11.decorations.compute([
2088
2092
  }).filter(isNonNullable);
2089
2093
  return Decoration7.set(decorations2);
2090
2094
  });
2091
- var commentClickedEffect = StateEffect4.define();
2095
+ var commentClickedEffect = StateEffect5.define();
2092
2096
  var handleCommentClick = EditorView11.domEventHandlers({
2093
2097
  click: (event, view) => {
2094
2098
  let target = event.target;
@@ -2191,10 +2195,10 @@ var trackPastedComments = (onUpdate) => {
2191
2195
  const { comments: comments2 } = update2.startState.field(commentsState);
2192
2196
  const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
2193
2197
  if (!exists) {
2194
- const cursor2 = Cursor.getCursorFromRange(update2.state, comment);
2198
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
2195
2199
  onUpdate({
2196
2200
  id: comment.id,
2197
- cursor: cursor2
2201
+ cursor
2198
2202
  });
2199
2203
  }
2200
2204
  }
@@ -2206,7 +2210,7 @@ var mapTrackedComment = (comment, changes) => ({
2206
2210
  from: changes.mapPos(comment.from, 1),
2207
2211
  to: changes.mapPos(comment.to, 1)
2208
2212
  });
2209
- var restoreCommentEffect = StateEffect4.define({
2213
+ var restoreCommentEffect = StateEffect5.define({
2210
2214
  map: mapTrackedComment
2211
2215
  });
2212
2216
  var createComment = (view) => {
@@ -2223,13 +2227,13 @@ var createComment = (view) => {
2223
2227
  }
2224
2228
  });
2225
2229
  }
2226
- const cursor2 = Cursor.getCursorFromRange(view.state, {
2230
+ const cursor = Cursor.getCursorFromRange(view.state, {
2227
2231
  from,
2228
2232
  to
2229
2233
  });
2230
- if (cursor2) {
2234
+ if (cursor) {
2231
2235
  options.onCreate?.({
2232
- cursor: cursor2,
2236
+ cursor,
2233
2237
  from,
2234
2238
  location: view.coordsAtPos(from)
2235
2239
  });
@@ -2470,7 +2474,7 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap }
2470
2474
  import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
2471
2475
  import { searchKeymap } from "@codemirror/search";
2472
2476
  import { EditorState } from "@codemirror/state";
2473
- import { EditorView as EditorView15, ViewPlugin as ViewPlugin11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2, scrollPastEnd } from "@codemirror/view";
2477
+ import { EditorView as EditorView16, ViewPlugin as ViewPlugin12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2 } from "@codemirror/view";
2474
2478
  import { vscodeDarkStyle, vscodeLightStyle } from "@uiw/codemirror-theme-vscode";
2475
2479
  import defaultsDeep2 from "lodash.defaultsdeep";
2476
2480
  import { generateName } from "@dxos/display-name";
@@ -2482,28 +2486,28 @@ import { EditorView as EditorView13 } from "@codemirror/view";
2482
2486
  import { mx as mx3 } from "@dxos/ui-theme";
2483
2487
  var headings = {
2484
2488
  1: {
2485
- className: "text-4xl",
2486
- fontSize: "var(--text-4xl)",
2489
+ className: "text-3xl",
2490
+ fontSize: "var(--text-3xl)",
2487
2491
  lineHeight: "var(--text-4xl--line-height)"
2488
2492
  },
2489
2493
  2: {
2490
- className: "text-3xl",
2491
- fontSize: "var(--text-3xl)",
2494
+ className: "text-2xl",
2495
+ fontSize: "var(--text-2xl)",
2492
2496
  lineHeight: "var(--text-3xl--line-height)"
2493
2497
  },
2494
2498
  3: {
2495
- className: "text-2xl",
2496
- fontSize: "var(--text-2xl)",
2499
+ className: "text-xl",
2500
+ fontSize: "var(--text-xl)",
2497
2501
  lineHeight: "var(--text-2xl--line-height)"
2498
2502
  },
2499
2503
  4: {
2500
- className: "text-xl",
2501
- fontSize: "var(--text-xl)",
2504
+ className: "text-lg",
2505
+ fontSize: "var(--text-lg)",
2502
2506
  lineHeight: "var(--text-xl--line-height)"
2503
2507
  },
2504
2508
  5: {
2505
- className: "text-lg",
2506
- fontSize: "var(--text-lg)",
2509
+ className: "text-base",
2510
+ fontSize: "var(--text-base)",
2507
2511
  lineHeight: "var(--text-lg--line-height)"
2508
2512
  },
2509
2513
  6: {
@@ -2512,20 +2516,20 @@ var headings = {
2512
2516
  lineHeight: "var(--text-base--line-height)"
2513
2517
  }
2514
2518
  };
2519
+ var fontBody = '"Inter Variable", ui-sans-serif, system-ui, sans-serif';
2520
+ var fontMono = '"JetBrains Mono Variable", ui-monospace, "Cascadia Code", "Source Code Pro", monospace';
2515
2521
  var markdownTheme = {
2516
- code: "font-mono no-underline! text-cm-code",
2517
- codeMark: "font-mono text-cm-code-mark",
2518
- mark: "opacity-50",
2522
+ code: "font-mono! cm-code-inline",
2523
+ codeMark: "font-mono! cm-code-mark",
2524
+ mark: "font-mono!",
2519
2525
  heading: (level) => ({
2520
- className: mx3(headings[level].className, "font-light text-cm-heading"),
2526
+ className: mx3(headings[level].className, "font-light text-(--color-cm-heading-number)"),
2521
2527
  color: "var(--color-cm-heading) !important",
2522
2528
  lineHeight: headings[level].lineHeight,
2523
2529
  fontSize: headings[level].fontSize,
2524
2530
  fontWeight: "100 !important"
2525
2531
  })
2526
2532
  };
2527
- var fontBody = "Inter Variable, ui-sans-serif, system-ui, sans-serif";
2528
- var fontMono = "JetBrains Mono Variable, ui-monospace, Cascadia Code, Source Code Pro, monospace";
2529
2533
  var baseTheme = EditorView13.baseTheme({
2530
2534
  /**
2531
2535
  * Outer frame.
@@ -2538,12 +2542,21 @@ var baseTheme = EditorView13.baseTheme({
2538
2542
  * Scroller
2539
2543
  */
2540
2544
  ".cm-scroller": {
2541
- overflowAnchor: "none"
2545
+ // Browser scroll-anchoring: see comment in `scroller.ts`. `auto` lets the browser pin a
2546
+ // stable element near the viewport top so widget resizes (e.g. tool-block TogglePanel
2547
+ // open/close) don't jump the user's view.
2548
+ overflowAnchor: "auto"
2542
2549
  },
2543
2550
  ".cm-scroller::-webkit-scrollbar": {
2544
- width: "8px"
2551
+ width: "var(--scrollbar-size,8px)",
2552
+ height: "var(--scrollbar-size,8px)"
2553
+ },
2554
+ ".cm-scroller::-webkit-scrollbar-corner": {
2555
+ background: "transparent"
2556
+ },
2557
+ ".cm-scroller::-webkit-scrollbar-track": {
2558
+ background: "transparent"
2545
2559
  },
2546
- ".cm-scroller::-webkit-scrollbar-track": {},
2547
2560
  ".cm-scroller::-webkit-scrollbar-thumb": {
2548
2561
  background: "transparent",
2549
2562
  transition: "background 0.15s"
@@ -2557,7 +2570,6 @@ var baseTheme = EditorView13.baseTheme({
2557
2570
  */
2558
2571
  ".cm-content": {
2559
2572
  padding: "unset",
2560
- lineHeight: "24px",
2561
2573
  color: "unset"
2562
2574
  },
2563
2575
  /**
@@ -2588,9 +2600,16 @@ var baseTheme = EditorView13.baseTheme({
2588
2600
  * Line.
2589
2601
  */
2590
2602
  ".cm-line": {
2591
- lineHeight: "24px",
2603
+ lineHeight: 1.5,
2592
2604
  paddingInline: 0
2593
2605
  },
2606
+ /**
2607
+ * Force all inline children to inherit line-height to prevent monospace font metrics
2608
+ * (JetBrains Mono ascent/descent) from inflating the line box beyond 24px.
2609
+ */
2610
+ ".cm-line *": {
2611
+ lineHeight: "inherit"
2612
+ },
2594
2613
  ".cm-activeLine": {
2595
2614
  background: "var(--color-cm-active-line)"
2596
2615
  },
@@ -2768,10 +2787,9 @@ var editorGutter = EditorView13.theme({
2768
2787
  }
2769
2788
  });
2770
2789
  var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2771
- // Set metrics on the scroller (this is often what CM uses for layout).
2790
+ // Main content.
2772
2791
  ".cm-scroller": {
2773
- fontFamily: monospace ? fontMono : fontBody,
2774
- fontSize: "16px"
2792
+ fontFamily: monospace ? fontMono : fontBody
2775
2793
  },
2776
2794
  // Maintain defaults for UI components.
2777
2795
  ".cm-content, .cm-gutters, .cm-panel": {
@@ -2781,9 +2799,9 @@ var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2781
2799
  });
2782
2800
 
2783
2801
  // src/extensions/focus.ts
2784
- import { StateEffect as StateEffect5, StateField as StateField5 } from "@codemirror/state";
2802
+ import { StateEffect as StateEffect6, StateField as StateField5 } from "@codemirror/state";
2785
2803
  import { EditorView as EditorView14 } from "@codemirror/view";
2786
- var focusEffect = StateEffect5.define();
2804
+ var focusEffect = StateEffect6.define();
2787
2805
  var focusField = StateField5.define({
2788
2806
  create: () => false,
2789
2807
  update: (value, tr) => {
@@ -2811,9 +2829,32 @@ var focus = [
2811
2829
  })
2812
2830
  ];
2813
2831
 
2832
+ // src/extensions/scroll-past-end.ts
2833
+ import { EditorView as EditorView15, ViewPlugin as ViewPlugin11 } from "@codemirror/view";
2834
+ var scrollPastEndPlugin = ViewPlugin11.fromClass(class {
2835
+ height = 1e3;
2836
+ attrs = {
2837
+ style: "padding-bottom: 1000px"
2838
+ };
2839
+ update({ view }) {
2840
+ const lastLineBlock = view.lineBlockAt(view.state.doc.length);
2841
+ const height = view.dom.clientHeight - lastLineBlock.height - view.documentPadding.top - 0.5;
2842
+ if (height >= 0 && height !== this.height) {
2843
+ this.height = height;
2844
+ this.attrs = {
2845
+ style: `padding-bottom: ${height}px`
2846
+ };
2847
+ }
2848
+ }
2849
+ });
2850
+ var scrollPastEnd = () => [
2851
+ scrollPastEndPlugin,
2852
+ EditorView15.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?.attrs ?? null)
2853
+ ];
2854
+
2814
2855
  // src/extensions/factories.ts
2815
2856
  var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2816
- var tabbable = EditorView15.contentAttributes.of({
2857
+ var tabbable = EditorView16.contentAttributes.of({
2817
2858
  tabindex: "0"
2818
2859
  });
2819
2860
  var filterChars = (chars) => {
@@ -2866,13 +2907,8 @@ var createBasicExtensions = (propsProp) => {
2866
2907
  const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2867
2908
  return [
2868
2909
  // NOTE: Doesn't catch errors in keymap functions.
2869
- EditorView15.exceptionSink.of((err) => {
2870
- log8.catch(err, void 0, {
2871
- F: __dxlog_file11,
2872
- L: 131,
2873
- S: void 0,
2874
- C: (f, a) => f(...a)
2875
- });
2910
+ EditorView16.exceptionSink.of((err) => {
2911
+ log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
2876
2912
  }),
2877
2913
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2878
2914
  props.bracketMatching && bracketMatching(),
@@ -2881,7 +2917,7 @@ var createBasicExtensions = (propsProp) => {
2881
2917
  props.drawSelection && drawSelection({
2882
2918
  cursorBlinkRate: 1200
2883
2919
  }),
2884
- props.editable !== void 0 && EditorView15.editable.of(props.editable),
2920
+ props.editable !== void 0 && EditorView16.editable.of(props.editable),
2885
2921
  props.focus && focus,
2886
2922
  props.highlightActiveLine && highlightActiveLine(),
2887
2923
  props.history && history(),
@@ -2889,9 +2925,16 @@ var createBasicExtensions = (propsProp) => {
2889
2925
  lineNumbers(),
2890
2926
  editorGutter
2891
2927
  ],
2892
- props.lineWrapping && EditorView15.lineWrapping,
2928
+ props.lineWrapping && EditorView16.lineWrapping,
2893
2929
  props.placeholder && placeholder2(props.placeholder),
2894
2930
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2931
+ // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
2932
+ // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that
2933
+ // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits
2934
+ // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)
2935
+ // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers
2936
+ // depend on being able to populate the doc themselves.
2937
+ props.readOnly && EditorState.transactionFilter.of((tr) => tr.docChanged && (tr.isUserEvent("input") || tr.isUserEvent("delete") || tr.isUserEvent("undo") || tr.isUserEvent("redo")) ? [] : tr),
2895
2938
  props.scrollPastEnd && scrollPastEnd(),
2896
2939
  props.tabbable && tabbable,
2897
2940
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -2934,24 +2977,29 @@ var defaultStyles = {
2934
2977
  dark: vscodeDarkStyle,
2935
2978
  light: vscodeLightStyle
2936
2979
  };
2937
- var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp } = {}) => {
2980
+ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp, themeMode } = {}) => {
2938
2981
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
2939
2982
  return [
2940
2983
  baseTheme,
2941
- EditorView15.darkTheme.of(themeMode === "dark"),
2984
+ EditorView16.darkTheme.of(themeMode === "dark"),
2942
2985
  createFontTheme({
2943
2986
  monospace
2944
2987
  }),
2945
2988
  syntaxHighlightingProp && syntaxHighlighting(HighlightStyle.define(themeMode === "dark" ? defaultStyles.dark : defaultStyles.light)),
2946
- slots.editor?.className && EditorView15.editorAttributes.of({
2989
+ slots.editor?.className && EditorView16.editorAttributes.of({
2947
2990
  class: slots.editor.className
2948
2991
  }),
2949
- slots.content?.className && EditorView15.contentAttributes.of({
2992
+ slots.content?.className && EditorView16.contentAttributes.of({
2950
2993
  class: slots.content.className
2951
2994
  }),
2952
- slots.scroll?.className && ViewPlugin11.fromClass(class {
2995
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
2953
2996
  constructor(view) {
2954
- view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
2997
+ if (slots.scroller?.className) {
2998
+ view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
2999
+ }
3000
+ if (scrollbarThin) {
3001
+ view.scrollDOM.style.setProperty("--scrollbar-size", "4px");
3002
+ }
2955
3003
  }
2956
3004
  })
2957
3005
  ].filter(isTruthy2);
@@ -2980,7 +3028,7 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2980
3028
 
2981
3029
  // src/extensions/folding.ts
2982
3030
  import { codeFolding, foldGutter } from "@codemirror/language";
2983
- import { EditorView as EditorView16 } from "@codemirror/view";
3031
+ import { EditorView as EditorView17 } from "@codemirror/view";
2984
3032
  import { Domino as Domino2, mx as mx4 } from "@dxos/ui";
2985
3033
  var folding = () => {
2986
3034
  return [
@@ -2988,13 +3036,14 @@ var folding = () => {
2988
3036
  placeholderDOM: () => Domino2.of("span").root
2989
3037
  }),
2990
3038
  foldGutter({
3039
+ // NOTE: We can't animate since the element is remounted on state change.
2991
3040
  markerDOM: (open) => {
2992
- return Domino2.of("div").classNames("flex h-full justify-center items-center").children(Domino2.of("svg", Domino2.SVG).classNames(mx4("w-4 h-4 cursor-pointer", open && "rotate-90")).children(Domino2.of("use", Domino2.SVG).attributes({
3041
+ return Domino2.of("div").classNames("flex h-full justify-center items-center").append(Domino2.of("svg", Domino2.SVG).classNames(mx4("w-4 h-4 cursor-pointer", open && "rotate-90")).append(Domino2.of("use", Domino2.SVG).attributes({
2993
3042
  href: Domino2.icon("ph--caret-right--regular")
2994
3043
  }))).root;
2995
3044
  }
2996
3045
  }),
2997
- EditorView16.theme({
3046
+ EditorView17.theme({
2998
3047
  ".cm-foldGutter": {
2999
3048
  opacity: 0.3,
3000
3049
  transition: "opacity 0.3s",
@@ -3008,7 +3057,7 @@ var folding = () => {
3008
3057
  };
3009
3058
 
3010
3059
  // src/extensions/hashtag.ts
3011
- import { Decoration as Decoration8, EditorView as EditorView17, MatchDecorator, ViewPlugin as ViewPlugin12, WidgetType as WidgetType4 } from "@codemirror/view";
3060
+ import { Decoration as Decoration8, EditorView as EditorView18, MatchDecorator, ViewPlugin as ViewPlugin13, WidgetType as WidgetType4 } from "@codemirror/view";
3012
3061
  import { getHashStyles, mx as mx5 } from "@dxos/ui-theme";
3013
3062
  var TagWidget = class extends WidgetType4 {
3014
3063
  _text;
@@ -3029,7 +3078,7 @@ var tagMatcher = new MatchDecorator({
3029
3078
  })
3030
3079
  });
3031
3080
  var hashtag = () => [
3032
- ViewPlugin12.fromClass(class {
3081
+ ViewPlugin13.fromClass(class {
3033
3082
  tags;
3034
3083
  constructor(view) {
3035
3084
  this.tags = tagMatcher.createDeco(view);
@@ -3039,11 +3088,11 @@ var hashtag = () => [
3039
3088
  }
3040
3089
  }, {
3041
3090
  decorations: (instance) => instance.tags,
3042
- provide: (plugin) => EditorView17.atomicRanges.of((view) => {
3091
+ provide: (plugin) => EditorView18.atomicRanges.of((view) => {
3043
3092
  return view.plugin(plugin)?.tags || Decoration8.none;
3044
3093
  })
3045
3094
  }),
3046
- EditorView17.theme({
3095
+ EditorView18.theme({
3047
3096
  ".cm-tag": {
3048
3097
  borderRadius: "4px",
3049
3098
  marginRight: "6px",
@@ -3098,18 +3147,18 @@ var schemaLinter = (validate) => (view) => {
3098
3147
  };
3099
3148
 
3100
3149
  // src/extensions/listener.ts
3101
- import { EditorView as EditorView18 } from "@codemirror/view";
3150
+ import { EditorView as EditorView19 } from "@codemirror/view";
3102
3151
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
3103
3152
  var listener = ({ onFocus, onChange }) => {
3104
3153
  return [
3105
- onFocus && EditorView18.focusChangeEffect.of((state, focusing) => {
3154
+ onFocus && EditorView19.focusChangeEffect.of((state, focusing) => {
3106
3155
  onFocus({
3107
3156
  id: state.facet(documentId),
3108
3157
  focusing
3109
3158
  });
3110
3159
  return null;
3111
3160
  }),
3112
- onChange && EditorView18.updateListener.of(({ state, docChanged }) => {
3161
+ onChange && EditorView19.updateListener.of(({ state, docChanged }) => {
3113
3162
  if (docChanged) {
3114
3163
  onChange({
3115
3164
  id: state.facet(documentId),
@@ -3124,7 +3173,7 @@ var listener = ({ onFocus, onChange }) => {
3124
3173
  import { snippet } from "@codemirror/autocomplete";
3125
3174
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3126
3175
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
3127
- import { EditorView as EditorView19, keymap as keymap8 } from "@codemirror/view";
3176
+ import { EditorView as EditorView20, keymap as keymap8 } from "@codemirror/view";
3128
3177
  import { debounceAndThrottle } from "@dxos/async";
3129
3178
  var formattingEquals = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
3130
3179
  var Inline = /* @__PURE__ */ (function(Inline2) {
@@ -4213,7 +4262,7 @@ var getFormatting = (state) => {
4213
4262
  };
4214
4263
  };
4215
4264
  var formattingListener = (onStateChange, delay = 100) => {
4216
- return EditorView19.updateListener.of(debounceAndThrottle((update2) => {
4265
+ return EditorView20.updateListener.of(debounceAndThrottle((update2) => {
4217
4266
  if (update2.docChanged || update2.selectionSet) {
4218
4267
  onStateChange(getFormatting(update2.state));
4219
4268
  }
@@ -4274,8 +4323,7 @@ import { completionKeymap } from "@codemirror/autocomplete";
4274
4323
  import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
4275
4324
  import { jsonLanguage } from "@codemirror/lang-json";
4276
4325
  import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
4277
- import { xml } from "@codemirror/lang-xml";
4278
- import { LanguageDescription, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4326
+ import { foldNodeProp, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4279
4327
  import { languages } from "@codemirror/language-data";
4280
4328
  import { keymap as keymap9 } from "@codemirror/view";
4281
4329
  import { isTruthy as isTruthy3 } from "@dxos/util";
@@ -4285,11 +4333,6 @@ import { markdownLanguage } from "@codemirror/lang-markdown";
4285
4333
  import { HighlightStyle as HighlightStyle2 } from "@codemirror/language";
4286
4334
  import { Tag, styleTags, tags } from "@lezer/highlight";
4287
4335
  import { Table } from "@lezer/markdown";
4288
- var styles4 = {
4289
- code: "font-mono no-underline! text-cm-code",
4290
- codeMark: "font-mono text-cm-code-mark",
4291
- mark: "opacity-50"
4292
- };
4293
4336
  var markdownTags = {
4294
4337
  Blockquote: Tag.define(),
4295
4338
  CodeMark: Tag.define(),
@@ -4371,7 +4414,7 @@ var markdownHighlightStyle = (_options = {}) => {
4371
4414
  markdownTags.LinkReference,
4372
4415
  markdownTags.ListMark
4373
4416
  ],
4374
- class: styles4.mark
4417
+ class: markdownTheme.mark
4375
4418
  },
4376
4419
  // Markdown marks.
4377
4420
  {
@@ -4382,7 +4425,7 @@ var markdownHighlightStyle = (_options = {}) => {
4382
4425
  markdownTags.QuoteMark,
4383
4426
  markdownTags.EmphasisMark
4384
4427
  ],
4385
- class: styles4.mark
4428
+ class: markdownTheme.mark
4386
4429
  },
4387
4430
  // E.g., code block language (after ```).
4388
4431
  {
@@ -4391,7 +4434,7 @@ var markdownHighlightStyle = (_options = {}) => {
4391
4434
  tags.function(tags.variableName),
4392
4435
  tags.labelName
4393
4436
  ],
4394
- class: styles4.codeMark
4437
+ class: markdownTheme.codeMark
4395
4438
  },
4396
4439
  // Fonts.
4397
4440
  {
@@ -4457,7 +4500,7 @@ var markdownHighlightStyle = (_options = {}) => {
4457
4500
  markdownTags.CodeText,
4458
4501
  markdownTags.InlineCode
4459
4502
  ],
4460
- class: styles4.code
4503
+ class: markdownTheme.code
4461
4504
  },
4462
4505
  {
4463
4506
  tag: [
@@ -4487,15 +4530,23 @@ var createMarkdownExtensions = (options = {}) => {
4487
4530
  // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
4488
4531
  base: markdownLanguage2,
4489
4532
  // Languages for syntax highlighting fenced code blocks.
4533
+ // Caller-supplied languages are checked first so they can override defaults.
4490
4534
  defaultCodeLanguage: jsonLanguage,
4491
- codeLanguages: languages,
4535
+ codeLanguages: [
4536
+ ...options.codeLanguages ?? [],
4537
+ ...languages
4538
+ ],
4492
4539
  // Don't complete HTML tags.
4493
4540
  completeHTMLTags: false,
4494
4541
  // Parser extensions.
4495
4542
  extensions: [
4496
4543
  // GFM provided by default.
4497
4544
  markdownTagsExtensions,
4498
- ...options.extensions ?? defaultExtensions()
4545
+ ...options.extensions ?? defaultExtensions(),
4546
+ // Disable folding for fenced code blocks by overriding foldNodeProp.
4547
+ // Note: returning null from foldService does not prevent syntaxFolding fallback,
4548
+ // so we must override the node prop directly on the FencedCode node type.
4549
+ noFencedCodeFolding
4499
4550
  ]
4500
4551
  }),
4501
4552
  // Custom styles.
@@ -4510,18 +4561,13 @@ var createMarkdownExtensions = (options = {}) => {
4510
4561
  ].filter(isTruthy3))
4511
4562
  ];
4512
4563
  };
4513
- var xmlLanguageDesc = LanguageDescription.of({
4514
- name: "xml",
4515
- alias: [
4516
- "html",
4517
- "xhtml"
4518
- ],
4519
- extensions: [
4520
- "xml",
4521
- "xhtml"
4522
- ],
4523
- load: async () => xml()
4524
- });
4564
+ var noFencedCodeFolding = {
4565
+ props: [
4566
+ foldNodeProp.add({
4567
+ FencedCode: () => null
4568
+ })
4569
+ ]
4570
+ };
4525
4571
  var defaultExtensions = () => [
4526
4572
  noSetExtHeading,
4527
4573
  noHtml
@@ -4541,19 +4587,19 @@ var debugTree = (cb) => StateField6.define({
4541
4587
  update: (value, tr) => cb(convertTreeToJson(tr.state))
4542
4588
  });
4543
4589
  var convertTreeToJson = (state) => {
4544
- const treeToJson = (cursor2) => {
4590
+ const treeToJson = (cursor) => {
4545
4591
  const node = {
4546
- type: cursor2.type.name,
4547
- from: cursor2.from,
4548
- to: cursor2.to,
4549
- text: state.doc.slice(cursor2.from, cursor2.to).toString(),
4592
+ type: cursor.type.name,
4593
+ from: cursor.from,
4594
+ to: cursor.to,
4595
+ text: state.doc.slice(cursor.from, cursor.to).toString(),
4550
4596
  children: []
4551
4597
  };
4552
- if (cursor2.firstChild()) {
4598
+ if (cursor.firstChild()) {
4553
4599
  do {
4554
- node.children.push(treeToJson(cursor2));
4555
- } while (cursor2.nextSibling());
4556
- cursor2.parent();
4600
+ node.children.push(treeToJson(cursor));
4601
+ } while (cursor.nextSibling());
4602
+ cursor.parent();
4557
4603
  }
4558
4604
  return node;
4559
4605
  };
@@ -4562,17 +4608,16 @@ var convertTreeToJson = (state) => {
4562
4608
 
4563
4609
  // src/extensions/markdown/decorate.ts
4564
4610
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4565
- import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect6 } from "@codemirror/state";
4566
- import { Decoration as Decoration11, EditorView as EditorView23, ViewPlugin as ViewPlugin14, WidgetType as WidgetType7 } from "@codemirror/view";
4611
+ import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect7 } from "@codemirror/state";
4612
+ import { Decoration as Decoration11, EditorView as EditorView24, ViewPlugin as ViewPlugin15, WidgetType as WidgetType7 } from "@codemirror/view";
4567
4613
  import { invariant as invariant4 } from "@dxos/invariant";
4568
- import { mx as mx6 } from "@dxos/ui-theme";
4569
4614
 
4570
4615
  // src/extensions/markdown/changes.ts
4571
4616
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4572
4617
  import { Transaction as Transaction4 } from "@codemirror/state";
4573
- import { ViewPlugin as ViewPlugin13 } from "@codemirror/view";
4618
+ import { ViewPlugin as ViewPlugin14 } from "@codemirror/view";
4574
4619
  var adjustChanges = () => {
4575
- return ViewPlugin13.fromClass(class {
4620
+ return ViewPlugin14.fromClass(class {
4576
4621
  update(update2) {
4577
4622
  const tree = syntaxTree4(update2.state);
4578
4623
  const adjustments = [];
@@ -4714,7 +4759,7 @@ var getValidUrl = (str) => {
4714
4759
  // src/extensions/markdown/image.ts
4715
4760
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4716
4761
  import { StateField as StateField7 } from "@codemirror/state";
4717
- import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType5 } from "@codemirror/view";
4762
+ import { Decoration as Decoration9, EditorView as EditorView21, WidgetType as WidgetType5 } from "@codemirror/view";
4718
4763
  var image = (_options = {}) => {
4719
4764
  return [
4720
4765
  StateField7.define({
@@ -4725,10 +4770,10 @@ var image = (_options = {}) => {
4725
4770
  if (!tr.docChanged && !tr.selection) {
4726
4771
  return value;
4727
4772
  }
4728
- const cursor2 = tr.state.selection.main.head;
4773
+ const cursor = tr.state.selection.main.head;
4729
4774
  const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
4730
- let from = Math.min(cursor2, oldCursor);
4731
- let to = Math.max(cursor2, oldCursor);
4775
+ let from = Math.min(cursor, oldCursor);
4776
+ let to = Math.max(cursor, oldCursor);
4732
4777
  tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
4733
4778
  from = Math.min(from, fromB);
4734
4779
  to = Math.max(to, toB);
@@ -4742,19 +4787,19 @@ var image = (_options = {}) => {
4742
4787
  add: buildDecorations(tr.state, from, to)
4743
4788
  });
4744
4789
  },
4745
- provide: (field) => EditorView20.decorations.from(field)
4790
+ provide: (field) => EditorView21.decorations.from(field)
4746
4791
  })
4747
4792
  ];
4748
4793
  };
4749
4794
  var buildDecorations = (state, from, to) => {
4750
4795
  const decorations2 = [];
4751
- const cursor2 = state.selection.main.head;
4796
+ const cursor = state.selection.main.head;
4752
4797
  syntaxTree5(state).iterate({
4753
4798
  enter: (node) => {
4754
4799
  if (node.name === "Image") {
4755
4800
  const urlNode = node.node.getChild("URL");
4756
4801
  if (urlNode) {
4757
- const hide2 = state.readOnly || cursor2 < node.from || cursor2 > node.to || !state.field(focusField);
4802
+ const hide2 = state.readOnly || cursor < node.from || cursor > node.to || !state.field(focusField);
4758
4803
  const url = state.sliceDoc(urlNode.from, urlNode.to);
4759
4804
  if (url.match(/^https?:\/\//) === null && url.match(/^file?:\/\//) === null) {
4760
4805
  return;
@@ -4802,10 +4847,10 @@ var ImageWidget = class extends WidgetType5 {
4802
4847
  };
4803
4848
 
4804
4849
  // src/extensions/markdown/styles.ts
4805
- import { EditorView as EditorView21 } from "@codemirror/view";
4850
+ import { EditorView as EditorView22 } from "@codemirror/view";
4806
4851
  var bulletListIndentationWidth = 24;
4807
4852
  var orderedListIndentationWidth = 36;
4808
- var formattingStyles = EditorView21.theme({
4853
+ var formattingStyles = EditorView22.theme({
4809
4854
  /**
4810
4855
  * Horizontal rule.
4811
4856
  */
@@ -4840,13 +4885,38 @@ var formattingStyles = EditorView21.theme({
4840
4885
  background: "var(--color-cm-codeblock)",
4841
4886
  borderLeft: "2px solid var(--color-cm-separator)",
4842
4887
  paddingLeft: "1rem",
4843
- margin: "0"
4888
+ margin: 0
4844
4889
  },
4845
4890
  /**
4846
4891
  * Code and codeblocks.
4847
4892
  */
4893
+ "& code": {
4894
+ fontFamily: fontMono,
4895
+ color: "var(--color-cm-code)",
4896
+ whiteSpace: "nowrap"
4897
+ },
4848
4898
  "& .cm-code": {
4849
- fontFamily: fontMono
4899
+ fontFamily: fontMono,
4900
+ color: "var(--color-cm-code)"
4901
+ },
4902
+ // Inline code spans (triggered by backticks) use `cm-code-inline` + `font-mono`.
4903
+ // Different monospace font metrics can slightly overflow the fixed CodeMirror line box,
4904
+ // so constrain them to the target 24px height.
4905
+ "& .cm-code-inline": {
4906
+ fontFamily: fontMono,
4907
+ height: "24px",
4908
+ // display: 'inline-flex',
4909
+ alignItems: "center",
4910
+ overflow: "hidden",
4911
+ whiteSpace: "nowrap",
4912
+ color: "var(--color-cm-code-inline)"
4913
+ },
4914
+ "& .cm-code-mark": {
4915
+ fontFamily: fontMono,
4916
+ height: "24px",
4917
+ display: "inline-flex",
4918
+ alignItems: "center",
4919
+ overflow: "hidden"
4850
4920
  },
4851
4921
  "& .cm-codeblock-line": {
4852
4922
  background: "var(--color-cm-codeblock)",
@@ -4878,16 +4948,24 @@ var formattingStyles = EditorView21.theme({
4878
4948
  */
4879
4949
  ".cm-table *": {
4880
4950
  fontFamily: fontMono,
4951
+ lineHeight: 1.5,
4881
4952
  textDecoration: "none !important"
4882
4953
  },
4883
4954
  ".cm-table-head": {
4884
4955
  padding: "2px 16px 2px 0px",
4956
+ overflowWrap: "break-word",
4957
+ whiteSpace: "pre-wrap",
4958
+ wordBreak: "keep-all",
4885
4959
  textAlign: "left",
4886
- borderBottom: "1px solid var(--color-cm-separator)",
4887
- color: "var(--color-subdued)"
4960
+ color: "var(--color-subdued)",
4961
+ borderBottom: "1px solid var(--color-cm-separator)"
4888
4962
  },
4889
4963
  ".cm-table-cell": {
4890
- padding: "2px 16px 2px 0px"
4964
+ padding: "2px 16px 2px 0px",
4965
+ overflowWrap: "break-word",
4966
+ whiteSpace: "pre-wrap",
4967
+ wordBreak: "keep-all",
4968
+ verticalAlign: "top"
4891
4969
  },
4892
4970
  /**
4893
4971
  * Image.
@@ -4903,12 +4981,12 @@ var formattingStyles = EditorView21.theme({
4903
4981
  },
4904
4982
  ".cm-image-with-loader": {
4905
4983
  display: "block",
4906
- opacity: "0",
4984
+ opacity: 0,
4907
4985
  transitionDuration: "350ms",
4908
4986
  transitionProperty: "opacity"
4909
4987
  },
4910
4988
  ".cm-image-with-loader.cm-loaded-image": {
4911
- opacity: "1"
4989
+ opacity: 1
4912
4990
  },
4913
4991
  ".cm-image-wrapper": {
4914
4992
  "grid-template-columns": "1fr",
@@ -4927,17 +5005,17 @@ var formattingStyles = EditorView21.theme({
4927
5005
  // src/extensions/markdown/table.ts
4928
5006
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4929
5007
  import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4930
- import { Decoration as Decoration10, EditorView as EditorView22, WidgetType as WidgetType6 } from "@codemirror/view";
5008
+ import { Decoration as Decoration10, EditorView as EditorView23, WidgetType as WidgetType6 } from "@codemirror/view";
4931
5009
  var table = (options = {}) => {
4932
5010
  return StateField8.define({
4933
5011
  create: (state) => update(state, options),
4934
5012
  update: (_, tr) => update(tr.state, options),
4935
- provide: (field) => EditorView22.decorations.from(field)
5013
+ provide: (field) => EditorView23.decorations.from(field)
4936
5014
  });
4937
5015
  };
4938
5016
  var update = (state, _options) => {
4939
5017
  const builder = new RangeSetBuilder4();
4940
- const cursor2 = state.selection.main.head;
5018
+ const cursor = state.selection.main.head;
4941
5019
  const tables = [];
4942
5020
  const getTable = () => tables[tables.length - 1];
4943
5021
  const getRow = () => {
@@ -4975,7 +5053,7 @@ var update = (state, _options) => {
4975
5053
  }
4976
5054
  });
4977
5055
  tables.forEach((table2) => {
4978
- const replace = state.readOnly || cursor2 < table2.from || cursor2 > table2.to;
5056
+ const replace = state.readOnly || cursor < table2.from || cursor > table2.to;
4979
5057
  if (replace) {
4980
5058
  builder.add(table2.from, table2.to, Decoration10.replace({
4981
5059
  block: true,
@@ -4989,6 +5067,26 @@ var update = (state, _options) => {
4989
5067
  });
4990
5068
  return builder.finish();
4991
5069
  };
5070
+ var renderCellContent = (el, text) => {
5071
+ const parts = text.split(/(`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_)/);
5072
+ for (const part of parts) {
5073
+ if (part.length > 2 && part.startsWith("`") && part.endsWith("`")) {
5074
+ const code = document.createElement("code");
5075
+ code.textContent = part.slice(1, -1);
5076
+ el.appendChild(code);
5077
+ } else if (part.startsWith("**") && part.endsWith("**") || part.startsWith("__") && part.endsWith("__")) {
5078
+ const strong = document.createElement("strong");
5079
+ strong.textContent = part.slice(2, -2);
5080
+ el.appendChild(strong);
5081
+ } else if (part.startsWith("*") && part.endsWith("*") || part.startsWith("_") && part.endsWith("_")) {
5082
+ const em = document.createElement("em");
5083
+ em.textContent = part.slice(1, -1);
5084
+ el.appendChild(em);
5085
+ } else {
5086
+ el.appendChild(document.createTextNode(part));
5087
+ }
5088
+ }
5089
+ };
4992
5090
  var TableWidget = class extends WidgetType6 {
4993
5091
  _table;
4994
5092
  constructor(_table) {
@@ -5008,7 +5106,7 @@ var TableWidget = class extends WidgetType6 {
5008
5106
  this._table.header?.forEach((cell) => {
5009
5107
  const th = document.createElement("th");
5010
5108
  th.setAttribute("class", "cm-table-head");
5011
- tr.appendChild(th).textContent = cell;
5109
+ renderCellContent(tr.appendChild(th), cell);
5012
5110
  });
5013
5111
  const body = table2.appendChild(document.createElement("tbody"));
5014
5112
  this._table.rows?.forEach((row) => {
@@ -5016,7 +5114,7 @@ var TableWidget = class extends WidgetType6 {
5016
5114
  row.forEach((cell) => {
5017
5115
  const td = document.createElement("td");
5018
5116
  td.setAttribute("class", "cm-table-cell");
5019
- tr2.appendChild(td).textContent = cell;
5117
+ renderCellContent(tr2.appendChild(td), cell);
5020
5118
  });
5021
5119
  });
5022
5120
  return div;
@@ -5123,10 +5221,10 @@ var fencedCodeLine = Decoration11.line({
5123
5221
  class: "cm-code cm-codeblock-line"
5124
5222
  });
5125
5223
  var fencedCodeLineFirst = Decoration11.line({
5126
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-start")
5224
+ class: "cm-code cm-codeblock-line cm-codeblock-start"
5127
5225
  });
5128
5226
  var fencedCodeLineLast = Decoration11.line({
5129
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-end")
5227
+ class: "cm-code cm-codeblock-line cm-codeblock-end"
5130
5228
  });
5131
5229
  var commentBlockLine = fencedCodeLine;
5132
5230
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -5158,15 +5256,7 @@ var buildDecorations2 = (view, options, focus2) => {
5158
5256
  const { state } = view;
5159
5257
  const headerLevels = [];
5160
5258
  const getHeaderLevels = (node, level) => {
5161
- invariant4(level > 0, void 0, {
5162
- F: __dxlog_file12,
5163
- L: 179,
5164
- S: void 0,
5165
- A: [
5166
- "level > 0",
5167
- ""
5168
- ]
5169
- });
5259
+ invariant4(level > 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 160, S: void 0, A: ["level > 0", ""] });
5170
5260
  if (level > headerLevels.length) {
5171
5261
  const len = headerLevels.length;
5172
5262
  headerLevels.length = level;
@@ -5197,15 +5287,7 @@ var buildDecorations2 = (view, options, focus2) => {
5197
5287
  listLevels.pop();
5198
5288
  };
5199
5289
  const getCurrentListLevel = () => {
5200
- invariant4(listLevels.length, void 0, {
5201
- F: __dxlog_file12,
5202
- L: 201,
5203
- S: void 0,
5204
- A: [
5205
- "listLevels.length",
5206
- ""
5207
- ]
5208
- });
5290
+ invariant4(listLevels.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 192, S: void 0, A: ["listLevels.length", ""] });
5209
5291
  return listLevels[listLevels.length - 1];
5210
5292
  };
5211
5293
  const enterNode = (node) => {
@@ -5243,7 +5325,7 @@ var buildDecorations2 = (view, options, focus2) => {
5243
5325
  deco: hide
5244
5326
  });
5245
5327
  } else {
5246
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5328
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + "). ";
5247
5329
  if (num.length) {
5248
5330
  atomicDecoRanges.push({
5249
5331
  from: mark.from,
@@ -5426,11 +5508,11 @@ var buildDecorations2 = (view, options, focus2) => {
5426
5508
  }
5427
5509
  decoRanges.push({
5428
5510
  from: marks[0].to,
5429
- to: marks[1].from,
5511
+ to: !editing && options.renderLinkButton ? node.to : marks[1].from,
5430
5512
  deco: Decoration11.mark({
5431
5513
  tagName: "a",
5432
5514
  attributes: {
5433
- class: "cm-link",
5515
+ class: options.renderLinkButton ? "cm-link cm-link-with-button" : "cm-link",
5434
5516
  href: url,
5435
5517
  rel: "noreferrer",
5436
5518
  target: "_blank"
@@ -5508,18 +5590,21 @@ var buildDecorations2 = (view, options, focus2) => {
5508
5590
  deco.add(from, to, d);
5509
5591
  }
5510
5592
  const atomicDeco = new RangeSetBuilder5();
5511
- for (const { from, to, deco: d } of atomicDecoRanges) {
5512
- atomicDeco.add(from, to, d);
5593
+ for (const { from, to, deco: deco2 } of atomicDecoRanges) {
5594
+ if (from < to && state.doc.lineAt(from).number !== state.doc.lineAt(to).number) {
5595
+ continue;
5596
+ }
5597
+ atomicDeco.add(from, to, deco2);
5513
5598
  }
5514
5599
  return {
5515
5600
  deco: deco.finish(),
5516
5601
  atomicDeco: atomicDeco.finish()
5517
5602
  };
5518
5603
  };
5519
- var forceUpdate = StateEffect6.define();
5604
+ var forceUpdate = StateEffect7.define();
5520
5605
  var decorateMarkdown = (options = {}) => {
5521
5606
  return [
5522
- ViewPlugin14.fromClass(class {
5607
+ ViewPlugin15.fromClass(class {
5523
5608
  deco;
5524
5609
  atomicDeco;
5525
5610
  pendingUpdate;
@@ -5554,9 +5639,9 @@ var decorateMarkdown = (options = {}) => {
5554
5639
  }
5555
5640
  }, {
5556
5641
  provide: (plugin) => [
5557
- Prec4.low(EditorView23.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5558
- EditorView23.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5559
- EditorView23.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5642
+ Prec4.low(EditorView24.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5643
+ EditorView24.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5644
+ EditorView24.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5560
5645
  ]
5561
5646
  }),
5562
5647
  image(),
@@ -5624,12 +5709,7 @@ var mention = ({ debug, onSearch }) => {
5624
5709
  (context) => {
5625
5710
  log9.info("completion context", {
5626
5711
  context
5627
- }, {
5628
- F: __dxlog_file13,
5629
- L: 27,
5630
- S: void 0,
5631
- C: (f, a) => f(...a)
5632
- });
5712
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 18, S: void 0 });
5633
5713
  const match = context.matchBefore(/@(\w+)?/);
5634
5714
  if (!match || match.from === match.to && !context.explicit) {
5635
5715
  return null;
@@ -5646,8 +5726,8 @@ var mention = ({ debug, onSearch }) => {
5646
5726
  };
5647
5727
 
5648
5728
  // src/extensions/modal.ts
5649
- import { StateEffect as StateEffect7, StateField as StateField9 } from "@codemirror/state";
5650
- var modalStateEffect = StateEffect7.define();
5729
+ import { StateEffect as StateEffect8, StateField as StateField9 } from "@codemirror/state";
5730
+ var modalStateEffect = StateEffect8.define();
5651
5731
  var modalStateField = StateField9.define({
5652
5732
  create: () => false,
5653
5733
  update: (value, tr) => {
@@ -5862,15 +5942,7 @@ var outlinerTree = (_options = {}) => {
5862
5942
  break;
5863
5943
  }
5864
5944
  case "BulletList": {
5865
- invariant5(current, void 0, {
5866
- F: __dxlog_file14,
5867
- L: 219,
5868
- S: void 0,
5869
- A: [
5870
- "current",
5871
- ""
5872
- ]
5873
- });
5945
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 169, S: void 0, A: ["current", ""] });
5874
5946
  parent = current;
5875
5947
  if (current) {
5876
5948
  current.lineRange.to = current.node.from;
@@ -5879,15 +5951,7 @@ var outlinerTree = (_options = {}) => {
5879
5951
  break;
5880
5952
  }
5881
5953
  case "ListItem": {
5882
- invariant5(parent, void 0, {
5883
- F: __dxlog_file14,
5884
- L: 228,
5885
- S: void 0,
5886
- A: [
5887
- "parent",
5888
- ""
5889
- ]
5890
- });
5954
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 179, S: void 0, A: ["parent", ""] });
5891
5955
  const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
5892
5956
  const docRange = {
5893
5957
  from: state.doc.lineAt(node.from).from,
@@ -5921,42 +5985,18 @@ var outlinerTree = (_options = {}) => {
5921
5985
  break;
5922
5986
  }
5923
5987
  case "ListMark": {
5924
- invariant5(current, void 0, {
5925
- F: __dxlog_file14,
5926
- L: 272,
5927
- S: void 0,
5928
- A: [
5929
- "current",
5930
- ""
5931
- ]
5932
- });
5988
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 219, S: void 0, A: ["current", ""] });
5933
5989
  current.type = "bullet";
5934
5990
  current.contentRange.from = node.from + "- ".length;
5935
5991
  break;
5936
5992
  }
5937
5993
  case "Task": {
5938
- invariant5(current, void 0, {
5939
- F: __dxlog_file14,
5940
- L: 278,
5941
- S: void 0,
5942
- A: [
5943
- "current",
5944
- ""
5945
- ]
5946
- });
5994
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 226, S: void 0, A: ["current", ""] });
5947
5995
  current.type = "task";
5948
5996
  break;
5949
5997
  }
5950
5998
  case "TaskMarker": {
5951
- invariant5(current, void 0, {
5952
- F: __dxlog_file14,
5953
- L: 283,
5954
- S: void 0,
5955
- A: [
5956
- "current",
5957
- ""
5958
- ]
5959
- });
5999
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 232, S: void 0, A: ["current", ""] });
5960
6000
  current.contentRange.from = node.from + "[ ] ".length;
5961
6001
  break;
5962
6002
  }
@@ -5964,29 +6004,13 @@ var outlinerTree = (_options = {}) => {
5964
6004
  },
5965
6005
  leave: (node) => {
5966
6006
  if (node.name === "BulletList") {
5967
- invariant5(parent, void 0, {
5968
- F: __dxlog_file14,
5969
- L: 291,
5970
- S: void 0,
5971
- A: [
5972
- "parent",
5973
- ""
5974
- ]
5975
- });
6007
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 240, S: void 0, A: ["parent", ""] });
5976
6008
  prevSiblings[level--] = void 0;
5977
6009
  parent = parent.parent;
5978
6010
  }
5979
6011
  }
5980
6012
  });
5981
- invariant5(tree, void 0, {
5982
- F: __dxlog_file14,
5983
- L: 298,
5984
- S: void 0,
5985
- A: [
5986
- "tree",
5987
- ""
5988
- ]
5989
- });
6013
+ invariant5(tree, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 246, S: void 0, A: ["tree", ""] });
5990
6014
  return tree;
5991
6015
  };
5992
6016
  return [
@@ -6271,17 +6295,17 @@ var commands = () => keymap11.of([
6271
6295
 
6272
6296
  // src/extensions/outliner/outliner.ts
6273
6297
  import { Prec as Prec5 } from "@codemirror/state";
6274
- import { Decoration as Decoration12, EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6275
- import { mx as mx7 } from "@dxos/ui-theme";
6298
+ import { Decoration as Decoration12, EditorView as EditorView26, ViewPlugin as ViewPlugin18 } from "@codemirror/view";
6299
+ import { mx as mx6 } from "@dxos/ui-theme";
6276
6300
 
6277
6301
  // src/extensions/outliner/editor.ts
6278
6302
  import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
6279
- import { ViewPlugin as ViewPlugin15 } from "@codemirror/view";
6303
+ import { ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6280
6304
  import { log as log10 } from "@dxos/log";
6281
6305
  var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6282
6306
  var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
6283
6307
  var initialize = () => {
6284
- return ViewPlugin15.fromClass(class {
6308
+ return ViewPlugin16.fromClass(class {
6285
6309
  constructor(view) {
6286
6310
  const first = view.state.doc.lineAt(0);
6287
6311
  const text = view.state.sliceDoc(first.from, first.to);
@@ -6428,35 +6452,20 @@ var editor = () => [
6428
6452
  text: insert.toString(),
6429
6453
  length: insert.length
6430
6454
  }
6431
- }, {
6432
- F: __dxlog_file15,
6433
- L: 164,
6434
- S: void 0,
6435
- C: (f, a) => f(...a)
6436
- });
6455
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 174, S: void 0 });
6437
6456
  }
6438
6457
  });
6439
6458
  if (changes.length > 0) {
6440
6459
  log10("modified,", {
6441
6460
  changes
6442
- }, {
6443
- F: __dxlog_file15,
6444
- L: 175,
6445
- S: void 0,
6446
- C: (f, a) => f(...a)
6447
- });
6461
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 196, S: void 0 });
6448
6462
  return [
6449
6463
  {
6450
6464
  changes
6451
6465
  }
6452
6466
  ];
6453
6467
  } else if (cancel) {
6454
- log10("cancel", void 0, {
6455
- F: __dxlog_file15,
6456
- L: 178,
6457
- S: void 0,
6458
- C: (f, a) => f(...a)
6459
- });
6468
+ log10("cancel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 205, S: void 0 });
6460
6469
  return [];
6461
6470
  }
6462
6471
  return tr;
@@ -6464,10 +6473,10 @@ var editor = () => [
6464
6473
  ];
6465
6474
 
6466
6475
  // src/extensions/outliner/menu.ts
6467
- import { EditorView as EditorView24, ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6476
+ import { EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6468
6477
  import { addEventListener as addEventListener2 } from "@dxos/async";
6469
6478
  var menu = (options = {}) => [
6470
- ViewPlugin16.fromClass(class {
6479
+ ViewPlugin17.fromClass(class {
6471
6480
  view;
6472
6481
  tag;
6473
6482
  rafId;
@@ -6529,7 +6538,7 @@ var menu = (options = {}) => [
6529
6538
  this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
6530
6539
  }
6531
6540
  }),
6532
- EditorView24.theme({
6541
+ EditorView25.theme({
6533
6542
  ".cm-popover-trigger": {
6534
6543
  position: "fixed",
6535
6544
  padding: "0",
@@ -6565,12 +6574,12 @@ var outliner = (_options = {}) => [
6565
6574
  listPaddingLeft: 8
6566
6575
  }),
6567
6576
  // Researve space for menu.
6568
- EditorView25.contentAttributes.of({
6577
+ EditorView26.contentAttributes.of({
6569
6578
  class: "w-full !mr-[3rem]"
6570
6579
  })
6571
6580
  ];
6572
6581
  var decorations = () => [
6573
- ViewPlugin17.fromClass(class {
6582
+ ViewPlugin18.fromClass(class {
6574
6583
  decorations = Decoration12.none;
6575
6584
  constructor(view) {
6576
6585
  this.updateDecorations(view.state, view);
@@ -6595,7 +6604,7 @@ var decorations = () => [
6595
6604
  const lineTo = doc.lineAt(item.contentRange.to);
6596
6605
  const isSelected = selection.includes(item.index) || item === current;
6597
6606
  decorations2.push(Decoration12.line({
6598
- class: mx7("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6607
+ class: mx6("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6599
6608
  }).range(line.from, line.from));
6600
6609
  }
6601
6610
  }
@@ -6605,7 +6614,7 @@ var decorations = () => [
6605
6614
  decorations: (v) => v.decorations
6606
6615
  }),
6607
6616
  // Theme.
6608
- EditorView25.theme(Object.assign({
6617
+ EditorView26.theme(Object.assign({
6609
6618
  ".cm-list-item": {
6610
6619
  borderLeftWidth: "1px",
6611
6620
  borderRightWidth: "1px",
@@ -6640,28 +6649,57 @@ var decorations = () => [
6640
6649
 
6641
6650
  // src/extensions/preview/preview.ts
6642
6651
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
6643
- import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField11 } from "@codemirror/state";
6644
- import { Decoration as Decoration13, EditorView as EditorView26, WidgetType as WidgetType8 } from "@codemirror/view";
6652
+ import { RangeSetBuilder as RangeSetBuilder6, StateEffect as StateEffect9, StateField as StateField11 } from "@codemirror/state";
6653
+ import { Decoration as Decoration13, EditorView as EditorView27, ViewPlugin as ViewPlugin19, WidgetType as WidgetType8 } from "@codemirror/view";
6654
+ import { DXN, Entity } from "@dxos/echo";
6655
+ var labelResolvedEffect = StateEffect9.define();
6645
6656
  var preview = (options = {}) => {
6657
+ const viewRef = {
6658
+ current: void 0
6659
+ };
6646
6660
  return [
6647
6661
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
6648
6662
  // "Block decorations may not be specified via plugins".
6649
6663
  StateField11.define({
6650
- create: (state) => buildDecorations3(state, options),
6664
+ create: (state) => buildDecorations3(state, options, viewRef),
6651
6665
  update: (decorations2, tr) => {
6652
- if (tr.docChanged) {
6653
- return buildDecorations3(tr.state, options);
6666
+ if (tr.docChanged || tr.effects.some((effect) => effect.is(labelResolvedEffect))) {
6667
+ return buildDecorations3(tr.state, options, viewRef);
6654
6668
  }
6655
6669
  return decorations2.map(tr.changes);
6656
6670
  },
6657
6671
  provide: (field) => [
6658
- EditorView26.decorations.from(field),
6659
- EditorView26.atomicRanges.of((view) => view.state.field(field))
6672
+ EditorView27.decorations.from(field),
6673
+ EditorView27.atomicRanges.of((view) => view.state.field(field))
6660
6674
  ]
6675
+ }),
6676
+ ViewPlugin19.define((view) => {
6677
+ viewRef.current = view;
6678
+ return {
6679
+ destroy() {
6680
+ viewRef.current = void 0;
6681
+ }
6682
+ };
6661
6683
  })
6662
6684
  ];
6663
6685
  };
6664
- var buildDecorations3 = (state, options) => {
6686
+ var resolveLabel = (db, dxnStr, viewRef) => {
6687
+ const dxn = DXN.tryParse(dxnStr);
6688
+ if (!dxn) {
6689
+ return;
6690
+ }
6691
+ const ref = db.makeRef(dxn);
6692
+ const target = ref.target;
6693
+ if (target) {
6694
+ return Entity.getLabel(target);
6695
+ }
6696
+ void ref.tryLoad().then(() => {
6697
+ viewRef.current?.dispatch({
6698
+ effects: labelResolvedEffect.of(void 0)
6699
+ });
6700
+ });
6701
+ };
6702
+ var buildDecorations3 = (state, options, viewRef) => {
6665
6703
  const builder = new RangeSetBuilder6();
6666
6704
  syntaxTree10(state).iterate({
6667
6705
  enter: (node) => {
@@ -6673,8 +6711,13 @@ var buildDecorations3 = (state, options) => {
6673
6711
  case "Link": {
6674
6712
  const link = getLinkRef(state, node.node);
6675
6713
  if (link) {
6714
+ const resolved = options.db ? resolveLabel(options.db, link.dxn, viewRef) : void 0;
6715
+ const displayLink = resolved ? {
6716
+ ...link,
6717
+ label: resolved
6718
+ } : link;
6676
6719
  builder.add(node.from, node.to, Decoration13.replace({
6677
- widget: new PreviewInlineWidget(options, link),
6720
+ widget: new PreviewInlineWidget(options, displayLink),
6678
6721
  side: 1
6679
6722
  }));
6680
6723
  }
@@ -6766,7 +6809,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6766
6809
  };
6767
6810
 
6768
6811
  // src/extensions/replacer.ts
6769
- import { EditorView as EditorView27 } from "@codemirror/view";
6812
+ import { EditorView as EditorView28 } from "@codemirror/view";
6770
6813
  var defaultReplacements = [
6771
6814
  {
6772
6815
  input: "--",
@@ -6829,7 +6872,7 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6829
6872
  const sortedReplacements = [
6830
6873
  ...replacements
6831
6874
  ].sort((a, b) => b.input.length - a.input.length);
6832
- return EditorView27.inputHandler.of((view, from, to, insert) => {
6875
+ return EditorView28.inputHandler.of((view, from, to, insert) => {
6833
6876
  if (insert.length !== 1) {
6834
6877
  return false;
6835
6878
  }
@@ -6863,12 +6906,69 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6863
6906
  });
6864
6907
  };
6865
6908
 
6909
+ // src/extensions/snippets.ts
6910
+ import { keymap as keymap12 } from "@codemirror/view";
6911
+ var defaultItems = [
6912
+ "hello world!",
6913
+ "this is a test.",
6914
+ "this is [DXOS](https://dxos.org)"
6915
+ ];
6916
+ var snippets2 = ({ delay = 75, items = defaultItems } = {}) => {
6917
+ let timer;
6918
+ let index = 0;
6919
+ return [
6920
+ keymap12.of([
6921
+ {
6922
+ // Reset.
6923
+ key: "alt-meta-'",
6924
+ run: () => {
6925
+ clearTimeout(timer);
6926
+ index = 0;
6927
+ return true;
6928
+ }
6929
+ },
6930
+ {
6931
+ // Next snippet.
6932
+ // TODO(burdon): Press 1-9 to select snippet?
6933
+ key: "Shift-Meta-'",
6934
+ run: (view) => {
6935
+ clearTimeout(timer);
6936
+ const text = items[index++];
6937
+ if (index === items?.length) {
6938
+ index = 0;
6939
+ }
6940
+ let offset = 0;
6941
+ const insert = (delayMs = 0) => {
6942
+ timer = setTimeout(() => {
6943
+ const pos = view.state.selection.main.head;
6944
+ view.dispatch({
6945
+ changes: {
6946
+ from: pos,
6947
+ insert: text[offset++]
6948
+ },
6949
+ selection: {
6950
+ anchor: pos + 1
6951
+ }
6952
+ });
6953
+ if (offset < text.length) {
6954
+ insert(Math.random() * delay * (text[offset] === " " ? 2 : 1));
6955
+ }
6956
+ }, delayMs);
6957
+ };
6958
+ insert();
6959
+ return true;
6960
+ }
6961
+ }
6962
+ ])
6963
+ ];
6964
+ };
6965
+
6866
6966
  // src/extensions/submit.ts
6867
6967
  import { Prec as Prec6 } from "@codemirror/state";
6868
- import { keymap as keymap12 } from "@codemirror/view";
6968
+ import { keymap as keymap13 } from "@codemirror/view";
6869
6969
  var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6870
6970
  return [
6871
- Prec6.highest(keymap12.of([
6971
+ Prec6.highest(keymap13.of([
6872
6972
  {
6873
6973
  key: "Enter",
6874
6974
  preventDefault: true,
@@ -6913,6 +7013,7 @@ var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6913
7013
  // src/extensions/tags/extended-markdown.ts
6914
7014
  import { xmlLanguage } from "@codemirror/lang-xml";
6915
7015
  import { parseMixed } from "@lezer/common";
7016
+ var escapeRegExpSource = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6916
7017
  var extendedMarkdown = ({ registry } = {}) => {
6917
7018
  return [
6918
7019
  createMarkdownExtensions({
@@ -6924,13 +7025,65 @@ var extendedMarkdown = ({ registry } = {}) => {
6924
7025
  {
6925
7026
  name: "SetextHeading",
6926
7027
  parse: () => false
6927
- }
7028
+ },
7029
+ // Custom XML block parser that keeps registered tags as a single HTMLBlock
7030
+ // even when their content contains blank lines.
7031
+ ...xmlBlockParsers(registry)
6928
7032
  ]
6929
7033
  }
6930
7034
  ]
6931
7035
  })
6932
7036
  ];
6933
7037
  };
7038
+ var xmlBlockParsers = (registry) => {
7039
+ const customTags = Object.keys(registry ?? {});
7040
+ if (customTags.length === 0) {
7041
+ return [];
7042
+ }
7043
+ const tagPattern = customTags.map(escapeRegExpSource).join("|");
7044
+ const selfClosePattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/>\\s*$`);
7045
+ const openPattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/?>`);
7046
+ return [
7047
+ {
7048
+ name: "XMLBlock",
7049
+ before: "HTMLBlock",
7050
+ parse: (cx, line) => {
7051
+ const match = openPattern.exec(line.text);
7052
+ if (!match) {
7053
+ return false;
7054
+ }
7055
+ if (selfClosePattern.test(line.text)) {
7056
+ const end2 = cx.lineStart + line.text.length;
7057
+ cx.addElement(cx.elt("HTMLBlock", cx.lineStart, end2));
7058
+ cx.nextLine();
7059
+ return true;
7060
+ }
7061
+ if (match[0].trimEnd().endsWith("/>")) {
7062
+ return false;
7063
+ }
7064
+ const tagName = match[1];
7065
+ const closeTag = `</${tagName}>`;
7066
+ const start = cx.lineStart;
7067
+ if (line.text.includes(closeTag)) {
7068
+ cx.addElement(cx.elt("HTMLBlock", start, start + line.text.length));
7069
+ cx.nextLine();
7070
+ return true;
7071
+ }
7072
+ let end = cx.lineStart + line.text.length;
7073
+ while (cx.nextLine()) {
7074
+ end = cx.lineStart + line.text.length;
7075
+ if (line.text.includes(closeTag)) {
7076
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7077
+ cx.nextLine();
7078
+ return true;
7079
+ }
7080
+ }
7081
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7082
+ return true;
7083
+ }
7084
+ }
7085
+ ];
7086
+ };
6934
7087
  var mixedParser = (registry) => {
6935
7088
  const customTags = Object.keys(registry ?? {});
6936
7089
  const tagPattern = new RegExp(`<(${customTags.join("|")})`);
@@ -6964,219 +7117,793 @@ var mixedParser = (registry) => {
6964
7117
  });
6965
7118
  };
6966
7119
 
6967
- // src/extensions/tags/streamer.ts
6968
- import { StateEffect as StateEffect8, StateField as StateField12 } from "@codemirror/state";
6969
- import { Decoration as Decoration14, EditorView as EditorView28, ViewPlugin as ViewPlugin18, WidgetType as WidgetType9 } from "@codemirror/view";
6970
- import { Domino as Domino3 } from "@dxos/ui";
6971
- import { isTruthy as isTruthy4 } from "@dxos/util";
6972
- var BLINK_RATE = 2e3;
6973
- var streamer = (options = {}) => {
6974
- return [
6975
- options.cursor && cursor(),
6976
- options.fadeIn && fadeIn(typeof options.fadeIn === "object" ? options.fadeIn : {})
6977
- ].filter(isTruthy4);
6978
- };
6979
- var cursor = () => {
6980
- const hideCursor = StateEffect8.define();
6981
- const showCursor = StateField12.define({
6982
- create: () => true,
6983
- update: (value, tr) => {
7120
+ // src/extensions/tags/fader.ts
7121
+ import { StateEffect as StateEffect10, StateField as StateField12 } from "@codemirror/state";
7122
+ import { Decoration as Decoration14, EditorView as EditorView29, ViewPlugin as ViewPlugin20 } from "@codemirror/view";
7123
+ var DEFAULT_REMOVAL_DELAY = 5e3;
7124
+ var DEFAULT_COALESCE_WINDOW = 100;
7125
+ var CLEANUP_INTERVAL = 1e3;
7126
+ var fader = (options = {}) => {
7127
+ const removalDelay = DEFAULT_REMOVAL_DELAY;
7128
+ const coalesceWindow = options.coalesce ?? DEFAULT_COALESCE_WINDOW;
7129
+ let lastCount = -1;
7130
+ const log12 = (expiries) => {
7131
+ if (expiries.length !== lastCount) {
7132
+ lastCount = expiries.length;
7133
+ }
7134
+ };
7135
+ const dequeue = StateEffect10.define();
7136
+ const fadeField = StateField12.define({
7137
+ create: () => ({
7138
+ decorations: Decoration14.none,
7139
+ expiries: [],
7140
+ batchStart: 0
7141
+ }),
7142
+ update: ({ decorations: decorations2, expiries, batchStart }, tr) => {
6984
7143
  for (const effect of tr.effects) {
6985
- if (effect.is(hideCursor)) {
6986
- return false;
7144
+ if (effect.is(dequeue)) {
7145
+ const now2 = effect.value;
7146
+ let removeCount = 0;
7147
+ while (removeCount < expiries.length && expiries[removeCount] <= now2) {
7148
+ removeCount++;
7149
+ }
7150
+ if (removeCount > 0) {
7151
+ expiries = expiries.slice(removeCount);
7152
+ let skipped = 0;
7153
+ decorations2 = decorations2.update({
7154
+ filter: () => {
7155
+ if (skipped < removeCount) {
7156
+ skipped++;
7157
+ return false;
7158
+ }
7159
+ return true;
7160
+ }
7161
+ });
7162
+ }
7163
+ }
7164
+ }
7165
+ if (!tr.docChanged) {
7166
+ log12(expiries);
7167
+ return {
7168
+ decorations: decorations2,
7169
+ expiries,
7170
+ batchStart
7171
+ };
7172
+ }
7173
+ let isReset = tr.state.doc.length === 0;
7174
+ if (!isReset && tr.startState.doc.length > 0) {
7175
+ tr.changes.iterChanges((fromA, toA) => {
7176
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7177
+ isReset = true;
7178
+ }
7179
+ });
7180
+ }
7181
+ if (isReset) {
7182
+ log12([]);
7183
+ return {
7184
+ decorations: Decoration14.none,
7185
+ expiries: [],
7186
+ batchStart: 0
7187
+ };
7188
+ }
7189
+ const now = Date.now();
7190
+ const add = [];
7191
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7192
+ if (toA === tr.startState.doc.length && inserted.length > 0) {
7193
+ add.push({
7194
+ from: fromB,
7195
+ to: toB
7196
+ });
7197
+ }
7198
+ });
7199
+ if (add.length > 0) {
7200
+ const canCoalesce = expiries.length > 0 && batchStart > 0 && now - batchStart < coalesceWindow;
7201
+ if (canCoalesce) {
7202
+ let lastFrom = -1;
7203
+ let lastTo = -1;
7204
+ decorations2.between(0, tr.state.doc.length, (from, to) => {
7205
+ lastFrom = from;
7206
+ lastTo = to;
7207
+ });
7208
+ if (lastFrom >= 0) {
7209
+ decorations2 = decorations2.update({
7210
+ filter: (from, to) => !(from === lastFrom && to === lastTo)
7211
+ });
7212
+ const mergedFrom = Math.min(lastFrom, add[0].from);
7213
+ const mergedTo = add[add.length - 1].to;
7214
+ decorations2 = decorations2.update({
7215
+ add: [
7216
+ Decoration14.mark({
7217
+ class: "cm-fader"
7218
+ }).range(mergedFrom, mergedTo)
7219
+ ]
7220
+ });
7221
+ expiries = [
7222
+ ...expiries.slice(0, -1),
7223
+ now + removalDelay
7224
+ ];
7225
+ }
7226
+ } else {
7227
+ batchStart = now;
7228
+ expiries = [
7229
+ ...expiries,
7230
+ now + removalDelay
7231
+ ];
7232
+ decorations2 = decorations2.update({
7233
+ add: add.map(({ from, to }) => Decoration14.mark({
7234
+ class: "cm-fader"
7235
+ }).range(from, to))
7236
+ });
7237
+ }
7238
+ }
7239
+ log12(expiries);
7240
+ return {
7241
+ decorations: decorations2,
7242
+ expiries,
7243
+ batchStart
7244
+ };
7245
+ },
7246
+ provide: (f) => EditorView29.decorations.from(f, (value) => value.decorations)
7247
+ });
7248
+ const cleanup = ViewPlugin20.fromClass(class {
7249
+ view;
7250
+ #timer;
7251
+ constructor(view) {
7252
+ this.view = view;
7253
+ this.#schedule();
7254
+ }
7255
+ update() {
7256
+ this.#schedule();
7257
+ }
7258
+ #schedule() {
7259
+ const { expiries } = this.view.state.field(fadeField);
7260
+ if (expiries.length === 0) {
7261
+ clearTimeout(this.#timer);
7262
+ this.#timer = void 0;
7263
+ return;
7264
+ }
7265
+ if (this.#timer !== void 0) {
7266
+ return;
7267
+ }
7268
+ const delay = Math.max(CLEANUP_INTERVAL, expiries[0] - Date.now());
7269
+ this.#timer = setTimeout(() => {
7270
+ this.#timer = void 0;
7271
+ this.view.dispatch({
7272
+ effects: dequeue.of(Date.now())
7273
+ });
7274
+ }, delay);
7275
+ }
7276
+ destroy() {
7277
+ clearTimeout(this.#timer);
7278
+ }
7279
+ });
7280
+ return [
7281
+ fadeField,
7282
+ cleanup,
7283
+ EditorView29.theme({
7284
+ ".cm-fader": {
7285
+ animation: "fader 1s ease-out forwards"
7286
+ },
7287
+ "@keyframes fader": {
7288
+ "0%": {
7289
+ textShadow: "0 0 16px rgba(100, 200, 255, 1), 0 0 32px rgba(100, 200, 255, 0.6)"
7290
+ },
7291
+ "100%": {}
7292
+ }
7293
+ })
7294
+ ];
7295
+ };
7296
+
7297
+ // src/extensions/tags/typewriter.ts
7298
+ import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7299
+ import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7300
+ import { Domino as Domino3 } from "@dxos/ui";
7301
+ var typewriterBypass = Annotation3.define();
7302
+ var typewriterDrainingEffect = StateEffect11.define();
7303
+ var CURSOR_LINGER = 3e3;
7304
+ var FRAME_BUDGET_MS = 4;
7305
+ var CHARS_PER_FRAME = 5;
7306
+ var FLUSH_THRESHOLD = 2e3;
7307
+ var COMPACT_HEAD_THRESHOLD = 4096;
7308
+ var typewriter = (options = {}) => {
7309
+ const streamingTags = options.streamingTags ?? /* @__PURE__ */ new Set();
7310
+ const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;
7311
+ const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;
7312
+ const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;
7313
+ const suppressAppend = StateEffect11.define();
7314
+ const insertChunk = StateEffect11.define();
7315
+ const bufferField = StateField13.define({
7316
+ create: () => ({
7317
+ text: "",
7318
+ head: 0,
7319
+ insertAt: 0
7320
+ }),
7321
+ update: (value, tr) => {
7322
+ let { text, head, insertAt } = value;
7323
+ for (const effect of tr.effects) {
7324
+ if (effect.is(suppressAppend)) {
7325
+ if (text.length === head) {
7326
+ insertAt = effect.value.from;
7327
+ }
7328
+ text += effect.value.text;
7329
+ }
7330
+ if (effect.is(insertChunk)) {
7331
+ head += effect.value.text.length;
7332
+ insertAt = effect.value.from + effect.value.text.length;
7333
+ if (head >= COMPACT_HEAD_THRESHOLD || head > 0 && head * 2 >= text.length) {
7334
+ text = text.slice(head);
7335
+ head = 0;
7336
+ }
6987
7337
  }
6988
7338
  }
6989
7339
  if (tr.docChanged) {
6990
- return true;
7340
+ let isReset = tr.state.doc.length === 0;
7341
+ if (!isReset && tr.startState.doc.length > 0) {
7342
+ tr.changes.iterChanges((fromA, toA) => {
7343
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7344
+ isReset = true;
7345
+ }
7346
+ });
7347
+ }
7348
+ if (isReset) {
7349
+ return {
7350
+ text: "",
7351
+ head: 0,
7352
+ insertAt: 0
7353
+ };
7354
+ }
7355
+ if (!tr.effects.some((effect) => effect.is(insertChunk))) {
7356
+ insertAt = tr.changes.mapPos(Math.min(insertAt, tr.startState.doc.length));
7357
+ }
6991
7358
  }
6992
- return value;
7359
+ return {
7360
+ text,
7361
+ head,
7362
+ insertAt
7363
+ };
6993
7364
  }
6994
7365
  });
6995
- const timerPlugin = ViewPlugin18.fromClass(class {
7366
+ const filter = EditorState3.transactionFilter.of((tr) => {
7367
+ if (!tr.docChanged) {
7368
+ return tr;
7369
+ }
7370
+ if (tr.annotation(typewriterBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7371
+ return tr;
7372
+ }
7373
+ let appendedText = "";
7374
+ let appendFrom = -1;
7375
+ let isAppendOnly = true;
7376
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
7377
+ if (toA === tr.startState.doc.length && fromA === toA && inserted.length > 0) {
7378
+ appendedText += inserted.sliceString(0);
7379
+ if (appendFrom === -1) {
7380
+ appendFrom = fromA;
7381
+ }
7382
+ } else {
7383
+ isAppendOnly = false;
7384
+ }
7385
+ });
7386
+ if (!isAppendOnly || appendedText.length === 0) {
7387
+ return tr;
7388
+ }
7389
+ return {
7390
+ changes: ChangeSet2.empty(tr.startState.doc.length),
7391
+ effects: suppressAppend.of({
7392
+ from: appendFrom,
7393
+ text: appendedText
7394
+ })
7395
+ };
7396
+ });
7397
+ const drainPlugin = ViewPlugin21.fromClass(class {
6996
7398
  view;
6997
- timer;
7399
+ _raf;
7400
+ _activeStreamTag = null;
6998
7401
  constructor(view) {
6999
7402
  this.view = view;
7000
7403
  }
7001
7404
  update(update2) {
7002
- if (update2.docChanged) {
7003
- clearTimeout(this.timer);
7004
- this.timer = setTimeout(() => {
7005
- this.view.dispatch({
7006
- effects: hideCursor.of(null)
7007
- });
7008
- }, BLINK_RATE);
7405
+ const { text, head } = update2.state.field(bufferField);
7406
+ const pending = text.length - head;
7407
+ if (pending === 0) {
7408
+ this._activeStreamTag = null;
7409
+ }
7410
+ if (pending > 0 && this._raf === void 0) {
7411
+ this._start();
7009
7412
  }
7010
7413
  }
7414
+ _start() {
7415
+ queueMicrotask(() => {
7416
+ this.view.dispatch({
7417
+ effects: typewriterDrainingEffect.of(true),
7418
+ annotations: typewriterBypass.of(true)
7419
+ });
7420
+ });
7421
+ this._raf = requestAnimationFrame(this._tick);
7422
+ }
7423
+ _tick = () => {
7424
+ const { text, head, insertAt } = this.view.state.field(bufferField);
7425
+ const pending = text.length - head;
7426
+ if (pending === 0) {
7427
+ this.view.dispatch({
7428
+ effects: typewriterDrainingEffect.of(false),
7429
+ annotations: typewriterBypass.of(true)
7430
+ });
7431
+ this._raf = void 0;
7432
+ return;
7433
+ }
7434
+ if (pending > flushThreshold) {
7435
+ const chunk = text.slice(head);
7436
+ this._activeStreamTag = null;
7437
+ this.view.dispatch({
7438
+ changes: {
7439
+ from: insertAt,
7440
+ insert: chunk
7441
+ },
7442
+ effects: insertChunk.of({
7443
+ from: insertAt,
7444
+ text: chunk
7445
+ })
7446
+ });
7447
+ this._raf = requestAnimationFrame(this._tick);
7448
+ return;
7449
+ }
7450
+ const startTime = performance.now();
7451
+ let pos = head;
7452
+ let activeTag = this._activeStreamTag;
7453
+ let charsEmitted = 0;
7454
+ while (pos < text.length && performance.now() - startTime < frameBudgetMs) {
7455
+ const result = flushable(text, pos, streamingTags, activeTag);
7456
+ if (result.count === 0) {
7457
+ break;
7458
+ }
7459
+ if (charsEmitted > 0 && charsEmitted + result.count > charsPerFrame) {
7460
+ break;
7461
+ }
7462
+ if (result.enterTag) {
7463
+ activeTag = result.enterTag;
7464
+ }
7465
+ if (result.exitTag) {
7466
+ activeTag = null;
7467
+ }
7468
+ pos += result.count;
7469
+ charsEmitted += result.count;
7470
+ }
7471
+ const totalCount = pos - head;
7472
+ if (totalCount > 0) {
7473
+ const chunk = text.slice(head, head + totalCount);
7474
+ this._activeStreamTag = activeTag;
7475
+ this.view.dispatch({
7476
+ changes: {
7477
+ from: insertAt,
7478
+ insert: chunk
7479
+ },
7480
+ effects: insertChunk.of({
7481
+ from: insertAt,
7482
+ text: chunk
7483
+ })
7484
+ });
7485
+ }
7486
+ this._raf = requestAnimationFrame(this._tick);
7487
+ };
7011
7488
  destroy() {
7012
- clearTimeout(this.timer);
7489
+ if (this._raf !== void 0) {
7490
+ cancelAnimationFrame(this._raf);
7491
+ }
7013
7492
  }
7014
7493
  });
7015
- const cursorDecoration = StateField12.define({
7016
- create: () => Decoration14.none,
7017
- update: (_decorations, tr) => {
7018
- const show = tr.state.field(showCursor);
7019
- if (!show) {
7020
- return Decoration14.none;
7494
+ return [
7495
+ bufferField,
7496
+ filter,
7497
+ drainPlugin,
7498
+ options.cursor && typewriterCursor(bufferField)
7499
+ ].filter(Boolean);
7500
+ };
7501
+ var typewriterCursor = (bufferField) => {
7502
+ const hideCursor = StateEffect11.define();
7503
+ const visibilityField = StateField13.define({
7504
+ create: () => ({
7505
+ visible: false,
7506
+ insertAt: 0,
7507
+ lastNonWsAt: 0
7508
+ }),
7509
+ update: (value, tr) => {
7510
+ const { text, head, insertAt } = tr.state.field(bufferField);
7511
+ const pending = text.length - head;
7512
+ if (pending > 0) {
7513
+ let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));
7514
+ if (tr.docChanged) {
7515
+ tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
7516
+ const chunk = inserted.sliceString(0);
7517
+ if (chunk.trim().length > 0) {
7518
+ lastNonWsAt = _fromB + chunk.length;
7519
+ }
7520
+ });
7521
+ }
7522
+ return {
7523
+ visible: true,
7524
+ insertAt,
7525
+ lastNonWsAt
7526
+ };
7021
7527
  }
7022
- const endPos = tr.state.doc.length;
7023
- return Decoration14.set([
7024
- Decoration14.widget({
7528
+ for (const effect of tr.effects) {
7529
+ if (effect.is(hideCursor)) {
7530
+ return {
7531
+ ...value,
7532
+ visible: false
7533
+ };
7534
+ }
7535
+ }
7536
+ return value;
7537
+ }
7538
+ });
7539
+ const decorationField = StateField13.define({
7540
+ create: () => Decoration15.none,
7541
+ update: (_decorations, tr) => {
7542
+ const { visible, insertAt, lastNonWsAt } = tr.state.field(visibilityField);
7543
+ if (!visible) {
7544
+ return Decoration15.none;
7545
+ }
7546
+ const { text, head } = tr.state.field(bufferField);
7547
+ const cursorAt = text.length > head ? insertAt : lastNonWsAt;
7548
+ const pos = Math.min(cursorAt, tr.state.doc.length);
7549
+ return Decoration15.set([
7550
+ Decoration15.widget({
7025
7551
  widget: new CursorWidget(),
7026
7552
  side: 1
7027
- }).range(endPos)
7553
+ }).range(pos)
7028
7554
  ]);
7029
7555
  },
7030
- provide: (f) => EditorView28.decorations.from(f)
7556
+ provide: (field) => EditorView30.decorations.from(field)
7557
+ });
7558
+ const timerPlugin = ViewPlugin21.fromClass(class {
7559
+ view;
7560
+ _timer;
7561
+ constructor(view) {
7562
+ this.view = view;
7563
+ }
7564
+ update(update2) {
7565
+ const { text, head } = update2.state.field(bufferField);
7566
+ const { visible } = update2.state.field(visibilityField);
7567
+ const pending = text.length - head;
7568
+ if (pending > 0) {
7569
+ clearTimeout(this._timer);
7570
+ this._timer = void 0;
7571
+ } else if (visible && this._timer === void 0) {
7572
+ this._timer = setTimeout(() => {
7573
+ this.view.dispatch({
7574
+ effects: hideCursor.of(null)
7575
+ });
7576
+ this._timer = void 0;
7577
+ }, CURSOR_LINGER);
7578
+ }
7579
+ }
7580
+ destroy() {
7581
+ clearTimeout(this._timer);
7582
+ }
7031
7583
  });
7032
7584
  return [
7033
- showCursor,
7034
- timerPlugin,
7035
- cursorDecoration
7585
+ visibilityField,
7586
+ decorationField,
7587
+ timerPlugin
7036
7588
  ];
7037
7589
  };
7038
- var CursorWidget = class extends WidgetType9 {
7590
+ var CursorWidget = class _CursorWidget extends WidgetType9 {
7591
+ // All instances are interchangeable — let CM reuse the existing DOM across drips so
7592
+ // the blink animation isn't restarted on every transaction.
7593
+ eq(other) {
7594
+ return other instanceof _CursorWidget;
7595
+ }
7039
7596
  toDOM() {
7040
- const inner = Domino3.of("span").text("\u258F").style({
7041
- animation: "blink 2s infinite"
7597
+ const inner = Domino3.of("span").text("\u2217").style({
7598
+ animation: "blink 1s infinite",
7599
+ animationDelay: "250ms"
7042
7600
  });
7043
7601
  return Domino3.of("span").style({
7044
7602
  opacity: "0.8"
7045
- }).children(inner).root;
7603
+ }).append(inner).root;
7046
7604
  }
7047
7605
  };
7048
- var fadeIn = (options = {}) => {
7049
- const FADE_IN_DURATION = 1e3;
7050
- const DEFAULT_REMOVAL_DELAY = 3e3;
7051
- const removalDelay = options.removalDelay ?? DEFAULT_REMOVAL_DELAY;
7052
- const removeDecoration = StateEffect8.define();
7053
- const fadeField = StateField12.define({
7054
- create: () => Decoration14.none,
7055
- update: (decorations2, tr) => {
7056
- let next = decorations2;
7057
- for (const effect of tr.effects) {
7058
- if (effect.is(removeDecoration)) {
7059
- const target = effect.value;
7060
- next = next.update({
7061
- filter: (from, to) => !(from === target.from && to === target.to)
7062
- });
7063
- }
7064
- }
7065
- if (!tr.docChanged) {
7066
- return next;
7606
+ var OPENING_TAG_NAME = /^<([a-zA-Z][\w-]*)/;
7607
+ var TAG_NAME_PROBE = 64;
7608
+ var escapeRegExpSource2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7609
+ var flushable = (buffer, start, streamingTags, activeStreamTag) => {
7610
+ if (start >= buffer.length) {
7611
+ return {
7612
+ count: 0
7613
+ };
7614
+ }
7615
+ if (activeStreamTag) {
7616
+ const closeTag = `</${activeStreamTag}>`;
7617
+ if (buffer.startsWith(closeTag, start)) {
7618
+ return {
7619
+ count: closeTag.length,
7620
+ exitTag: true
7621
+ };
7622
+ }
7623
+ if (buffer[start] === "<") {
7624
+ return {
7625
+ count: xmlElementLength(buffer, start)
7626
+ };
7627
+ }
7628
+ return {
7629
+ count: 1
7630
+ };
7631
+ }
7632
+ const ch = buffer[start];
7633
+ if (ch === "<") {
7634
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7635
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7636
+ if (nameMatch && streamingTags.has(nameMatch[1])) {
7637
+ const close = buffer.indexOf(">", start);
7638
+ if (close === -1) {
7639
+ return {
7640
+ count: 0
7641
+ };
7067
7642
  }
7068
- let isReset = tr.state.doc.length === 0;
7069
- if (!isReset) {
7070
- tr.changes.iterChanges((fromA, toA) => {
7071
- if (fromA === 0 && toA === tr.startState.doc.length) {
7072
- isReset = true;
7073
- }
7074
- });
7643
+ if (buffer[close - 1] === "/") {
7644
+ return {
7645
+ count: close + 1 - start
7646
+ };
7075
7647
  }
7076
- if (isReset) {
7077
- return Decoration14.none;
7648
+ return {
7649
+ count: close + 1 - start,
7650
+ enterTag: nameMatch[1]
7651
+ };
7652
+ }
7653
+ return {
7654
+ count: xmlElementLength(buffer, start)
7655
+ };
7656
+ }
7657
+ if (ch === "!" && buffer.length > start + 1 && buffer[start + 1] === "[") {
7658
+ return {
7659
+ count: linkLength(buffer, start, start + 1)
7660
+ };
7661
+ }
7662
+ if (ch === "[") {
7663
+ return {
7664
+ count: linkLength(buffer, start, start)
7665
+ };
7666
+ }
7667
+ return {
7668
+ count: 1
7669
+ };
7670
+ };
7671
+ var xmlElementLength = (buffer, start = 0) => {
7672
+ const close = buffer.indexOf(">", start);
7673
+ if (close === -1) {
7674
+ return 0;
7675
+ }
7676
+ if (buffer[close - 1] === "/") {
7677
+ return close + 1 - start;
7678
+ }
7679
+ if (buffer[start + 1] === "/") {
7680
+ return close + 1 - start;
7681
+ }
7682
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7683
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7684
+ if (!nameMatch) {
7685
+ return 1;
7686
+ }
7687
+ const tagName = nameMatch[1];
7688
+ let depth = 0;
7689
+ const tagPattern = new RegExp(`<(/?)${escapeRegExpSource2(tagName)}(\\s[^>]*)?>`, "g");
7690
+ tagPattern.lastIndex = start;
7691
+ let match;
7692
+ while ((match = tagPattern.exec(buffer)) !== null) {
7693
+ const isSelfClosing = match[0].endsWith("/>");
7694
+ const isClosing = match[1] === "/";
7695
+ if (isSelfClosing) {
7696
+ if (depth === 0) {
7697
+ return match.index + match[0].length - start;
7698
+ }
7699
+ } else if (isClosing) {
7700
+ depth--;
7701
+ if (depth === 0) {
7702
+ return match.index + match[0].length - start;
7078
7703
  }
7079
- const add = [];
7080
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7081
- if (fromB === 0 && toB === inserted.length) {
7704
+ } else {
7705
+ depth++;
7706
+ }
7707
+ }
7708
+ return 0;
7709
+ };
7710
+ var linkLength = (buffer, start, bracketAt) => {
7711
+ const bracketClose = buffer.indexOf("]", bracketAt + 1);
7712
+ if (bracketClose === -1) {
7713
+ return 0;
7714
+ }
7715
+ if (bracketClose + 1 >= buffer.length) {
7716
+ return 0;
7717
+ }
7718
+ if (buffer[bracketClose + 1] !== "(") {
7719
+ return 1;
7720
+ }
7721
+ const parenClose = buffer.indexOf(")", bracketClose + 2);
7722
+ if (parenClose === -1) {
7723
+ return 0;
7724
+ }
7725
+ return parenClose + 1 - start;
7726
+ };
7727
+
7728
+ // src/extensions/tags/xml-block-decoration.ts
7729
+ import { xmlLanguage as xmlLanguage2 } from "@codemirror/lang-xml";
7730
+ import { Decoration as Decoration16, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7731
+ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7732
+ const lineDecoration = lineClass ? Decoration16.line({
7733
+ class: lineClass
7734
+ }) : void 0;
7735
+ const contentDecoration = contentClass ? Decoration16.mark({
7736
+ class: contentClass
7737
+ }) : void 0;
7738
+ const hideDecoration = hideTags ? Decoration16.replace({}) : void 0;
7739
+ const buildDecorations5 = (view) => {
7740
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7741
+ if (!text.includes(`<${tag}`)) {
7742
+ return Decoration16.none;
7743
+ }
7744
+ const tree = xmlLanguage2.parser.parse(text);
7745
+ const ranges = [];
7746
+ tree.iterate({
7747
+ enter: (node) => {
7748
+ if (node.type.name !== "Element") {
7082
7749
  return;
7083
7750
  }
7084
- if (toA === tr.startState.doc.length && inserted.length > 0) {
7085
- add.push(Decoration14.mark({
7086
- class: "cm-fade-in"
7087
- }).range(fromB, toB));
7751
+ const openTag = node.node.getChild("OpenTag");
7752
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7753
+ const tagNameNode = openTag?.getChild("TagName");
7754
+ if (!openTag || !tagNameNode) {
7755
+ return;
7088
7756
  }
7089
- });
7090
- return next.update({
7091
- add
7092
- });
7093
- },
7094
- provide: (f) => EditorView28.decorations.from(f)
7095
- });
7096
- const timerPlugin = ViewPlugin18.fromClass(class {
7097
- view;
7098
- // Map a simple key "from-to" to timer id.
7099
- _timers = /* @__PURE__ */ new Map();
7757
+ if (text.slice(tagNameNode.from, tagNameNode.to) !== tag) {
7758
+ return;
7759
+ }
7760
+ const contentFrom = openTag.to;
7761
+ const contentTo = closeTag?.from ?? node.node.to;
7762
+ if (hideDecoration) {
7763
+ ranges.push(hideDecoration.range(openTag.from, openTag.to));
7764
+ if (closeTag) {
7765
+ ranges.push(hideDecoration.range(closeTag.from, closeTag.to));
7766
+ }
7767
+ }
7768
+ if (contentDecoration && contentFrom < contentTo) {
7769
+ ranges.push(contentDecoration.range(contentFrom, contentTo));
7770
+ }
7771
+ if (lineDecoration && contentFrom <= view.state.doc.length) {
7772
+ let pos = contentFrom;
7773
+ while (pos <= contentTo && pos <= view.state.doc.length) {
7774
+ const line = view.state.doc.lineAt(pos);
7775
+ ranges.push(lineDecoration.range(line.from));
7776
+ if (line.to >= contentTo) {
7777
+ break;
7778
+ }
7779
+ pos = line.to + 1;
7780
+ }
7781
+ }
7782
+ }
7783
+ });
7784
+ return Decoration16.set(ranges, true);
7785
+ };
7786
+ return ViewPlugin22.fromClass(class {
7787
+ decorations;
7100
7788
  constructor(view) {
7101
- this.view = view;
7789
+ this.decorations = buildDecorations5(view);
7102
7790
  }
7103
7791
  update(update2) {
7104
- if (!update2.docChanged) {
7105
- return;
7792
+ if (update2.docChanged) {
7793
+ this.decorations = buildDecorations5(update2.view);
7106
7794
  }
7107
- update2.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7108
- if (toA !== update2.startState.doc.length || inserted.length === 0) {
7795
+ }
7796
+ }, {
7797
+ decorations: (instance) => instance.decorations
7798
+ });
7799
+ };
7800
+
7801
+ // src/extensions/tags/xml-formatting.ts
7802
+ import { xmlLanguage as xmlLanguage3 } from "@codemirror/lang-xml";
7803
+ import { Decoration as Decoration17, EditorView as EditorView31, ViewPlugin as ViewPlugin23 } from "@codemirror/view";
7804
+ var XML_TAG_NODES = /* @__PURE__ */ new Set([
7805
+ "OpenTag",
7806
+ "CloseTag",
7807
+ "SelfClosingTag",
7808
+ "MismatchedCloseTag"
7809
+ ]);
7810
+ var xmlElementMark = Decoration17.mark({
7811
+ class: "cm-xml-element"
7812
+ });
7813
+ var xmlTagMark = Decoration17.mark({
7814
+ class: "cm-xml-tag"
7815
+ });
7816
+ var xmlContentMark = Decoration17.mark({
7817
+ class: "cm-xml-content"
7818
+ });
7819
+ var xmlFormatting = ({ skip } = {}) => {
7820
+ const skipSet = skip && skip.length > 0 ? new Set(skip) : void 0;
7821
+ const buildDecorations5 = (view) => {
7822
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7823
+ if (!text.includes("<")) {
7824
+ return Decoration17.none;
7825
+ }
7826
+ const tagNameAt = (node) => text.slice(node.from, node.to);
7827
+ const tree = xmlLanguage3.parser.parse(text);
7828
+ const ranges = [];
7829
+ tree.iterate({
7830
+ enter: (node) => {
7831
+ const name = node.type.name;
7832
+ if (name === "SelfClosingTag" && node.from < node.to) {
7833
+ if (skipSet) {
7834
+ const tagNameNode = node.node.getChild("TagName");
7835
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7836
+ return false;
7837
+ }
7838
+ }
7839
+ ranges.push(xmlElementMark.range(node.from, node.to));
7840
+ ranges.push(xmlTagMark.range(node.from, node.to));
7841
+ return;
7842
+ }
7843
+ if (XML_TAG_NODES.has(name) && node.from < node.to) {
7844
+ ranges.push(xmlTagMark.range(node.from, node.to));
7109
7845
  return;
7110
7846
  }
7111
- const key = `${fromB}-${toB}`;
7112
- if (this._timers.has(key)) {
7113
- clearTimeout(this._timers.get(key));
7847
+ if (name === "Element" && node.from < node.to) {
7848
+ const openTag = node.node.getChild("OpenTag");
7849
+ if (openTag && skipSet) {
7850
+ const tagNameNode = openTag.getChild("TagName");
7851
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7852
+ return false;
7853
+ }
7854
+ }
7855
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7856
+ ranges.push(xmlElementMark.range(node.from, node.to));
7857
+ if (openTag && closeTag && openTag.to < closeTag.from) {
7858
+ ranges.push(xmlContentMark.range(openTag.to, closeTag.from));
7859
+ }
7114
7860
  }
7115
- const totalDelay = FADE_IN_DURATION + removalDelay;
7116
- const id = setTimeout(() => {
7117
- this.view.dispatch({
7118
- effects: removeDecoration.of({
7119
- from: fromB,
7120
- to: toB
7121
- })
7122
- });
7123
- this._timers.delete(key);
7124
- }, totalDelay);
7125
- this._timers.set(key, id);
7126
- });
7127
- }
7128
- destroy() {
7129
- for (const id of this._timers.values()) {
7130
- clearTimeout(id);
7131
7861
  }
7132
- this._timers.clear();
7133
- }
7134
- });
7862
+ });
7863
+ return Decoration17.set(ranges, true);
7864
+ };
7135
7865
  return [
7136
- fadeField,
7137
- timerPlugin,
7138
- EditorView28.theme({
7139
- ".cm-line > span": {
7140
- opacity: "0.8"
7141
- },
7142
- ".cm-fade-in": {
7143
- animation: "fade-in 3s ease-out forwards"
7144
- },
7145
- "@keyframes fade-in": {
7146
- "0%": {
7147
- opacity: "0"
7148
- },
7149
- "80%": {
7150
- opacity: "1"
7151
- },
7152
- "100%": {
7153
- opacity: "0.8"
7866
+ ViewPlugin23.fromClass(class {
7867
+ decorations;
7868
+ constructor(view) {
7869
+ this.decorations = buildDecorations5(view);
7870
+ }
7871
+ update(update2) {
7872
+ if (update2.docChanged) {
7873
+ this.decorations = buildDecorations5(update2.view);
7154
7874
  }
7155
7875
  }
7876
+ }, {
7877
+ decorations: (instance) => instance.decorations
7878
+ }),
7879
+ EditorView31.baseTheme({
7880
+ ".cm-xml-element": {
7881
+ backgroundColor: "var(--color-active-surface)",
7882
+ borderRadius: "0.25rem",
7883
+ padding: "0.25rem"
7884
+ },
7885
+ ".cm-xml-tag": {
7886
+ color: "var(--color-blue-500)",
7887
+ fontFamily: "var(--font-mono)"
7888
+ },
7889
+ ".cm-xml-content": {}
7156
7890
  })
7157
7891
  ];
7158
7892
  };
7159
7893
 
7160
7894
  // src/extensions/tags/xml-tags.ts
7161
7895
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7162
- import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect9, StateField as StateField13 } from "@codemirror/state";
7163
- import { Decoration as Decoration15, EditorView as EditorView29, ViewPlugin as ViewPlugin19, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7896
+ import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7897
+ import { Decoration as Decoration18, EditorView as EditorView32, ViewPlugin as ViewPlugin24, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
7164
7898
  import { invariant as invariant7 } from "@dxos/invariant";
7165
7899
  import { log as log11 } from "@dxos/log";
7900
+ import { Domino as Domino4 } from "@dxos/ui";
7166
7901
 
7167
7902
  // src/extensions/tags/xml-util.ts
7168
7903
  import { invariant as invariant6 } from "@dxos/invariant";
7169
7904
  var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7170
7905
  var nodeToJson = (state, node) => {
7171
- invariant6(node.type.name === "Element", "Node is not an Element", {
7172
- F: __dxlog_file16,
7173
- L: 18,
7174
- S: void 0,
7175
- A: [
7176
- "node.type.name === 'Element'",
7177
- "'Node is not an Element'"
7178
- ]
7179
- });
7906
+ invariant6(node.type.name === "Element", "Node is not an Element", { "~LogMeta": "~LogMeta", F: __dxlog_file16, L: 8, S: void 0, A: ["node.type.name === 'Element'", "'Node is not an Element'"] });
7180
7907
  const openTag = node.node.getChild("OpenTag") || node.node.getChild("SelfClosingTag");
7181
7908
  if (openTag) {
7182
7909
  const tagName = openTag.getChild("TagName");
@@ -7208,13 +7935,23 @@ var nodeToJson = (state, node) => {
7208
7935
  if (node.type.name === "Element" && openTag.type.name !== "SelfClosingTag") {
7209
7936
  const children = [];
7210
7937
  let child = node.node.firstChild;
7938
+ const appendText = (raw) => {
7939
+ if (raw.length === 0) {
7940
+ return;
7941
+ }
7942
+ const last = children[children.length - 1];
7943
+ if (typeof last === "string") {
7944
+ children[children.length - 1] = last + raw;
7945
+ } else {
7946
+ children.push(raw);
7947
+ }
7948
+ };
7211
7949
  while (child) {
7212
7950
  if (child.type.name !== "OpenTag" && child.type.name !== "CloseTag") {
7213
7951
  if (child.type.name === "Text") {
7214
- const text = state.doc.sliceString(child.from, child.to).trim();
7215
- if (text) {
7216
- children.push(text);
7217
- }
7952
+ appendText(state.doc.sliceString(child.from, child.to));
7953
+ } else if (child.type.name === "EntityReference" || child.type.name === "CharacterReference") {
7954
+ appendText(decodeXmlEntity(state.doc.sliceString(child.from, child.to)));
7218
7955
  } else if (child.type.name === "Element") {
7219
7956
  const data = nodeToJson(state, child);
7220
7957
  if (data) {
@@ -7224,26 +7961,63 @@ var nodeToJson = (state, node) => {
7224
7961
  }
7225
7962
  child = child.nextSibling;
7226
7963
  }
7964
+ if (children.length > 0 && typeof children[0] === "string") {
7965
+ children[0] = children[0].trimStart();
7966
+ }
7227
7967
  if (children.length > 0) {
7228
- tag.children = children;
7968
+ const lastIndex = children.length - 1;
7969
+ const last = children[lastIndex];
7970
+ if (typeof last === "string") {
7971
+ children[lastIndex] = last.trimEnd();
7972
+ }
7973
+ }
7974
+ const trimmed = children.filter((value) => typeof value !== "string" || value.length > 0);
7975
+ if (trimmed.length > 0) {
7976
+ tag.children = trimmed;
7229
7977
  }
7230
7978
  }
7231
7979
  return tag;
7232
7980
  }
7233
7981
  };
7982
+ var XML_NAMED_ENTITIES = {
7983
+ "&lt;": "<",
7984
+ "&gt;": ">",
7985
+ "&amp;": "&",
7986
+ "&quot;": '"',
7987
+ "&apos;": "'"
7988
+ };
7989
+ var decodeXmlEntity = (raw) => {
7990
+ const named = XML_NAMED_ENTITIES[raw];
7991
+ if (named !== void 0) {
7992
+ return named;
7993
+ }
7994
+ const numeric = /^&#(x?)([0-9a-fA-F]+);$/.exec(raw);
7995
+ if (numeric) {
7996
+ const code = parseInt(numeric[2], numeric[1] ? 16 : 10);
7997
+ if (Number.isFinite(code)) {
7998
+ try {
7999
+ return String.fromCodePoint(code);
8000
+ } catch {
8001
+ }
8002
+ }
8003
+ }
8004
+ return raw;
8005
+ };
7234
8006
 
7235
8007
  // src/extensions/tags/xml-tags.ts
7236
8008
  var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7237
- var navigatePreviousEffect = StateEffect9.define();
7238
- var navigateNextEffect = StateEffect9.define();
8009
+ var navigatePreviousEffect = StateEffect12.define();
8010
+ var navigateNextEffect = StateEffect12.define();
7239
8011
  var getXmlTextChild = (children) => {
7240
8012
  const child = children?.[0];
7241
8013
  return typeof child === "string" ? child : null;
7242
8014
  };
7243
- var xmlTagContextEffect = StateEffect9.define();
7244
- var xmlTagResetEffect = StateEffect9.define();
7245
- var xmlTagUpdateEffect = StateEffect9.define();
7246
- var widgetContextStateField = StateField13.define({
8015
+ var xmlWidgetId = (explicit, fallback) => typeof explicit === "string" && explicit.length > 0 ? explicit : fallback;
8016
+ var escapeRegExpSource3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8017
+ var xmlTagContextEffect = StateEffect12.define();
8018
+ var xmlTagResetEffect = StateEffect12.define();
8019
+ var xmlTagUpdateEffect = StateEffect12.define();
8020
+ var widgetContextStateField = StateField14.define({
7247
8021
  create: () => void 0,
7248
8022
  update: (value, tr) => {
7249
8023
  for (const effect of tr.effects) {
@@ -7254,7 +8028,7 @@ var widgetContextStateField = StateField13.define({
7254
8028
  return value;
7255
8029
  }
7256
8030
  });
7257
- var widgetStateMapStateField = StateField13.define({
8031
+ var widgetStateMapStateField = StateField14.define({
7258
8032
  create: () => ({}),
7259
8033
  update: (map, tr) => {
7260
8034
  for (const effect of tr.effects) {
@@ -7266,12 +8040,7 @@ var widgetStateMapStateField = StateField13.define({
7266
8040
  log11("widget updated", {
7267
8041
  id,
7268
8042
  value
7269
- }, {
7270
- F: __dxlog_file17,
7271
- L: 153,
7272
- S: void 0,
7273
- C: (f, a) => f(...a)
7274
- });
8043
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 59, S: void 0 });
7275
8044
  const state = typeof value === "function" ? value(map[id]) : value;
7276
8045
  return {
7277
8046
  ...map,
@@ -7301,12 +8070,7 @@ var createWidgetMap = (setWidgets) => {
7301
8070
  log11("widget mounted", {
7302
8071
  id: state.id,
7303
8072
  tag: state.props._tag
7304
- }, {
7305
- F: __dxlog_file17,
7306
- L: 206,
7307
- S: void 0,
7308
- C: (f, a) => f(...a)
7309
- });
8073
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 101, S: void 0 });
7310
8074
  widgets.set(state.id, state);
7311
8075
  setWidgets?.([
7312
8076
  ...widgets.values()
@@ -7317,12 +8081,7 @@ var createWidgetMap = (setWidgets) => {
7317
8081
  log11("widget unmounted", {
7318
8082
  id,
7319
8083
  tag: state?.props._tag
7320
- }, {
7321
- F: __dxlog_file17,
7322
- L: 212,
7323
- S: void 0,
7324
- C: (f, a) => f(...a)
7325
- });
8084
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 112, S: void 0 });
7326
8085
  widgets.delete(id);
7327
8086
  setWidgets?.([
7328
8087
  ...widgets.values()
@@ -7331,7 +8090,7 @@ var createWidgetMap = (setWidgets) => {
7331
8090
  };
7332
8091
  return notifier;
7333
8092
  };
7334
- var keyHandlers = keymap13.of([
8093
+ var keyHandlers = keymap14.of([
7335
8094
  {
7336
8095
  key: "Mod-ArrowUp",
7337
8096
  run: (view) => {
@@ -7352,7 +8111,7 @@ var keyHandlers = keymap13.of([
7352
8111
  }
7353
8112
  ]);
7354
8113
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7355
- return EditorView29.updateListener.of((update2) => {
8114
+ return EditorView32.updateListener.of((update2) => {
7356
8115
  update2.transactions.forEach((transaction) => {
7357
8116
  for (const effect of transaction.effects) {
7358
8117
  if (effect.is(navigatePreviousEffect)) {
@@ -7437,7 +8196,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7437
8196
  });
7438
8197
  });
7439
8198
  };
7440
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin19.fromClass(class {
8199
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
7441
8200
  update(update2) {
7442
8201
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
7443
8202
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -7464,14 +8223,14 @@ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin1
7464
8223
  }
7465
8224
  }
7466
8225
  });
7467
- var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.define({
8226
+ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.define({
7468
8227
  create: (state) => {
7469
8228
  return buildDecorations4(state, {
7470
8229
  from: 0,
7471
8230
  to: state.doc.length
7472
8231
  }, registry, notifier);
7473
8232
  },
7474
- update: ({ from, decorations: decorations2 }, tr) => {
8233
+ update: ({ from, streamingFrom, decorations: decorations2 }, tr) => {
7475
8234
  for (const effect of tr.effects) {
7476
8235
  if (effect.is(xmlTagResetEffect)) {
7477
8236
  if (tr.docChanged) {
@@ -7482,7 +8241,7 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7482
8241
  }
7483
8242
  return {
7484
8243
  from: 0,
7485
- decorations: Decoration15.none
8244
+ decorations: Decoration18.none
7486
8245
  };
7487
8246
  }
7488
8247
  }
@@ -7493,24 +8252,23 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7493
8252
  log11("document reset", {
7494
8253
  from,
7495
8254
  to: state.doc.length
7496
- }, {
7497
- F: __dxlog_file17,
7498
- L: 374,
7499
- S: void 0,
7500
- C: (f, a) => f(...a)
7501
- });
8255
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 298, S: void 0 });
7502
8256
  return buildDecorations4(state, {
7503
8257
  from: 0,
7504
8258
  to: state.doc.length
7505
8259
  }, registry, notifier);
7506
8260
  } else {
8261
+ const rebuildFrom = streamingFrom ?? from;
7507
8262
  const result = buildDecorations4(state, {
7508
- from,
8263
+ from: rebuildFrom,
7509
8264
  to: state.doc.length
7510
8265
  }, registry, notifier);
7511
8266
  return {
7512
8267
  from: result.from,
8268
+ streamingFrom: result.streamingFrom,
7513
8269
  decorations: decorations2.update({
8270
+ // Remove old streaming decorations — they are rebuilt each tick.
8271
+ filter: (_f, _t, deco) => !deco.spec.streaming,
7514
8272
  add: decorationSetToArray(result.decorations)
7515
8273
  })
7516
8274
  };
@@ -7518,12 +8276,13 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7518
8276
  }
7519
8277
  return {
7520
8278
  from,
8279
+ streamingFrom,
7521
8280
  decorations: decorations2
7522
8281
  };
7523
8282
  },
7524
8283
  provide: (field) => [
7525
- EditorView29.decorations.from(field, (v) => v.decorations),
7526
- EditorView29.atomicRanges.of((view) => view.state.field(field).decorations || Decoration15.none)
8284
+ EditorView32.decorations.from(field, (v) => v.decorations),
8285
+ EditorView32.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
7527
8286
  ]
7528
8287
  });
7529
8288
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -7534,10 +8293,11 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7534
8293
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
7535
8294
  return {
7536
8295
  from: range.from,
7537
- decorations: Decoration15.none
8296
+ decorations: Decoration18.none
7538
8297
  };
7539
8298
  }
7540
8299
  let last = range.from;
8300
+ let streamingFrom;
7541
8301
  tree.iterate({
7542
8302
  from: range.from,
7543
8303
  to: range.to,
@@ -7550,21 +8310,26 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7550
8310
  if (args) {
7551
8311
  const def = registry[args._tag];
7552
8312
  if (def) {
8313
+ if (def.streaming && !node.node.getChild("CloseTag")) {
8314
+ return false;
8315
+ }
7553
8316
  const { block, factory, Component } = def;
7554
- const widgetState = args.id ? widgetStateMap[args.id] : void 0;
7555
8317
  const nodeRange = {
7556
8318
  from: node.node.from,
7557
8319
  to: node.node.to
7558
8320
  };
8321
+ const widgetId = xmlWidgetId(args.id, def.streaming ? `cm-xml-${nodeRange.from}` : `cm-xml-${nodeRange.from}-${nodeRange.to}`);
8322
+ const widgetState = widgetStateMap[widgetId];
7559
8323
  const props = {
7560
- context,
8324
+ id: widgetId,
7561
8325
  range: nodeRange,
8326
+ context,
7562
8327
  ...args,
7563
8328
  ...widgetState
7564
8329
  };
7565
- const widget = factory ? factory(props) : Component ? args.id && new PlaceholderWidget2(args.id, Component, props, notifier) : void 0;
8330
+ const widget = factory ? factory(props) ?? void 0 : Component ? new PlaceholderWidget2(widgetId, Component, props, notifier) : void 0;
7566
8331
  if (widget) {
7567
- builder.add(nodeRange.from, nodeRange.to, Decoration15.replace({
8332
+ builder.add(nodeRange.from, nodeRange.to, Decoration18.replace({
7568
8333
  widget,
7569
8334
  block,
7570
8335
  atomic: true,
@@ -7576,20 +8341,72 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7576
8341
  }
7577
8342
  }
7578
8343
  } catch (err) {
7579
- log11.catch(err, void 0, {
7580
- F: __dxlog_file17,
7581
- L: 459,
7582
- S: void 0,
7583
- C: (f, a) => f(...a)
7584
- });
8344
+ log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 401, S: void 0 });
7585
8345
  }
7586
8346
  return false;
7587
8347
  }
7588
8348
  }
7589
8349
  }
7590
8350
  });
8351
+ const streamingTagNames = Object.entries(registry).filter(([, def]) => def.streaming).map(([name]) => name).sort((a, b) => b.length - a.length);
8352
+ if (streamingTagNames.length > 0) {
8353
+ const tailText = state.sliceDoc(range.from, range.to);
8354
+ const streamingPattern = streamingTagNames.map(escapeRegExpSource3).join("|");
8355
+ const tagPattern = new RegExp(`<(${streamingPattern})(\\s[^>]*)?>`, "g");
8356
+ let match;
8357
+ while ((match = tagPattern.exec(tailText)) !== null) {
8358
+ const tagName = match[1];
8359
+ const closeTag = `</${tagName}>`;
8360
+ const afterOpen = match.index + match[0].length;
8361
+ if (tailText.indexOf(closeTag, afterOpen) === -1) {
8362
+ const absoluteFrom = range.from + match.index;
8363
+ const contentFrom = range.from + afterOpen;
8364
+ const innerText = state.sliceDoc(contentFrom, range.to).trim();
8365
+ const def = registry[tagName];
8366
+ const props = {
8367
+ _tag: tagName,
8368
+ context,
8369
+ range: {
8370
+ from: absoluteFrom,
8371
+ to: range.to
8372
+ },
8373
+ children: innerText ? [
8374
+ innerText
8375
+ ] : void 0
8376
+ };
8377
+ const attrPattern = /(\w+)="([^"]*)"/g;
8378
+ let attrMatch;
8379
+ while ((attrMatch = attrPattern.exec(match[0])) !== null) {
8380
+ props[attrMatch[1]] = attrMatch[2];
8381
+ }
8382
+ const widgetId = xmlWidgetId(props.id, `cm-xml-${absoluteFrom}`);
8383
+ const widgetState = widgetStateMap[widgetId];
8384
+ const mergedProps = {
8385
+ ...props,
8386
+ id: widgetId,
8387
+ ...widgetState
8388
+ };
8389
+ const widget = def.factory ? def.factory(mergedProps) ?? void 0 : def.Component ? new PlaceholderWidget2(widgetId, def.Component, mergedProps, notifier, true) : void 0;
8390
+ if (widget) {
8391
+ builder.add(absoluteFrom, range.to, Decoration18.replace({
8392
+ widget,
8393
+ block: def.block,
8394
+ atomic: true,
8395
+ inclusive: true,
8396
+ tag: tagName,
8397
+ streaming: true,
8398
+ contentFrom
8399
+ }));
8400
+ streamingFrom = absoluteFrom;
8401
+ last = absoluteFrom;
8402
+ }
8403
+ break;
8404
+ }
8405
+ }
8406
+ }
7591
8407
  return {
7592
8408
  from: last,
8409
+ streamingFrom,
7593
8410
  decorations: builder.finish()
7594
8411
  };
7595
8412
  };
@@ -7598,108 +8415,62 @@ var PlaceholderWidget2 = class extends WidgetType10 {
7598
8415
  Component;
7599
8416
  props;
7600
8417
  notifier;
7601
- _root = null;
7602
- constructor(id, Component, props, notifier) {
7603
- super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier;
7604
- invariant7(id, void 0, {
7605
- F: __dxlog_file17,
7606
- L: 485,
7607
- S: this,
7608
- A: [
7609
- "id",
7610
- ""
7611
- ]
7612
- });
8418
+ streaming;
8419
+ #root = null;
8420
+ #view;
8421
+ constructor(id, Component, props, notifier, streaming) {
8422
+ super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier, this.streaming = streaming;
8423
+ invariant7(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 488, S: this, A: ["id", ""] });
7613
8424
  }
7614
8425
  get root() {
7615
- return this._root;
8426
+ return this.#root;
7616
8427
  }
7617
8428
  eq(other) {
8429
+ if (this.streaming) {
8430
+ return false;
8431
+ }
7618
8432
  return this.id === other.id;
7619
8433
  }
7620
8434
  ignoreEvent() {
7621
8435
  return true;
7622
8436
  }
7623
- toDOM(_view) {
7624
- this._root = document.createElement("span");
8437
+ toDOM(view) {
8438
+ this.#view = view;
8439
+ this.#root = Domino4.of("div").classNames("min-h-[24px]").root;
8440
+ const props = Object.assign({}, this.props, {
8441
+ view
8442
+ });
8443
+ this.notifier.mounted({
8444
+ id: this.id,
8445
+ root: this.#root,
8446
+ props,
8447
+ Component: this.Component
8448
+ });
8449
+ return this.#root;
8450
+ }
8451
+ updateDOM(dom) {
8452
+ this.#root = dom;
8453
+ const props = Object.assign({}, this.props, {
8454
+ view: this.#view
8455
+ });
7625
8456
  this.notifier.mounted({
7626
8457
  id: this.id,
7627
- root: this._root,
7628
- props: this.props,
8458
+ root: this.#root,
8459
+ props,
7629
8460
  Component: this.Component
7630
8461
  });
7631
- return this._root;
8462
+ return true;
7632
8463
  }
7633
8464
  destroy(_dom) {
7634
8465
  this.notifier.unmounted(this.id);
7635
- this._root = null;
8466
+ this.#root = null;
8467
+ this.#view = void 0;
7636
8468
  }
7637
8469
  };
7638
-
7639
- // src/extensions/typewriter.ts
7640
- import { keymap as keymap14 } from "@codemirror/view";
7641
- var defaultItems = [
7642
- "hello world!",
7643
- "this is a test.",
7644
- "this is [DXOS](https://dxos.org)"
7645
- ];
7646
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
7647
- let t;
7648
- let idx = 0;
7649
- return [
7650
- keymap14.of([
7651
- {
7652
- // Reset.
7653
- key: "alt-meta-'",
7654
- run: () => {
7655
- clearTimeout(t);
7656
- idx = 0;
7657
- return true;
7658
- }
7659
- },
7660
- {
7661
- // Next prompt.
7662
- // TODO(burdon): Press 1-9 to select prompt?
7663
- key: "Shift-Meta-'",
7664
- run: (view) => {
7665
- clearTimeout(t);
7666
- const text = items[idx++];
7667
- if (idx === items?.length) {
7668
- idx = 0;
7669
- }
7670
- let i = 0;
7671
- const insert = (d = 0) => {
7672
- t = setTimeout(() => {
7673
- const pos = view.state.selection.main.head;
7674
- view.dispatch({
7675
- changes: {
7676
- from: pos,
7677
- insert: text[i++]
7678
- },
7679
- selection: {
7680
- anchor: pos + 1
7681
- }
7682
- });
7683
- if (i < text.length) {
7684
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
7685
- }
7686
- }, d);
7687
- };
7688
- insert();
7689
- return true;
7690
- }
7691
- }
7692
- ])
7693
- ];
7694
- };
7695
8470
  export {
7696
8471
  Cursor,
7697
- EditorInputMode,
7698
- EditorInputModes,
7699
- EditorState3 as EditorState,
7700
- EditorView30 as EditorView,
7701
- EditorViewMode,
7702
- EditorViewModes,
8472
+ EditorState4 as EditorState,
8473
+ EditorView33 as EditorView,
7703
8474
  Inline,
7704
8475
  InputModeExtensions,
7705
8476
  List,
@@ -7716,6 +8487,7 @@ export {
7716
8487
  addStyle,
7717
8488
  annotations,
7718
8489
  autoScroll,
8490
+ autoScrollEffect,
7719
8491
  autocomplete,
7720
8492
  automerge,
7721
8493
  awareness,
@@ -7729,6 +8501,7 @@ export {
7729
8501
  commentClickedEffect,
7730
8502
  comments,
7731
8503
  commentsState,
8504
+ compactSlots,
7732
8505
  convertTreeToJson,
7733
8506
  createBasicExtensions,
7734
8507
  createComment,
@@ -7752,12 +8525,12 @@ export {
7752
8525
  defaultThemeSlots,
7753
8526
  deleteItem,
7754
8527
  documentId,
8528
+ documentSlots,
7755
8529
  dropFile,
8530
+ editorClassNames,
7756
8531
  editorInputMode,
7757
- editorSlots,
7758
- editorWidth,
7759
- editorWithToolbarLayout,
7760
8532
  extendedMarkdown,
8533
+ fader,
7761
8534
  filterChars,
7762
8535
  flattenRect,
7763
8536
  focus,
@@ -7793,6 +8566,7 @@ export {
7793
8566
  markdownTagsExtensions,
7794
8567
  matchCompletion,
7795
8568
  mention,
8569
+ mobileSlots,
7796
8570
  modalStateEffect,
7797
8571
  modalStateField,
7798
8572
  moveItemDown,
@@ -7812,6 +8586,7 @@ export {
7812
8586
  removeList,
7813
8587
  removeStyle,
7814
8588
  replacer,
8589
+ scrollPastEnd,
7815
8590
  scrollThreadIntoView,
7816
8591
  scrollToLine,
7817
8592
  scroller,
@@ -7824,9 +8599,8 @@ export {
7824
8599
  setSelection,
7825
8600
  setStyle,
7826
8601
  singleValueFacet,
7827
- stackItemContentEditorClassNames,
8602
+ snippets2 as snippets,
7828
8603
  staticCompletion,
7829
- streamer,
7830
8604
  submit,
7831
8605
  tabbable,
7832
8606
  table,
@@ -7845,7 +8619,12 @@ export {
7845
8619
  treeFacet,
7846
8620
  typeahead,
7847
8621
  typewriter,
8622
+ typewriterBypass,
8623
+ typewriterDrainingEffect,
7848
8624
  wrapWithCatch,
8625
+ xmlBlockDecoration,
8626
+ xmlElementLength,
8627
+ xmlFormatting,
7849
8628
  xmlTagContextEffect,
7850
8629
  xmlTagResetEffect,
7851
8630
  xmlTagUpdateEffect,