@dxos/ui-editor 0.8.4-main.69d29f4 → 0.8.4-main.74a063c4e0

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 (100) hide show
  1. package/dist/lib/browser/index.mjs +1465 -721
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +1465 -721
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/defaults.d.ts +3 -10
  8. package/dist/types/src/defaults.d.ts.map +1 -1
  9. package/dist/types/src/extensions/auto-scroll.d.ts +8 -0
  10. package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -0
  11. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  12. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  13. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  14. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  15. package/dist/types/src/extensions/index.d.ts +3 -2
  16. package/dist/types/src/extensions/index.d.ts.map +1 -1
  17. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  18. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -0
  19. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  20. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  21. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  22. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  23. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  24. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  25. package/dist/types/src/extensions/preview/preview.d.ts +3 -1
  26. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  27. package/dist/types/src/extensions/scroll-past-end.d.ts +3 -0
  28. package/dist/types/src/extensions/scroll-past-end.d.ts.map +1 -0
  29. package/dist/types/src/extensions/scroller.d.ts +63 -0
  30. package/dist/types/src/extensions/scroller.d.ts.map +1 -0
  31. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
  32. package/dist/types/src/extensions/tags/fader.d.ts +12 -0
  33. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
  34. package/dist/types/src/extensions/tags/index.d.ts +2 -1
  35. package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
  36. package/dist/types/src/extensions/tags/wire.d.ts +23 -0
  37. package/dist/types/src/extensions/tags/wire.d.ts.map +1 -0
  38. package/dist/types/src/extensions/tags/wire.test.d.ts +2 -0
  39. package/dist/types/src/extensions/tags/wire.test.d.ts.map +1 -0
  40. package/dist/types/src/extensions/tags/xml-tags.d.ts +28 -8
  41. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  42. package/dist/types/src/styles/index.d.ts +0 -2
  43. package/dist/types/src/styles/index.d.ts.map +1 -1
  44. package/dist/types/src/styles/theme.d.ts +15 -0
  45. package/dist/types/src/styles/theme.d.ts.map +1 -1
  46. package/dist/types/src/util/cursor.d.ts +1 -1
  47. package/dist/types/src/util/cursor.d.ts.map +1 -1
  48. package/dist/types/tsconfig.tsbuildinfo +1 -1
  49. package/package.json +32 -32
  50. package/src/defaults.ts +22 -20
  51. package/src/extensions/annotations.ts +1 -1
  52. package/src/extensions/auto-scroll.ts +179 -0
  53. package/src/extensions/automerge/automerge.test.tsx +2 -2
  54. package/src/extensions/automerge/automerge.ts +6 -6
  55. package/src/extensions/blocks.ts +5 -5
  56. package/src/extensions/comments.ts +5 -6
  57. package/src/extensions/dnd.ts +2 -2
  58. package/src/extensions/factories.ts +7 -9
  59. package/src/extensions/folding.ts +5 -22
  60. package/src/extensions/index.ts +3 -2
  61. package/src/extensions/markdown/action.ts +0 -1
  62. package/src/extensions/markdown/bundle.ts +23 -9
  63. package/src/extensions/markdown/decorate.ts +15 -12
  64. package/src/extensions/markdown/highlight.ts +15 -7
  65. package/src/extensions/markdown/link.ts +27 -33
  66. package/src/extensions/markdown/parser.test.ts +0 -1
  67. package/src/extensions/markdown/styles.ts +42 -9
  68. package/src/extensions/markdown/table.ts +24 -2
  69. package/src/extensions/outliner/outliner.test.ts +0 -1
  70. package/src/extensions/outliner/outliner.ts +3 -4
  71. package/src/extensions/outliner/tree.test.ts +0 -1
  72. package/src/extensions/preview/preview.ts +62 -15
  73. package/src/extensions/scroll-past-end.ts +32 -0
  74. package/src/extensions/scroller.ts +245 -0
  75. package/src/extensions/selection.ts +1 -1
  76. package/src/extensions/tags/extended-markdown.ts +73 -1
  77. package/src/extensions/tags/fader.ts +195 -0
  78. package/src/extensions/tags/index.ts +2 -1
  79. package/src/extensions/tags/wire.test.ts +65 -0
  80. package/src/extensions/tags/wire.ts +459 -0
  81. package/src/extensions/tags/xml-tags.ts +185 -34
  82. package/src/extensions/tags/xml-util.test.ts +111 -23
  83. package/src/styles/index.ts +0 -2
  84. package/src/styles/theme.ts +116 -34
  85. package/src/util/cursor.ts +1 -2
  86. package/dist/types/src/extensions/autoscroll.d.ts +0 -20
  87. package/dist/types/src/extensions/autoscroll.d.ts.map +0 -1
  88. package/dist/types/src/extensions/scrolling.d.ts +0 -78
  89. package/dist/types/src/extensions/scrolling.d.ts.map +0 -1
  90. package/dist/types/src/extensions/tags/streamer.d.ts +0 -12
  91. package/dist/types/src/extensions/tags/streamer.d.ts.map +0 -1
  92. package/dist/types/src/styles/markdown.d.ts +0 -8
  93. package/dist/types/src/styles/markdown.d.ts.map +0 -1
  94. package/dist/types/src/styles/tokens.d.ts +0 -3
  95. package/dist/types/src/styles/tokens.d.ts.map +0 -1
  96. package/src/extensions/autoscroll.ts +0 -165
  97. package/src/extensions/scrolling.ts +0 -189
  98. package/src/extensions/tags/streamer.ts +0 -243
  99. package/src/styles/markdown.ts +0 -26
  100. package/src/styles/tokens.ts +0 -17
@@ -6,24 +6,34 @@ import {
6
6
  } from "./chunk-HL3YF6WC.mjs";
7
7
 
8
8
  // src/index.ts
9
- import { EditorState as EditorState3 } from "@codemirror/state";
10
- import { EditorView as EditorView30, keymap as keymap15 } from "@codemirror/view";
9
+ import { EditorState as EditorState4 } from "@codemirror/state";
10
+ import { EditorView as EditorView32, keymap as keymap15 } from "@codemirror/view";
11
11
  import { tags as tags2 } from "@lezer/highlight";
12
12
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
13
13
 
14
14
  // src/defaults.ts
15
15
  import { mx } from "@dxos/ui-theme";
16
- var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
17
- var editorSlots = {
18
- scroll: {
19
- className: "pbs-2"
16
+ 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");
17
+ var documentSlots = {
18
+ content: {
19
+ /**
20
+ * CodeMirror content width.
21
+ * 40rem = 640px. Corresponds to initial plank width (Google docs, Stashpad, etc.)
22
+ * 50rem = 800px. Maximum content width for solo mode.
23
+ * NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).
24
+ */
25
+ className: "mx-auto! w-full pointer-fine:max-w-[min(50rem,100%-4rem)] pointer-coarse:max-w-[min(50rem,100%-2rem)]"
20
26
  },
27
+ scroll: {
28
+ // NOTE: Child widgets must have `max-w-[100cqi]`.
29
+ className: "dx-size-container"
30
+ }
31
+ };
32
+ var compactSlots = {
21
33
  content: {
22
- className: editorWidth
34
+ className: "mx-2! w-full"
23
35
  }
24
36
  };
25
- var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
26
- var stackItemContentEditorClassNames = (role) => mx("p-0.5 dx-focus-ring-inset attention-surface data-[toolbar=disabled]:pbs-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24" : "min-bs-0");
27
37
 
28
38
  // src/extensions/annotations.ts
29
39
  import { RangeSetBuilder } from "@codemirror/state";
@@ -58,7 +68,7 @@ var annotations = ({ match } = {}) => {
58
68
  ".cm-annotation": {
59
69
  textDecoration: "underline",
60
70
  textDecorationStyle: "wavy",
61
- textDecorationColor: "var(--dx-errorText)"
71
+ textDecorationColor: "var(--color-error-text)"
62
72
  }
63
73
  })
64
74
  ];
@@ -207,7 +217,7 @@ var singleValueFacet = (defaultValue) => Facet.define({
207
217
  var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
208
218
  var defaultCursorConverter = {
209
219
  toCursor: (position) => position.toString(),
210
- fromCursor: (cursor2) => parseInt(cursor2)
220
+ fromCursor: (cursor) => parseInt(cursor)
211
221
  };
212
222
  var Cursor = class _Cursor {
213
223
  static converter = singleValueFacet(defaultCursorConverter);
@@ -220,9 +230,9 @@ var Cursor = class _Cursor {
220
230
  to
221
231
  ].join(":");
222
232
  };
223
- static getRangeFromCursor = (state, cursor2) => {
233
+ static getRangeFromCursor = (state, cursor) => {
224
234
  const cursorConverter2 = state.facet(_Cursor.converter);
225
- const parts = cursor2.split(":");
235
+ const parts = cursor.split(":");
226
236
  const from = cursorConverter2.fromCursor(parts[0]);
227
237
  const to = cursorConverter2.fromCursor(parts[1]);
228
238
  return from !== void 0 && to !== void 0 ? {
@@ -501,228 +511,295 @@ var typeahead = ({ onComplete } = {}) => {
501
511
  ];
502
512
  };
503
513
 
504
- // src/extensions/autoscroll.ts
514
+ // src/extensions/auto-scroll.ts
505
515
  import { StateEffect as StateEffect2 } from "@codemirror/state";
506
516
  import { EditorView as EditorView5, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
507
- import { debounce } from "@dxos/async";
517
+ import { addEventListener, combine, throttle } from "@dxos/async";
508
518
  import { Domino } from "@dxos/ui";
519
+ import { getSize } from "@dxos/ui-theme";
509
520
 
510
- // src/extensions/scrolling.ts
521
+ // src/extensions/scroller.ts
511
522
  import { StateEffect } from "@codemirror/state";
512
523
  import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
513
- var scrollToLineEffect = StateEffect.define();
514
- var smoothScroll = ({ offset = 0, position = "start" } = {}) => {
515
- const scrollPlugin = ViewPlugin5.fromClass(class SmoothScrollPlugin {
524
+ import { log as log2 } from "@dxos/log";
525
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scroller.ts";
526
+ var scrollerLineEffect = StateEffect.define();
527
+ var scrollerCrawlEffect = StateEffect.define();
528
+ var scrollToLine = (view, options) => {
529
+ view.dispatch({
530
+ effects: scrollerLineEffect.of(options)
531
+ });
532
+ };
533
+ var scroller = ({ overScroll = 0 } = {}) => {
534
+ const scrollPlugin = ViewPlugin5.fromClass(class ScrollerPlugin {
516
535
  view;
536
+ crawler;
517
537
  constructor(view) {
518
538
  this.view = view;
539
+ this.crawler = createCrawler(this.view);
519
540
  }
520
541
  // No-op.
521
542
  destroy() {
543
+ this.crawler.cancel();
522
544
  }
523
- /**
524
- * Perform smooth scroll to the specified line.
525
- */
526
- scrollToLine(lineNumber, options) {
527
- const { offset: animOffset = 0, position: animPosition, behavior } = options;
528
- const doc = this.view.state.doc;
529
- const scroller = this.view.scrollDOM;
530
- const targetLine = Math.max(0, lineNumber - 1);
531
- if (behavior === "instant") {
532
- requestAnimationFrame(() => {
533
- this.view.dispatch({
534
- selection: {
535
- anchor: doc.line(targetLine + 1).from
536
- },
537
- scrollIntoView: true
538
- });
539
- });
540
- return;
541
- }
542
- if (targetLine >= doc.lines) {
543
- const targetScrollTop2 = scroller.scrollHeight - scroller.clientHeight + (animOffset || 0);
544
- this.animateScroll(scroller, targetScrollTop2);
545
- return;
546
- }
547
- const lineStart = doc.line(targetLine + 1).from;
548
- const coords = this.view.coordsAtPos(lineStart);
549
- if (!coords) {
550
- return;
551
- }
552
- const currentScrollTop = scroller.scrollTop;
553
- const scrollerRect = scroller.getBoundingClientRect();
554
- const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
555
- let targetScrollTop;
556
- if (animPosition === "end") {
557
- targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + animOffset;
545
+ cancel() {
546
+ this.crawler.cancel();
547
+ }
548
+ crawl(start = false) {
549
+ if (start) {
550
+ this.crawler.scroll();
558
551
  } else {
559
- targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + animOffset;
552
+ this.crawler.cancel();
560
553
  }
561
- const clampedScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
562
- this.animateScroll(scroller, clampedScrollTop);
563
554
  }
564
- /**
565
- * Animate scroll using browser's built-in smooth scrolling.
566
- */
567
- animateScroll(element, targetScrollTop) {
568
- if (Math.abs(targetScrollTop - element.scrollTop) < 1) {
569
- return;
555
+ scroll({ line, offset = 0, position, behavior = "instant" }) {
556
+ const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
557
+ const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
558
+ const doc = this.view.state.doc;
559
+ let targetScrollTop = scrollHeight - clientHeight + offset;
560
+ if (line >= 0 && line <= doc.lines - 1) {
561
+ const lineStart = doc.line(line + 1).from;
562
+ const coords = this.view.coordsAtPos(lineStart);
563
+ if (coords) {
564
+ const currentScrollTop = scrollTop;
565
+ const maxScrollTop = scrollHeight - clientHeight;
566
+ if (position === "end") {
567
+ targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
568
+ } else {
569
+ targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
570
+ }
571
+ targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
572
+ }
570
573
  }
571
- element.scrollTo({
572
- top: targetScrollTop,
573
- behavior: "smooth"
574
+ requestAnimationFrame(() => {
575
+ this.view.scrollDOM.scrollTo({
576
+ top: targetScrollTop
577
+ });
574
578
  });
575
579
  }
576
580
  });
577
581
  return [
578
582
  scrollPlugin,
579
- // Update listener to handle scroll effects.
583
+ // Listen for effect.s
580
584
  EditorView4.updateListener.of((update2) => {
581
585
  update2.transactions.forEach((transaction) => {
582
- for (const effect of transaction.effects) {
583
- if (effect.is(scrollToLineEffect)) {
584
- const { line, options = {} } = effect.value;
585
- const plugin = update2.view.plugin(scrollPlugin);
586
- if (plugin) {
587
- plugin.scrollToLine(line, {
588
- offset,
589
- position,
590
- ...options
591
- });
586
+ try {
587
+ const plugin = update2.view.plugin(scrollPlugin);
588
+ if (plugin) {
589
+ for (const effect of transaction.effects) {
590
+ if (effect.is(scrollerCrawlEffect)) {
591
+ plugin.crawl(effect.value);
592
+ } else if (effect.is(scrollerLineEffect)) {
593
+ plugin.scroll(effect.value);
594
+ }
592
595
  }
593
596
  }
597
+ } catch (err) {
598
+ log2.catch(err, void 0, {
599
+ F: __dxlog_file2,
600
+ L: 146,
601
+ S: void 0,
602
+ C: (f, a) => f(...a)
603
+ });
594
604
  }
595
605
  });
606
+ }),
607
+ // Styles.
608
+ EditorView4.theme({
609
+ ".cm-content": {
610
+ paddingBottom: `${overScroll}px`
611
+ },
612
+ ".cm-scroller": {
613
+ overflowY: "scroll",
614
+ overflowAnchor: "none",
615
+ paddingBottom: "0"
616
+ },
617
+ ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
618
+ display: "none"
619
+ },
620
+ ".cm-scroller::-webkit-scrollbar-thumb": {
621
+ background: "transparent",
622
+ transition: "background 0.15s"
623
+ },
624
+ "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
625
+ background: "var(--color-scrollbar-thumb)"
626
+ },
627
+ ".cm-scroll-button": {
628
+ position: "absolute",
629
+ bottom: "0.5rem",
630
+ right: "1rem"
631
+ }
596
632
  })
597
633
  ];
598
634
  };
599
- var scrollToLine = (view, line, options) => {
600
- view.dispatch({
601
- effects: scrollToLineEffect.of({
602
- line,
603
- options
604
- })
605
- });
606
- };
635
+ function createCrawler(view, accel = 0.15, maxVelocity = 1, snapThreshold = 0.5) {
636
+ const el = view.scrollDOM;
637
+ let currentTop = 0;
638
+ let velocity = 0;
639
+ let rafId = null;
640
+ function frame() {
641
+ const targetTop = el.scrollHeight - el.clientHeight;
642
+ const delta = targetTop - currentTop;
643
+ const absDelta = Math.abs(delta);
644
+ if (absDelta < snapThreshold && Math.abs(velocity) < snapThreshold) {
645
+ el.scrollTop = targetTop;
646
+ currentTop = targetTop;
647
+ velocity = 0;
648
+ rafId = null;
649
+ return;
650
+ }
651
+ const stoppingDistance = velocity * velocity / (2 * accel);
652
+ const direction = Math.sign(delta);
653
+ if (velocity !== 0 && (absDelta <= stoppingDistance || direction !== Math.sign(velocity))) {
654
+ velocity -= Math.sign(velocity) * Math.min(accel, Math.abs(velocity));
655
+ } else {
656
+ velocity += direction * accel;
657
+ velocity = Math.sign(velocity) * Math.min(Math.abs(velocity), maxVelocity);
658
+ }
659
+ currentTop += velocity;
660
+ el.scrollTop = currentTop;
661
+ rafId = requestAnimationFrame(frame);
662
+ }
663
+ return {
664
+ scroll: () => {
665
+ if (rafId === null) {
666
+ currentTop = el.scrollTop;
667
+ rafId = requestAnimationFrame(frame);
668
+ }
669
+ },
670
+ cancel: () => {
671
+ if (rafId !== null) {
672
+ cancelAnimationFrame(rafId);
673
+ rafId = null;
674
+ velocity = 0;
675
+ }
676
+ }
677
+ };
678
+ }
607
679
 
608
- // src/extensions/autoscroll.ts
609
- var scrollToBottomEffect = StateEffect2.define();
610
- var autoScroll = ({ autoScroll: autoScroll2 = true, threshold = 100, throttleDelay = 1e3, onAutoScroll } = {}) => {
680
+ // src/extensions/auto-scroll.ts
681
+ var autoScrollEffect = StateEffect2.define();
682
+ var autoScroll = (_ = {}) => {
611
683
  let buttonContainer;
612
- let hideTimeout;
613
- let lastScrollTop = 0;
614
684
  let isPinned = true;
615
- const setPinned = (pin) => {
616
- isPinned = pin;
617
- buttonContainer?.classList.toggle("opacity-0", pin);
618
- };
619
- const hideScrollbar = (view) => {
620
- view.scrollDOM.classList.add("cm-hide-scrollbar");
621
- clearTimeout(hideTimeout);
622
- hideTimeout = setTimeout(() => {
623
- view.scrollDOM.classList.remove("cm-hide-scrollbar");
624
- }, 1e3);
625
- };
626
- const scrollToBottom = (view, behavior) => {
627
- setPinned(true);
628
- hideScrollbar(view);
629
- const line = view.state.doc.lineAt(view.state.doc.length);
630
- view.dispatch({
631
- selection: {
632
- anchor: line.to,
633
- head: line.to
634
- },
635
- effects: scrollToLineEffect.of({
636
- line: line.number,
637
- options: {
638
- position: "end",
639
- offset: threshold,
640
- behavior
641
- }
642
- })
643
- });
685
+ let jumpPending = false;
686
+ let enabled = true;
687
+ let firstUpdate = true;
688
+ const setPinned = (pinned) => {
689
+ buttonContainer?.classList.toggle("opacity-0", pinned);
690
+ isPinned = pinned;
644
691
  };
645
- const checkDistance = debounce((view) => {
646
- const scrollerRect = view.scrollDOM.getBoundingClientRect();
647
- const coords = view.coordsAtPos(view.state.doc.length);
648
- const distanceFromBottom = coords ? coords.bottom - scrollerRect.bottom : 0;
649
- setPinned(distanceFromBottom < 0);
650
- }, 1e3);
651
- const triggerUpdate = debounce((view) => scrollToBottom(view), throttleDelay);
652
692
  return [
653
- // Update listener for logging when scrolling is needed.
654
- EditorView5.updateListener.of(({ view, transactions, heightChanged }) => {
655
- transactions.forEach((transaction) => {
656
- for (const effect of transaction.effects) {
657
- if (effect.is(scrollToBottomEffect)) {
658
- scrollToBottom(view, effect.value);
693
+ // Update listener for scrolling when content changes.
694
+ EditorView5.updateListener.of((update2) => {
695
+ const { view, heightChanged, state, startState } = update2;
696
+ for (const tr of update2.transactions) {
697
+ for (const effect of tr.effects) {
698
+ if (effect.is(autoScrollEffect)) {
699
+ enabled = effect.value;
700
+ if (enabled) {
701
+ setPinned(true);
702
+ view.dispatch({
703
+ effects: scrollerCrawlEffect.of(true)
704
+ });
705
+ } else {
706
+ view.dispatch({
707
+ effects: scrollerCrawlEffect.of(false)
708
+ });
709
+ }
659
710
  }
660
711
  }
661
- });
662
- if (heightChanged && isPinned) {
663
- const coords = view.coordsAtPos(view.state.doc.length);
664
- const scrollerRect = view.scrollDOM.getBoundingClientRect();
665
- const distanceFromBottom = coords ? scrollerRect.bottom - coords.bottom : 0;
666
- if (autoScroll2 && distanceFromBottom < threshold) {
667
- const shouldScroll = onAutoScroll?.({
668
- view,
669
- distanceFromBottom
670
- }) ?? true;
671
- if (shouldScroll) {
672
- triggerUpdate(view);
712
+ }
713
+ if (!enabled) {
714
+ return;
715
+ }
716
+ if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
717
+ firstUpdate = false;
718
+ jumpPending = true;
719
+ requestAnimationFrame(() => {
720
+ view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
721
+ jumpPending = false;
722
+ });
723
+ return;
724
+ }
725
+ firstUpdate = false;
726
+ if (jumpPending) {
727
+ return;
728
+ }
729
+ if (heightChanged) {
730
+ if (isPinned) {
731
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
732
+ const delta = scrollHeight - scrollTop - clientHeight;
733
+ if (delta > 0) {
734
+ setPinned(true);
735
+ view.dispatch({
736
+ effects: scrollerCrawlEffect.of(true)
737
+ });
738
+ } else if (delta < -1) {
739
+ setPinned(false);
740
+ }
741
+ } else {
742
+ if (state.doc.length === 0) {
743
+ setPinned(true);
673
744
  }
674
- } else if (distanceFromBottom < 0) {
675
- setPinned(false);
676
745
  }
677
746
  }
678
747
  }),
679
- // Detect user scroll.
680
- EditorView5.domEventHandlers({
681
- scroll: (event, view) => {
682
- const currentScrollTop = view.scrollDOM.scrollTop;
683
- const scrollingUp = currentScrollTop < lastScrollTop;
684
- lastScrollTop = currentScrollTop;
685
- if (scrollingUp) {
686
- setPinned(false);
687
- } else {
688
- checkDistance(view);
689
- }
748
+ // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
749
+ ViewPlugin6.fromClass(class {
750
+ cleanup;
751
+ constructor(view) {
752
+ this.cleanup = createUserScrollDetector(view.scrollDOM, throttle(() => {
753
+ requestAnimationFrame(() => {
754
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
755
+ const delta = scrollHeight - scrollTop - clientHeight;
756
+ const pinned = delta === 0;
757
+ setPinned(pinned);
758
+ if (!pinned) {
759
+ view.dispatch({
760
+ effects: scrollerCrawlEffect.of(false)
761
+ });
762
+ }
763
+ });
764
+ }, 500));
765
+ }
766
+ destroy() {
767
+ this.cleanup();
690
768
  }
691
769
  }),
692
770
  // Scroll button.
693
771
  ViewPlugin6.fromClass(class {
694
772
  constructor(view) {
695
- const icon = Domino.of("dx-icon").attributes({
773
+ const icon = Domino.of("dx-icon").classNames(getSize(4)).attributes({
696
774
  icon: "ph--arrow-down--regular"
697
775
  });
698
- const button = Domino.of("button").classNames("dx-button bg-accentSurface").attributes({
776
+ const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
699
777
  "data-density": "fine"
700
- }).children(icon).on("click", () => {
701
- scrollToBottom(view);
778
+ }).append(icon).on("click", () => {
779
+ setPinned(true);
780
+ view.dispatch({
781
+ effects: scrollerLineEffect.of({
782
+ line: -1,
783
+ position: "end",
784
+ behavior: "smooth"
785
+ })
786
+ });
702
787
  });
703
- buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").children(button).root;
788
+ buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").append(button).root;
704
789
  view.scrollDOM.parentElement.appendChild(buttonContainer);
705
790
  }
706
- }),
707
- // Styles.
708
- EditorView5.theme({
709
- ".cm-scroller": {
710
- scrollbarWidth: "thin"
711
- },
712
- ".cm-scroller.cm-hide-scrollbar": {
713
- scrollbarWidth: "none"
714
- },
715
- ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
716
- display: "none"
717
- },
718
- ".cm-scroll-button": {
719
- position: "absolute",
720
- bottom: "0.5rem",
721
- right: "1rem"
722
- }
723
791
  })
724
792
  ];
725
793
  };
794
+ function createUserScrollDetector(element, onUserScroll) {
795
+ return combine(addEventListener(element, "wheel", () => onUserScroll(), {
796
+ passive: true
797
+ }), addEventListener(element, "pointerdown", (event) => {
798
+ if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
799
+ onUserScroll();
800
+ }
801
+ }));
802
+ }
726
803
 
727
804
  // src/extensions/automerge/automerge.ts
728
805
  import { next as A3 } from "@automerge/automerge";
@@ -736,15 +813,15 @@ var initialSync = Transaction.userEvent.of("initial.sync");
736
813
 
737
814
  // src/extensions/automerge/cursor.ts
738
815
  import { fromCursor, toCursor } from "@dxos/echo-db";
739
- import { log as log2 } from "@dxos/log";
740
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
816
+ import { log as log3 } from "@dxos/log";
817
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
741
818
  var cursorConverter = (accessor) => ({
742
819
  toCursor: (pos, assoc) => {
743
820
  try {
744
821
  return toCursor(accessor, pos, assoc);
745
822
  } catch (err) {
746
- log2.catch(err, void 0, {
747
- F: __dxlog_file2,
823
+ log3.catch(err, void 0, {
824
+ F: __dxlog_file3,
748
825
  L: 15,
749
826
  S: void 0,
750
827
  C: (f, a) => f(...a)
@@ -752,12 +829,12 @@ var cursorConverter = (accessor) => ({
752
829
  return "";
753
830
  }
754
831
  },
755
- fromCursor: (cursor2) => {
832
+ fromCursor: (cursor) => {
756
833
  try {
757
- return fromCursor(accessor, cursor2);
834
+ return fromCursor(accessor, cursor);
758
835
  } catch (err) {
759
- log2.catch(err, void 0, {
760
- F: __dxlog_file2,
836
+ log3.catch(err, void 0, {
837
+ F: __dxlog_file3,
761
838
  L: 24,
762
839
  S: void 0,
763
840
  C: (f, a) => f(...a)
@@ -782,7 +859,7 @@ var isReconcile = (tr) => {
782
859
 
783
860
  // src/extensions/automerge/sync.ts
784
861
  import { next as A2 } from "@automerge/automerge";
785
- import { log as log3 } from "@dxos/log";
862
+ import { log as log4 } from "@dxos/log";
786
863
 
787
864
  // src/extensions/automerge/update-automerge.ts
788
865
  import { next as A } from "@automerge/automerge";
@@ -923,7 +1000,7 @@ var charPath = (textPath, candidatePath) => {
923
1000
  };
924
1001
 
925
1002
  // src/extensions/automerge/sync.ts
926
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/sync.ts";
1003
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/sync.ts";
927
1004
  var Syncer = class {
928
1005
  _handle;
929
1006
  _state;
@@ -946,8 +1023,8 @@ var Syncer = class {
946
1023
  this._pending = false;
947
1024
  }
948
1025
  onEditorChange(view) {
949
- log3("onEditorChange", void 0, {
950
- F: __dxlog_file3,
1026
+ log4("onEditorChange", void 0, {
1027
+ F: __dxlog_file4,
951
1028
  L: 45,
952
1029
  S: this,
953
1030
  C: (f, a) => f(...a)
@@ -962,8 +1039,8 @@ var Syncer = class {
962
1039
  }
963
1040
  }
964
1041
  onAutomergeChange(view) {
965
- log3("onAutomergeChange", void 0, {
966
- F: __dxlog_file3,
1042
+ log4("onAutomergeChange", void 0, {
1043
+ F: __dxlog_file4,
967
1044
  L: 60,
968
1045
  S: this,
969
1046
  C: (f, a) => f(...a)
@@ -1029,6 +1106,15 @@ var automerge = (accessor) => {
1029
1106
  const value = DocAccessor.getValue(accessor);
1030
1107
  const current = this._view.state.doc.toString();
1031
1108
  if (value !== current) {
1109
+ console.warn("ENABLING INITIAL SYNC -- THIS MAY BE A REGRESSION");
1110
+ this._view.dispatch({
1111
+ changes: {
1112
+ from: 0,
1113
+ to: this._view.state.doc.length,
1114
+ insert: value
1115
+ },
1116
+ annotations: initialSync
1117
+ });
1032
1118
  }
1033
1119
  });
1034
1120
  }
@@ -1056,7 +1142,7 @@ import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1056
1142
  import { Decoration as Decoration5, EditorView as EditorView7, ViewPlugin as ViewPlugin8, WidgetType as WidgetType3 } from "@codemirror/view";
1057
1143
  import { Event } from "@dxos/async";
1058
1144
  import { Context } from "@dxos/context";
1059
- var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness.ts";
1145
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness.ts";
1060
1146
  var dummyProvider = {
1061
1147
  remoteStateChange: new Event(),
1062
1148
  open: () => {
@@ -1080,7 +1166,7 @@ var awareness = (provider = dummyProvider) => {
1080
1166
  };
1081
1167
  var RemoteSelectionsDecorator = class {
1082
1168
  _ctx = new Context(void 0, {
1083
- F: __dxlog_file4,
1169
+ F: __dxlog_file5,
1084
1170
  L: 80
1085
1171
  });
1086
1172
  _cursorConverter;
@@ -1293,8 +1379,8 @@ var styles = EditorView7.theme({
1293
1379
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
1294
1380
  import { Context as Context2 } from "@dxos/context";
1295
1381
  import { invariant } from "@dxos/invariant";
1296
- import { log as log4 } from "@dxos/log";
1297
- var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness-provider.ts";
1382
+ import { log as log5 } from "@dxos/log";
1383
+ var __dxlog_file6 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness-provider.ts";
1298
1384
  var DEBOUNCE_INTERVAL = 100;
1299
1385
  var SpaceAwarenessProvider = class {
1300
1386
  _remoteStates = /* @__PURE__ */ new Map();
@@ -1314,7 +1400,7 @@ var SpaceAwarenessProvider = class {
1314
1400
  }
1315
1401
  open() {
1316
1402
  this._ctx = new Context2(void 0, {
1317
- F: __dxlog_file5,
1403
+ F: __dxlog_file6,
1318
1404
  L: 57
1319
1405
  });
1320
1406
  this._postTask = new DeferredTask(this._ctx, async () => {
@@ -1341,10 +1427,10 @@ var SpaceAwarenessProvider = class {
1341
1427
  void this._messenger.postMessage(this._channel, {
1342
1428
  kind: "query"
1343
1429
  }).catch((err) => {
1344
- log4.debug("failed to query awareness", {
1430
+ log5.debug("failed to query awareness", {
1345
1431
  err
1346
1432
  }, {
1347
- F: __dxlog_file5,
1433
+ F: __dxlog_file6,
1348
1434
  L: 91,
1349
1435
  S: this,
1350
1436
  C: (f, a) => f(...a)
@@ -1361,7 +1447,7 @@ var SpaceAwarenessProvider = class {
1361
1447
  }
1362
1448
  update(position) {
1363
1449
  invariant(this._postTask, void 0, {
1364
- F: __dxlog_file5,
1450
+ F: __dxlog_file6,
1365
1451
  L: 106,
1366
1452
  S: this,
1367
1453
  A: [
@@ -1378,7 +1464,7 @@ var SpaceAwarenessProvider = class {
1378
1464
  }
1379
1465
  _handleQueryMessage() {
1380
1466
  invariant(this._postTask, void 0, {
1381
- F: __dxlog_file5,
1467
+ F: __dxlog_file6,
1382
1468
  L: 117,
1383
1469
  S: this,
1384
1470
  A: [
@@ -1390,7 +1476,7 @@ var SpaceAwarenessProvider = class {
1390
1476
  }
1391
1477
  _handlePostMessage(message) {
1392
1478
  invariant(message.kind === "post", void 0, {
1393
- F: __dxlog_file5,
1479
+ F: __dxlog_file6,
1394
1480
  L: 122,
1395
1481
  S: this,
1396
1482
  A: [
@@ -1406,9 +1492,9 @@ var SpaceAwarenessProvider = class {
1406
1492
  // src/extensions/blast.ts
1407
1493
  import { EditorView as EditorView8, keymap as keymap3 } from "@codemirror/view";
1408
1494
  import defaultsDeep from "lodash.defaultsdeep";
1409
- import { throttle } from "@dxos/async";
1495
+ import { throttle as throttle2 } from "@dxos/async";
1410
1496
  import { invariant as invariant2 } from "@dxos/invariant";
1411
- var __dxlog_file6 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/blast.ts";
1497
+ var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/blast.ts";
1412
1498
  var defaultOptions = {
1413
1499
  effect: 2,
1414
1500
  maxParticles: 200,
@@ -1527,7 +1613,7 @@ var Blaster = class {
1527
1613
  }
1528
1614
  initialize() {
1529
1615
  invariant2(!this._canvas && !this._ctx, void 0, {
1530
- F: __dxlog_file6,
1616
+ F: __dxlog_file7,
1531
1617
  L: 142,
1532
1618
  S: this,
1533
1619
  A: [
@@ -1564,7 +1650,7 @@ var Blaster = class {
1564
1650
  }
1565
1651
  start() {
1566
1652
  invariant2(this._canvas && this._ctx, void 0, {
1567
- F: __dxlog_file6,
1653
+ F: __dxlog_file7,
1568
1654
  L: 181,
1569
1655
  S: this,
1570
1656
  A: [
@@ -1598,11 +1684,11 @@ var Blaster = class {
1598
1684
  this.drawParticles();
1599
1685
  requestAnimationFrame(this.loop.bind(this));
1600
1686
  }
1601
- shake = throttle(({ time }) => {
1687
+ shake = throttle2(({ time }) => {
1602
1688
  this._shakeTime = this._shakeTimeMax || time;
1603
1689
  this._shakeTimeMax = time;
1604
1690
  }, 100);
1605
- spawn = throttle(({ element, point }) => {
1691
+ spawn = throttle2(({ element, point }) => {
1606
1692
  const color = getRGBComponents(element, this._options.color);
1607
1693
  const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
1608
1694
  const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
@@ -1776,11 +1862,11 @@ var blocks = () => [
1776
1862
  ".cm-line.block-line": {
1777
1863
  paddingLeft: "0.75rem",
1778
1864
  paddingRight: "0.75rem",
1779
- borderLeft: "1px solid var(--dx-subduedSeparator)",
1780
- borderRight: "1px solid var(--dx-subduedSeparator)"
1865
+ borderLeft: "1px solid var(--color-subdued-separator)",
1866
+ borderRight: "1px solid var(--color-subdued-separator)"
1781
1867
  },
1782
1868
  ".cm-line.block-single": {
1783
- border: "1px solid var(--dx-subduedSeparator)",
1869
+ border: "1px solid var(--color-subdued-separator)",
1784
1870
  borderRadius: "6px",
1785
1871
  paddingTop: "0.5rem",
1786
1872
  paddingBottom: "0.5rem",
@@ -1788,7 +1874,7 @@ var blocks = () => [
1788
1874
  marginBottom: "0.5rem"
1789
1875
  },
1790
1876
  ".cm-line.block-first": {
1791
- borderTop: "1px solid var(--dx-subduedSeparator)",
1877
+ borderTop: "1px solid var(--color-subdued-separator)",
1792
1878
  borderTopLeftRadius: "6px",
1793
1879
  borderTopRightRadius: "6px",
1794
1880
  paddingTop: "0.5rem",
@@ -1796,7 +1882,7 @@ var blocks = () => [
1796
1882
  },
1797
1883
  ".cm-line.block-middle": {},
1798
1884
  ".cm-line.block-last": {
1799
- borderBottom: "1px solid var(--dx-subduedSeparator)",
1885
+ borderBottom: "1px solid var(--color-subdued-separator)",
1800
1886
  borderBottomLeftRadius: "6px",
1801
1887
  borderBottomRightRadius: "6px",
1802
1888
  paddingBottom: "0.5rem",
@@ -1808,8 +1894,8 @@ var blocks = () => [
1808
1894
  // src/extensions/bookmarks.ts
1809
1895
  import { Prec as Prec3, StateEffect as StateEffect4, StateField as StateField2 } from "@codemirror/state";
1810
1896
  import { keymap as keymap4 } from "@codemirror/view";
1811
- import { log as log5 } from "@dxos/log";
1812
- var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1897
+ import { log as log6 } from "@dxos/log";
1898
+ var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1813
1899
  var addBookmark = StateEffect4.define();
1814
1900
  var removeBookmark = StateEffect4.define();
1815
1901
  var clearBookmarks = StateEffect4.define();
@@ -1821,8 +1907,8 @@ var bookmarks = () => {
1821
1907
  key: "Mod-ArrowUp",
1822
1908
  run: (view) => {
1823
1909
  const bookmarks2 = view.state.field(bookmarksField);
1824
- log5("up", bookmarks2, {
1825
- F: __dxlog_file7,
1910
+ log6("up", bookmarks2, {
1911
+ F: __dxlog_file8,
1826
1912
  L: 29,
1827
1913
  S: void 0,
1828
1914
  C: (f, a) => f(...a)
@@ -1834,8 +1920,8 @@ var bookmarks = () => {
1834
1920
  key: "Mod-ArrowDown",
1835
1921
  run: (view) => {
1836
1922
  const bookmarks2 = view.state.field(bookmarksField);
1837
- log5("down", bookmarks2, {
1838
- F: __dxlog_file7,
1923
+ log6("down", bookmarks2, {
1924
+ F: __dxlog_file8,
1839
1925
  L: 37,
1840
1926
  S: void 0,
1841
1927
  C: (f, a) => f(...a)
@@ -1879,19 +1965,19 @@ import { invertedEffects } from "@codemirror/commands";
1879
1965
  import { StateEffect as StateEffect5, StateField as StateField3 } from "@codemirror/state";
1880
1966
  import { Decoration as Decoration7, EditorView as EditorView11, ViewPlugin as ViewPlugin10, hoverTooltip, keymap as keymap6 } from "@codemirror/view";
1881
1967
  import sortBy from "lodash.sortby";
1882
- import { debounce as debounce3 } from "@dxos/async";
1883
- import { log as log6 } from "@dxos/log";
1968
+ import { debounce as debounce2 } from "@dxos/async";
1969
+ import { log as log7 } from "@dxos/log";
1884
1970
  import { isNonNullable } from "@dxos/util";
1885
1971
 
1886
1972
  // src/extensions/selection.ts
1887
1973
  import { Transaction as Transaction3 } from "@codemirror/state";
1888
1974
  import { EditorView as EditorView10, keymap as keymap5 } from "@codemirror/view";
1889
- import { debounce as debounce2 } from "@dxos/async";
1975
+ import { debounce } from "@dxos/async";
1890
1976
  import { invariant as invariant3 } from "@dxos/invariant";
1891
1977
  import { isTruthy } from "@dxos/util";
1892
- var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/selection.ts";
1978
+ var __dxlog_file9 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/selection.ts";
1893
1979
  var documentId = singleValueFacet();
1894
- var stateRestoreAnnotation = "dxos.org/cm/state-restore";
1980
+ var stateRestoreAnnotation = "org.dxos.cm.state-restore";
1895
1981
  var createEditorStateTransaction = ({ scrollTo, selection }) => {
1896
1982
  return {
1897
1983
  selection,
@@ -1905,7 +1991,7 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1905
1991
  var createEditorStateStore = (keyPrefix) => ({
1906
1992
  getState: (id) => {
1907
1993
  invariant3(id, void 0, {
1908
- F: __dxlog_file8,
1994
+ F: __dxlog_file9,
1909
1995
  L: 47,
1910
1996
  S: void 0,
1911
1997
  A: [
@@ -1918,7 +2004,7 @@ var createEditorStateStore = (keyPrefix) => ({
1918
2004
  },
1919
2005
  setState: (id, state) => {
1920
2006
  invariant3(id, void 0, {
1921
- F: __dxlog_file8,
2007
+ F: __dxlog_file9,
1922
2008
  L: 53,
1923
2009
  S: void 0,
1924
2010
  A: [
@@ -1930,7 +2016,7 @@ var createEditorStateStore = (keyPrefix) => ({
1930
2016
  }
1931
2017
  });
1932
2018
  var selectionState = ({ getState, setState } = {}) => {
1933
- const setStateDebounced = debounce2(setState, 1e3);
2019
+ const setStateDebounced = debounce(setState, 1e3);
1934
2020
  return [
1935
2021
  // TODO(burdon): Track scrolling (currently only updates when cursor moves).
1936
2022
  // EditorView.domEventHandlers({
@@ -1977,7 +2063,7 @@ var selectionState = ({ getState, setState } = {}) => {
1977
2063
  };
1978
2064
 
1979
2065
  // src/extensions/comments.ts
1980
- var __dxlog_file9 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
2066
+ var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
1981
2067
  var setComments = StateEffect5.define();
1982
2068
  var setSelection = StateEffect5.define();
1983
2069
  var setCommentState = StateEffect5.define();
@@ -2022,14 +2108,14 @@ var commentsState = StateField3.define({
2022
2108
  var styles2 = EditorView11.theme({
2023
2109
  ".cm-comment, .cm-comment-current": {
2024
2110
  padding: "3px 0",
2025
- color: "var(--dx-cmCommentText)",
2026
- backgroundColor: "var(--dx-cmCommentSurface)"
2111
+ color: "var(--color-cm-comment-text)",
2112
+ backgroundColor: "var(--color-cm-comment-surface)"
2027
2113
  },
2028
2114
  ".cm-comment > span, .cm-comment-current > span": {
2029
2115
  boxDecorationBreak: "clone",
2030
- boxShadow: "0 0 1px 3px var(--dx-cmCommentSurface)",
2031
- backgroundColor: "var(--dx-cmCommentSurface)",
2032
- color: "var(--dx-cmCommentText)",
2116
+ boxShadow: "0 0 1px 3px var(--color-cm-comment-surface)",
2117
+ backgroundColor: "var(--color-cm-comment-surface)",
2118
+ color: "var(--color-cm-comment-text)",
2033
2119
  cursor: "pointer"
2034
2120
  }
2035
2121
  });
@@ -2047,9 +2133,9 @@ var commentsDecorations = EditorView11.decorations.compute([
2047
2133
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2048
2134
  const range = comment.range;
2049
2135
  if (!range) {
2050
- log6.warn("Invalid range:", range, {
2051
- F: __dxlog_file9,
2052
- L: 140,
2136
+ log7.warn("Invalid range:", range, {
2137
+ F: __dxlog_file10,
2138
+ L: 139,
2053
2139
  S: void 0,
2054
2140
  C: (f, a) => f(...a)
2055
2141
  });
@@ -2165,10 +2251,10 @@ var trackPastedComments = (onUpdate) => {
2165
2251
  const { comments: comments2 } = update2.startState.field(commentsState);
2166
2252
  const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
2167
2253
  if (!exists) {
2168
- const cursor2 = Cursor.getCursorFromRange(update2.state, comment);
2254
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
2169
2255
  onUpdate({
2170
2256
  id: comment.id,
2171
- cursor: cursor2
2257
+ cursor
2172
2258
  });
2173
2259
  }
2174
2260
  }
@@ -2197,13 +2283,13 @@ var createComment = (view) => {
2197
2283
  }
2198
2284
  });
2199
2285
  }
2200
- const cursor2 = Cursor.getCursorFromRange(view.state, {
2286
+ const cursor = Cursor.getCursorFromRange(view.state, {
2201
2287
  from,
2202
2288
  to
2203
2289
  });
2204
- if (cursor2) {
2290
+ if (cursor) {
2205
2291
  options.onCreate?.({
2206
- cursor: cursor2,
2292
+ cursor,
2207
2293
  from,
2208
2294
  location: view.coordsAtPos(from)
2209
2295
  });
@@ -2214,7 +2300,7 @@ var createComment = (view) => {
2214
2300
  var optionsFacet = singleValueFacet();
2215
2301
  var comments = (options = {}) => {
2216
2302
  const { key: shortcut = "meta-'" } = options;
2217
- const handleSelect = debounce3((state) => options.onSelect?.(state), 200);
2303
+ const handleSelect = debounce2((state) => options.onSelect?.(state), 200);
2218
2304
  return [
2219
2305
  optionsFacet.of(options),
2220
2306
  options.id ? documentId.of(options.id) : void 0,
@@ -2392,9 +2478,9 @@ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin10.fro
2392
2478
  // src/extensions/debug.ts
2393
2479
  import { syntaxTree } from "@codemirror/language";
2394
2480
  import { StateField as StateField4 } from "@codemirror/state";
2395
- var debugNodeLogger = (log11 = console.log) => {
2481
+ var debugNodeLogger = (log12 = console.log) => {
2396
2482
  const logTokens = (state) => syntaxTree(state).iterate({
2397
- enter: (node) => log11(node.type)
2483
+ enter: (node) => log12(node.type)
2398
2484
  });
2399
2485
  return StateField4.define({
2400
2486
  create: (state) => logTokens(state),
@@ -2429,8 +2515,8 @@ var dropFile = (options = {}) => {
2429
2515
  };
2430
2516
  var styles3 = EditorView12.theme({
2431
2517
  ".cm-dropCursor": {
2432
- borderLeft: "2px solid var(--dx-accentText)",
2433
- color: "var(--dx-accentText)",
2518
+ borderLeft: "2px solid var(--color-accent-text)",
2519
+ color: "var(--color-accent-text)",
2434
2520
  padding: "0 4px"
2435
2521
  },
2436
2522
  ".cm-dropCursor:after": {
@@ -2444,47 +2530,66 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap }
2444
2530
  import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
2445
2531
  import { searchKeymap } from "@codemirror/search";
2446
2532
  import { EditorState } from "@codemirror/state";
2447
- import { EditorView as EditorView15, ViewPlugin as ViewPlugin11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2, scrollPastEnd } from "@codemirror/view";
2533
+ import { EditorView as EditorView16, ViewPlugin as ViewPlugin12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2 } from "@codemirror/view";
2448
2534
  import { vscodeDarkStyle, vscodeLightStyle } from "@uiw/codemirror-theme-vscode";
2449
2535
  import defaultsDeep2 from "lodash.defaultsdeep";
2450
2536
  import { generateName } from "@dxos/display-name";
2451
- import { log as log7 } from "@dxos/log";
2537
+ import { log as log8 } from "@dxos/log";
2452
2538
  import { hexToHue, isTruthy as isTruthy2 } from "@dxos/util";
2453
2539
 
2454
- // src/styles/markdown.ts
2540
+ // src/styles/theme.ts
2541
+ import { EditorView as EditorView13 } from "@codemirror/view";
2455
2542
  import { mx as mx3 } from "@dxos/ui-theme";
2456
2543
  var headings = {
2457
- 1: "text-4xl",
2458
- 2: "text-3xl",
2459
- 3: "text-2xl",
2460
- 4: "text-xl",
2461
- 5: "text-lg",
2462
- 6: ""
2544
+ 1: {
2545
+ className: "text-3xl",
2546
+ fontSize: "var(--text-3xl)",
2547
+ lineHeight: "var(--text-4xl--line-height)"
2548
+ },
2549
+ 2: {
2550
+ className: "text-2xl",
2551
+ fontSize: "var(--text-2xl)",
2552
+ lineHeight: "var(--text-3xl--line-height)"
2553
+ },
2554
+ 3: {
2555
+ className: "text-xl",
2556
+ fontSize: "var(--text-xl)",
2557
+ lineHeight: "var(--text-2xl--line-height)"
2558
+ },
2559
+ 4: {
2560
+ className: "text-lg",
2561
+ fontSize: "var(--text-lg)",
2562
+ lineHeight: "var(--text-xl--line-height)"
2563
+ },
2564
+ 5: {
2565
+ className: "text-base",
2566
+ fontSize: "var(--text-base)",
2567
+ lineHeight: "var(--text-lg--line-height)"
2568
+ },
2569
+ 6: {
2570
+ className: "text-base",
2571
+ fontSize: "var(--text-base)",
2572
+ lineHeight: "var(--text-base--line-height)"
2573
+ }
2463
2574
  };
2575
+ var fontBody = '"Inter Variable", ui-sans-serif, system-ui, sans-serif';
2576
+ var fontMono = '"JetBrains Mono Variable", ui-monospace, "Cascadia Code", "Source Code Pro", monospace';
2464
2577
  var markdownTheme = {
2465
- code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
2466
- codeMark: "font-mono text-primary-500",
2467
- mark: "opacity-50",
2468
- heading: (level) => {
2469
- return mx3(headings[level], "dark:text-neutral-400");
2470
- }
2578
+ code: "font-mono! cm-code-inline",
2579
+ codeMark: "font-mono! cm-code-mark",
2580
+ mark: "font-mono!",
2581
+ heading: (level) => ({
2582
+ className: mx3(headings[level].className, "font-light text-(--color-cm-heading-number)"),
2583
+ color: "var(--color-cm-heading) !important",
2584
+ lineHeight: headings[level].lineHeight,
2585
+ fontSize: headings[level].fontSize,
2586
+ fontWeight: "100 !important"
2587
+ })
2471
2588
  };
2472
-
2473
- // src/styles/theme.ts
2474
- import { EditorView as EditorView13 } from "@codemirror/view";
2475
-
2476
- // src/styles/tokens.ts
2477
- import { tokens } from "@dxos/ui-theme";
2478
- import { get } from "@dxos/util";
2479
- var getToken = (path, defaultValue) => {
2480
- const value = get(tokens, path, defaultValue);
2481
- return value?.toString() ?? "";
2482
- };
2483
- var fontBody = getToken("fontFamily.body");
2484
- var fontMono = getToken("fontFamily.mono");
2485
-
2486
- // src/styles/theme.ts
2487
2589
  var baseTheme = EditorView13.baseTheme({
2590
+ /**
2591
+ * Outer frame.
2592
+ */
2488
2593
  "&": {},
2489
2594
  "&.cm-focused": {
2490
2595
  outline: "none"
@@ -2493,7 +2598,18 @@ var baseTheme = EditorView13.baseTheme({
2493
2598
  * Scroller
2494
2599
  */
2495
2600
  ".cm-scroller": {
2496
- overflowY: "auto"
2601
+ overflowAnchor: "none"
2602
+ },
2603
+ ".cm-scroller::-webkit-scrollbar": {
2604
+ width: "8px"
2605
+ },
2606
+ ".cm-scroller::-webkit-scrollbar-track": {},
2607
+ ".cm-scroller::-webkit-scrollbar-thumb": {
2608
+ background: "transparent",
2609
+ transition: "background 0.15s"
2610
+ },
2611
+ "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
2612
+ background: "var(--color-scrollbar-thumb)"
2497
2613
  },
2498
2614
  /**
2499
2615
  * Content
@@ -2501,7 +2617,6 @@ var baseTheme = EditorView13.baseTheme({
2501
2617
  */
2502
2618
  ".cm-content": {
2503
2619
  padding: "unset",
2504
- lineHeight: "24px",
2505
2620
  color: "unset"
2506
2621
  },
2507
2622
  /**
@@ -2515,8 +2630,8 @@ var baseTheme = EditorView13.baseTheme({
2515
2630
  ".cm-gutter": {},
2516
2631
  ".cm-gutter.cm-lineNumbers": {
2517
2632
  paddingRight: "4px",
2518
- borderRight: "1px solid var(--dx-subduedSeparator)",
2519
- color: "var(--dx-subduedText)"
2633
+ borderRight: "1px solid var(--color-subdued-separator)",
2634
+ color: "var(--color-subdued)"
2520
2635
  },
2521
2636
  ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
2522
2637
  minWidth: "40px"
@@ -2525,36 +2640,43 @@ var baseTheme = EditorView13.baseTheme({
2525
2640
  * Height is set to match the corresponding line (which may have wrapped).
2526
2641
  */
2527
2642
  ".cm-gutterElement": {
2528
- lineHeight: "24px",
2643
+ lineHeight: 1.5,
2529
2644
  fontSize: "12px"
2530
2645
  },
2531
2646
  /**
2532
2647
  * Line.
2533
2648
  */
2534
2649
  ".cm-line": {
2535
- lineHeight: "24px",
2650
+ lineHeight: 1.5,
2536
2651
  paddingInline: 0
2537
2652
  },
2653
+ /**
2654
+ * Force all inline children to inherit line-height to prevent monospace font metrics
2655
+ * (JetBrains Mono ascent/descent) from inflating the line box beyond 24px.
2656
+ */
2657
+ ".cm-line *": {
2658
+ lineHeight: "inherit"
2659
+ },
2538
2660
  ".cm-activeLine": {
2539
- background: "var(--dx-cmActiveLine)"
2661
+ background: "var(--color-cm-active-line)"
2540
2662
  },
2541
2663
  /**
2542
2664
  * Cursor (layer).
2543
2665
  */
2544
2666
  ".cm-cursor, .cm-dropCursor": {
2545
- borderLeft: "2px solid var(--dx-cmCursor)"
2667
+ borderLeft: "2px solid var(--color-cm-cursor)"
2546
2668
  },
2547
2669
  ".cm-placeholder": {
2548
- color: "var(--dx-placeholder)"
2670
+ color: "var(--color-placeholder)"
2549
2671
  },
2550
2672
  /**
2551
2673
  * Selection (layer).
2552
2674
  */
2553
2675
  ".cm-selectionBackground": {
2554
- background: "var(--dx-cmSelection)"
2676
+ background: "var(--color-cm-selection)"
2555
2677
  },
2556
2678
  "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
2557
- background: "var(--dx-cmFocusedSelection)"
2679
+ background: "var(--color-cm-focused-selection)"
2558
2680
  },
2559
2681
  /**
2560
2682
  * Search.
@@ -2564,8 +2686,8 @@ var baseTheme = EditorView13.baseTheme({
2564
2686
  margin: "0 -3px",
2565
2687
  padding: "3px",
2566
2688
  borderRadius: "3px",
2567
- background: "var(--dx-cmHighlightSurface)",
2568
- color: "var(--dx-cmHighlight)"
2689
+ background: "var(--color-cm-highlight-surface)",
2690
+ color: "var(--color-cm-highlight)"
2569
2691
  },
2570
2692
  ".cm-searchMatch-selected": {
2571
2693
  textDecoration: "underline"
@@ -2576,20 +2698,29 @@ var baseTheme = EditorView13.baseTheme({
2576
2698
  ".cm-link": {
2577
2699
  textDecorationLine: "underline",
2578
2700
  textDecorationThickness: "1px",
2579
- textDecorationColor: "var(--dx-separator)",
2701
+ textDecorationColor: "var(--color-separator)",
2580
2702
  textUnderlineOffset: "2px",
2581
2703
  borderRadius: ".125rem"
2582
2704
  },
2583
2705
  ".cm-link > span": {
2584
- color: "var(--dx-accentText)"
2706
+ color: "var(--color-accent-text)"
2707
+ },
2708
+ ".cm-link > span:hover": {
2709
+ color: "var(--color-accent-text-hover)"
2585
2710
  },
2586
2711
  /**
2587
2712
  * Tooltip.
2588
2713
  */
2589
2714
  ".cm-tooltip": {
2590
- background: "var(--dx-baseSurface)"
2715
+ background: "var(--color-modal-surface)"
2591
2716
  },
2592
2717
  ".cm-tooltip-below": {},
2718
+ ".cm-tooltip-hover": {
2719
+ background: "var(--color-modal-surface)",
2720
+ border: "1px solid var(--color-separator)",
2721
+ borderRadius: "4px",
2722
+ overflow: "hidden"
2723
+ },
2593
2724
  /**
2594
2725
  * Autocomplete.
2595
2726
  * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
@@ -2597,7 +2728,7 @@ var baseTheme = EditorView13.baseTheme({
2597
2728
  ".cm-tooltip.cm-tooltip-autocomplete": {
2598
2729
  marginTop: "6px",
2599
2730
  marginLeft: "-10px",
2600
- border: "2px solid var(--dx-separator)",
2731
+ border: "2px solid var(--color-separator)",
2601
2732
  borderRadius: "4px"
2602
2733
  },
2603
2734
  ".cm-tooltip.cm-tooltip-autocomplete > ul": {
@@ -2607,12 +2738,12 @@ var baseTheme = EditorView13.baseTheme({
2607
2738
  padding: "4px"
2608
2739
  },
2609
2740
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2610
- background: "var(--dx-activeSurface)",
2611
- color: "var(--dx-activeSurfaceText)"
2741
+ background: "var(--color-active-surface)",
2742
+ color: "var(--color-base-surface-text)"
2612
2743
  },
2613
2744
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2614
2745
  paddingLeft: "4px !important",
2615
- color: "var(--dx-hoverSurfaceText)"
2746
+ color: "var(--color-base-surface-text)"
2616
2747
  },
2617
2748
  /**
2618
2749
  * Completion info.
@@ -2621,17 +2752,17 @@ var baseTheme = EditorView13.baseTheme({
2621
2752
  width: "360px !important",
2622
2753
  margin: "-10px 1px 0 1px",
2623
2754
  padding: "8px !important",
2624
- borderColor: "var(--dx-separator)"
2755
+ borderColor: "var(--color-separator)"
2625
2756
  },
2626
2757
  ".cm-completionIcon": {
2627
2758
  display: "none"
2628
2759
  },
2629
2760
  ".cm-completionLabel": {
2630
- color: "var(--dx-description)",
2761
+ color: "var(--color-description)",
2631
2762
  padding: "0 4px"
2632
2763
  },
2633
2764
  ".cm-completionMatchedText": {
2634
- color: "var(--dx-baseText)",
2765
+ color: "var(--color-base-surface-text)",
2635
2766
  textDecoration: "none !important"
2636
2767
  },
2637
2768
  /**
@@ -2655,7 +2786,7 @@ var baseTheme = EditorView13.baseTheme({
2655
2786
  backgroundColor: "var(--surface-bg)"
2656
2787
  },
2657
2788
  ".cm-panel input, .cm-panel button, .cm-panel label": {
2658
- color: "var(--dx-subdued)",
2789
+ color: "var(--color-subdued)",
2659
2790
  fontSize: "14px",
2660
2791
  all: "unset",
2661
2792
  margin: "3px !important",
@@ -2663,10 +2794,10 @@ var baseTheme = EditorView13.baseTheme({
2663
2794
  outline: "1px solid transparent"
2664
2795
  },
2665
2796
  ".cm-panel input, .cm-panel button": {
2666
- backgroundColor: "var(--dx-inputSurface)"
2797
+ backgroundColor: "var(--color-input-surface)"
2667
2798
  },
2668
2799
  ".cm-panel input:focus, .cm-panel button:focus": {
2669
- outline: "1px solid var(--dx-neutralFocusIndicator)"
2800
+ outline: "1px solid var(--color-neutral-focus-indicator)"
2670
2801
  },
2671
2802
  ".cm-panel label": {
2672
2803
  display: "inline-flex",
@@ -2679,33 +2810,33 @@ var baseTheme = EditorView13.baseTheme({
2679
2810
  height: "8px",
2680
2811
  marginRight: "6px !important",
2681
2812
  padding: "2px !important",
2682
- color: "var(--dx-neutralFocusIndicator)"
2813
+ color: "var(--color-neutral-focus-indicator)"
2683
2814
  },
2684
2815
  ".cm-panel button": {
2685
2816
  "&:hover": {
2686
- backgroundColor: "var(--dx-accentSurfaceHover) !important"
2817
+ // TODO(burdon): Replace with layer and @apply bg-accent-surface-hover
2818
+ backgroundColor: "var(--color-accent-surface-hover) !important"
2687
2819
  },
2688
2820
  "&:active": {
2689
- backgroundColor: "var(--dx-accentSurfaceHover)"
2821
+ backgroundColor: "var(--color-accent-surface-hover)"
2690
2822
  }
2691
2823
  },
2692
2824
  ".cm-panel.cm-search": {
2693
2825
  padding: "4px",
2694
- borderTop: "1px solid var(--dx-separator)"
2826
+ borderTop: "1px solid var(--color-separator)"
2695
2827
  }
2696
2828
  });
2697
2829
  var editorGutter = EditorView13.theme({
2698
2830
  ".cm-gutters": {
2699
2831
  // NOTE: Non-transparent background required to cover content if scrolling horizontally.
2700
- background: "var(--dx-baseSurface) !important",
2832
+ background: "var(--color-base-surface) !important",
2701
2833
  paddingRight: "1rem"
2702
2834
  }
2703
2835
  });
2704
2836
  var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2705
- // Set metrics on the scroller (this is often what CM uses for layout).
2837
+ // Main content.
2706
2838
  ".cm-scroller": {
2707
- fontFamily: monospace ? fontMono : fontBody,
2708
- fontSize: "16px"
2839
+ fontFamily: monospace ? fontMono : fontBody
2709
2840
  },
2710
2841
  // Maintain defaults for UI components.
2711
2842
  ".cm-content, .cm-gutters, .cm-panel": {
@@ -2745,9 +2876,32 @@ var focus = [
2745
2876
  })
2746
2877
  ];
2747
2878
 
2879
+ // src/extensions/scroll-past-end.ts
2880
+ import { EditorView as EditorView15, ViewPlugin as ViewPlugin11 } from "@codemirror/view";
2881
+ var scrollPastEndPlugin = ViewPlugin11.fromClass(class {
2882
+ height = 1e3;
2883
+ attrs = {
2884
+ style: "padding-bottom: 1000px"
2885
+ };
2886
+ update({ view }) {
2887
+ const lastLineBlock = view.lineBlockAt(view.state.doc.length);
2888
+ const height = view.dom.clientHeight - lastLineBlock.height - view.documentPadding.top - 0.5;
2889
+ if (height >= 0 && height !== this.height) {
2890
+ this.height = height;
2891
+ this.attrs = {
2892
+ style: `padding-bottom: ${height}px`
2893
+ };
2894
+ }
2895
+ }
2896
+ });
2897
+ var scrollPastEnd = () => [
2898
+ scrollPastEndPlugin,
2899
+ EditorView15.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?.attrs ?? null)
2900
+ ];
2901
+
2748
2902
  // src/extensions/factories.ts
2749
- var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2750
- var tabbable = EditorView15.contentAttributes.of({
2903
+ var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2904
+ var tabbable = EditorView16.contentAttributes.of({
2751
2905
  tabindex: "0"
2752
2906
  });
2753
2907
  var filterChars = (chars) => {
@@ -2800,10 +2954,10 @@ var createBasicExtensions = (propsProp) => {
2800
2954
  const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2801
2955
  return [
2802
2956
  // NOTE: Doesn't catch errors in keymap functions.
2803
- EditorView15.exceptionSink.of((err) => {
2804
- log7.catch(err, void 0, {
2805
- F: __dxlog_file10,
2806
- L: 132,
2957
+ EditorView16.exceptionSink.of((err) => {
2958
+ log8.catch(err, void 0, {
2959
+ F: __dxlog_file11,
2960
+ L: 130,
2807
2961
  S: void 0,
2808
2962
  C: (f, a) => f(...a)
2809
2963
  });
@@ -2815,7 +2969,7 @@ var createBasicExtensions = (propsProp) => {
2815
2969
  props.drawSelection && drawSelection({
2816
2970
  cursorBlinkRate: 1200
2817
2971
  }),
2818
- props.editable !== void 0 && EditorView15.editable.of(props.editable),
2972
+ props.editable !== void 0 && EditorView16.editable.of(props.editable),
2819
2973
  props.focus && focus,
2820
2974
  props.highlightActiveLine && highlightActiveLine(),
2821
2975
  props.history && history(),
@@ -2823,7 +2977,7 @@ var createBasicExtensions = (propsProp) => {
2823
2977
  lineNumbers(),
2824
2978
  editorGutter
2825
2979
  ],
2826
- props.lineWrapping && EditorView15.lineWrapping,
2980
+ props.lineWrapping && EditorView16.lineWrapping,
2827
2981
  props.placeholder && placeholder2(props.placeholder),
2828
2982
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2829
2983
  props.scrollPastEnd && scrollPastEnd(),
@@ -2855,12 +3009,12 @@ var createBasicExtensions = (propsProp) => {
2855
3009
  };
2856
3010
  var grow = {
2857
3011
  editor: {
2858
- className: "bs-full is-full"
3012
+ className: "h-full w-full"
2859
3013
  }
2860
3014
  };
2861
3015
  var fullWidth = {
2862
3016
  editor: {
2863
- className: "is-full"
3017
+ className: "w-full"
2864
3018
  }
2865
3019
  };
2866
3020
  var defaultThemeSlots = grow;
@@ -2872,18 +3026,18 @@ var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHig
2872
3026
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
2873
3027
  return [
2874
3028
  baseTheme,
2875
- EditorView15.darkTheme.of(themeMode === "dark"),
3029
+ EditorView16.darkTheme.of(themeMode === "dark"),
2876
3030
  createFontTheme({
2877
3031
  monospace
2878
3032
  }),
2879
3033
  syntaxHighlightingProp && syntaxHighlighting(HighlightStyle.define(themeMode === "dark" ? defaultStyles.dark : defaultStyles.light)),
2880
- slots.editor?.className && EditorView15.editorAttributes.of({
3034
+ slots.editor?.className && EditorView16.editorAttributes.of({
2881
3035
  class: slots.editor.className
2882
3036
  }),
2883
- slots.content?.className && EditorView15.contentAttributes.of({
3037
+ slots.content?.className && EditorView16.contentAttributes.of({
2884
3038
  class: slots.content.className
2885
3039
  }),
2886
- slots.scroll?.className && ViewPlugin11.fromClass(class {
3040
+ slots.scroll?.className && ViewPlugin12.fromClass(class {
2887
3041
  constructor(view) {
2888
3042
  view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
2889
3043
  }
@@ -2903,8 +3057,8 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2903
3057
  channel: `awareness.${id}`,
2904
3058
  peerId: identity.identityKey.toHex(),
2905
3059
  info: {
2906
- darkColor: `var(--dx-${hue}Cursor)`,
2907
- lightColor: `var(--dx-${hue}Cursor)`,
3060
+ darkColor: `var(--color-${hue}-border)`,
3061
+ lightColor: `var(--color-${hue}-border)`,
2908
3062
  displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex())
2909
3063
  }
2910
3064
  })));
@@ -2914,7 +3068,7 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2914
3068
 
2915
3069
  // src/extensions/folding.ts
2916
3070
  import { codeFolding, foldGutter } from "@codemirror/language";
2917
- import { EditorView as EditorView16 } from "@codemirror/view";
3071
+ import { EditorView as EditorView17 } from "@codemirror/view";
2918
3072
  import { Domino as Domino2, mx as mx4 } from "@dxos/ui";
2919
3073
  var folding = () => {
2920
3074
  return [
@@ -2922,13 +3076,14 @@ var folding = () => {
2922
3076
  placeholderDOM: () => Domino2.of("span").root
2923
3077
  }),
2924
3078
  foldGutter({
3079
+ // NOTE: We can't animate since the element is remounted on state change.
2925
3080
  markerDOM: (open) => {
2926
- return Domino2.of("div").classNames("flex bs-full justify-center items-center").children(Domino2.of("svg", Domino2.SVG).classNames(mx4("is-4 bs-4 cursor-pointer", open && "rotate-90")).children(Domino2.of("use", Domino2.SVG).attributes({
3081
+ return Domino2.of("div").classNames("flex h-full justify-center items-center").append(Domino2.of("svg", Domino2.SVG).classNames(mx4("w-4 h-4 cursor-pointer", open && "rotate-90")).append(Domino2.of("use", Domino2.SVG).attributes({
2927
3082
  href: Domino2.icon("ph--caret-right--regular")
2928
3083
  }))).root;
2929
3084
  }
2930
3085
  }),
2931
- EditorView16.theme({
3086
+ EditorView17.theme({
2932
3087
  ".cm-foldGutter": {
2933
3088
  opacity: 0.3,
2934
3089
  transition: "opacity 0.3s",
@@ -2942,7 +3097,7 @@ var folding = () => {
2942
3097
  };
2943
3098
 
2944
3099
  // src/extensions/hashtag.ts
2945
- import { Decoration as Decoration8, EditorView as EditorView17, MatchDecorator, ViewPlugin as ViewPlugin12, WidgetType as WidgetType4 } from "@codemirror/view";
3100
+ import { Decoration as Decoration8, EditorView as EditorView18, MatchDecorator, ViewPlugin as ViewPlugin13, WidgetType as WidgetType4 } from "@codemirror/view";
2946
3101
  import { getHashStyles, mx as mx5 } from "@dxos/ui-theme";
2947
3102
  var TagWidget = class extends WidgetType4 {
2948
3103
  _text;
@@ -2963,7 +3118,7 @@ var tagMatcher = new MatchDecorator({
2963
3118
  })
2964
3119
  });
2965
3120
  var hashtag = () => [
2966
- ViewPlugin12.fromClass(class {
3121
+ ViewPlugin13.fromClass(class {
2967
3122
  tags;
2968
3123
  constructor(view) {
2969
3124
  this.tags = tagMatcher.createDeco(view);
@@ -2973,11 +3128,11 @@ var hashtag = () => [
2973
3128
  }
2974
3129
  }, {
2975
3130
  decorations: (instance) => instance.tags,
2976
- provide: (plugin) => EditorView17.atomicRanges.of((view) => {
3131
+ provide: (plugin) => EditorView18.atomicRanges.of((view) => {
2977
3132
  return view.plugin(plugin)?.tags || Decoration8.none;
2978
3133
  })
2979
3134
  }),
2980
- EditorView17.theme({
3135
+ EditorView18.theme({
2981
3136
  ".cm-tag": {
2982
3137
  borderRadius: "4px",
2983
3138
  marginRight: "6px",
@@ -3032,18 +3187,18 @@ var schemaLinter = (validate) => (view) => {
3032
3187
  };
3033
3188
 
3034
3189
  // src/extensions/listener.ts
3035
- import { EditorView as EditorView18 } from "@codemirror/view";
3190
+ import { EditorView as EditorView19 } from "@codemirror/view";
3036
3191
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
3037
3192
  var listener = ({ onFocus, onChange }) => {
3038
3193
  return [
3039
- onFocus && EditorView18.focusChangeEffect.of((state, focusing) => {
3194
+ onFocus && EditorView19.focusChangeEffect.of((state, focusing) => {
3040
3195
  onFocus({
3041
3196
  id: state.facet(documentId),
3042
3197
  focusing
3043
3198
  });
3044
3199
  return null;
3045
3200
  }),
3046
- onChange && EditorView18.updateListener.of(({ state, docChanged }) => {
3201
+ onChange && EditorView19.updateListener.of(({ state, docChanged }) => {
3047
3202
  if (docChanged) {
3048
3203
  onChange({
3049
3204
  id: state.facet(documentId),
@@ -3058,7 +3213,7 @@ var listener = ({ onFocus, onChange }) => {
3058
3213
  import { snippet } from "@codemirror/autocomplete";
3059
3214
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3060
3215
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
3061
- import { EditorView as EditorView19, keymap as keymap8 } from "@codemirror/view";
3216
+ import { EditorView as EditorView20, keymap as keymap8 } from "@codemirror/view";
3062
3217
  import { debounceAndThrottle } from "@dxos/async";
3063
3218
  var formattingEquals = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
3064
3219
  var Inline = /* @__PURE__ */ (function(Inline2) {
@@ -4147,7 +4302,7 @@ var getFormatting = (state) => {
4147
4302
  };
4148
4303
  };
4149
4304
  var formattingListener = (onStateChange, delay = 100) => {
4150
- return EditorView19.updateListener.of(debounceAndThrottle((update2) => {
4305
+ return EditorView20.updateListener.of(debounceAndThrottle((update2) => {
4151
4306
  if (update2.docChanged || update2.selectionSet) {
4152
4307
  onStateChange(getFormatting(update2.state));
4153
4308
  }
@@ -4208,8 +4363,7 @@ import { completionKeymap } from "@codemirror/autocomplete";
4208
4363
  import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
4209
4364
  import { jsonLanguage } from "@codemirror/lang-json";
4210
4365
  import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
4211
- import { xml } from "@codemirror/lang-xml";
4212
- import { LanguageDescription, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4366
+ import { foldNodeProp, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4213
4367
  import { languages } from "@codemirror/language-data";
4214
4368
  import { keymap as keymap9 } from "@codemirror/view";
4215
4369
  import { isTruthy as isTruthy3 } from "@dxos/util";
@@ -4330,30 +4484,38 @@ var markdownHighlightStyle = (_options = {}) => {
4330
4484
  ],
4331
4485
  class: "font-mono"
4332
4486
  },
4333
- // Headings.
4487
+ // Headings — use CSS properties only (no class:) so CodeMirror generates scoped CSS via
4488
+ // StyleModule that overrides vscodeDarkStyle's t.heading rule. When class: is present,
4489
+ // HighlightStyle silently ignores all other CSS properties (they're mutually exclusive).
4490
+ // Font sizes use Tailwind v4 CSS variables so nothing is hardcoded.
4491
+ {
4492
+ tag: tags.heading,
4493
+ color: "var(--color-cm-heading) !important",
4494
+ fontWeight: "300"
4495
+ },
4334
4496
  {
4335
4497
  tag: tags.heading1,
4336
- class: markdownTheme.heading(1)
4498
+ ...markdownTheme.heading(1)
4337
4499
  },
4338
4500
  {
4339
4501
  tag: tags.heading2,
4340
- class: markdownTheme.heading(2)
4502
+ ...markdownTheme.heading(2)
4341
4503
  },
4342
4504
  {
4343
4505
  tag: tags.heading3,
4344
- class: markdownTheme.heading(3)
4506
+ ...markdownTheme.heading(3)
4345
4507
  },
4346
4508
  {
4347
4509
  tag: tags.heading4,
4348
- class: markdownTheme.heading(4)
4510
+ ...markdownTheme.heading(4)
4349
4511
  },
4350
4512
  {
4351
4513
  tag: tags.heading5,
4352
- class: markdownTheme.heading(5)
4514
+ ...markdownTheme.heading(5)
4353
4515
  },
4354
4516
  {
4355
4517
  tag: tags.heading6,
4356
- class: markdownTheme.heading(6)
4518
+ ...markdownTheme.heading(6)
4357
4519
  },
4358
4520
  // Emphasis.
4359
4521
  {
@@ -4408,15 +4570,23 @@ var createMarkdownExtensions = (options = {}) => {
4408
4570
  // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
4409
4571
  base: markdownLanguage2,
4410
4572
  // Languages for syntax highlighting fenced code blocks.
4573
+ // Caller-supplied languages are checked first so they can override defaults.
4411
4574
  defaultCodeLanguage: jsonLanguage,
4412
- codeLanguages: languages,
4575
+ codeLanguages: [
4576
+ ...options.codeLanguages ?? [],
4577
+ ...languages
4578
+ ],
4413
4579
  // Don't complete HTML tags.
4414
4580
  completeHTMLTags: false,
4415
4581
  // Parser extensions.
4416
4582
  extensions: [
4417
4583
  // GFM provided by default.
4418
4584
  markdownTagsExtensions,
4419
- ...options.extensions ?? defaultExtensions()
4585
+ ...options.extensions ?? defaultExtensions(),
4586
+ // Disable folding for fenced code blocks by overriding foldNodeProp.
4587
+ // Note: returning null from foldService does not prevent syntaxFolding fallback,
4588
+ // so we must override the node prop directly on the FencedCode node type.
4589
+ noFencedCodeFolding
4420
4590
  ]
4421
4591
  }),
4422
4592
  // Custom styles.
@@ -4431,18 +4601,13 @@ var createMarkdownExtensions = (options = {}) => {
4431
4601
  ].filter(isTruthy3))
4432
4602
  ];
4433
4603
  };
4434
- var xmlLanguageDesc = LanguageDescription.of({
4435
- name: "xml",
4436
- alias: [
4437
- "html",
4438
- "xhtml"
4439
- ],
4440
- extensions: [
4441
- "xml",
4442
- "xhtml"
4443
- ],
4444
- load: async () => xml()
4445
- });
4604
+ var noFencedCodeFolding = {
4605
+ props: [
4606
+ foldNodeProp.add({
4607
+ FencedCode: () => null
4608
+ })
4609
+ ]
4610
+ };
4446
4611
  var defaultExtensions = () => [
4447
4612
  noSetExtHeading,
4448
4613
  noHtml
@@ -4462,19 +4627,19 @@ var debugTree = (cb) => StateField6.define({
4462
4627
  update: (value, tr) => cb(convertTreeToJson(tr.state))
4463
4628
  });
4464
4629
  var convertTreeToJson = (state) => {
4465
- const treeToJson = (cursor2) => {
4630
+ const treeToJson = (cursor) => {
4466
4631
  const node = {
4467
- type: cursor2.type.name,
4468
- from: cursor2.from,
4469
- to: cursor2.to,
4470
- text: state.doc.slice(cursor2.from, cursor2.to).toString(),
4632
+ type: cursor.type.name,
4633
+ from: cursor.from,
4634
+ to: cursor.to,
4635
+ text: state.doc.slice(cursor.from, cursor.to).toString(),
4471
4636
  children: []
4472
4637
  };
4473
- if (cursor2.firstChild()) {
4638
+ if (cursor.firstChild()) {
4474
4639
  do {
4475
- node.children.push(treeToJson(cursor2));
4476
- } while (cursor2.nextSibling());
4477
- cursor2.parent();
4640
+ node.children.push(treeToJson(cursor));
4641
+ } while (cursor.nextSibling());
4642
+ cursor.parent();
4478
4643
  }
4479
4644
  return node;
4480
4645
  };
@@ -4484,16 +4649,15 @@ var convertTreeToJson = (state) => {
4484
4649
  // src/extensions/markdown/decorate.ts
4485
4650
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4486
4651
  import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect7 } from "@codemirror/state";
4487
- import { Decoration as Decoration11, EditorView as EditorView23, ViewPlugin as ViewPlugin14, WidgetType as WidgetType7 } from "@codemirror/view";
4652
+ import { Decoration as Decoration11, EditorView as EditorView24, ViewPlugin as ViewPlugin15, WidgetType as WidgetType7 } from "@codemirror/view";
4488
4653
  import { invariant as invariant4 } from "@dxos/invariant";
4489
- import { mx as mx6 } from "@dxos/ui-theme";
4490
4654
 
4491
4655
  // src/extensions/markdown/changes.ts
4492
4656
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4493
4657
  import { Transaction as Transaction4 } from "@codemirror/state";
4494
- import { ViewPlugin as ViewPlugin13 } from "@codemirror/view";
4658
+ import { ViewPlugin as ViewPlugin14 } from "@codemirror/view";
4495
4659
  var adjustChanges = () => {
4496
- return ViewPlugin13.fromClass(class {
4660
+ return ViewPlugin14.fromClass(class {
4497
4661
  update(update2) {
4498
4662
  const tree = syntaxTree4(update2.state);
4499
4663
  const adjustments = [];
@@ -4635,7 +4799,7 @@ var getValidUrl = (str) => {
4635
4799
  // src/extensions/markdown/image.ts
4636
4800
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4637
4801
  import { StateField as StateField7 } from "@codemirror/state";
4638
- import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType5 } from "@codemirror/view";
4802
+ import { Decoration as Decoration9, EditorView as EditorView21, WidgetType as WidgetType5 } from "@codemirror/view";
4639
4803
  var image = (_options = {}) => {
4640
4804
  return [
4641
4805
  StateField7.define({
@@ -4646,10 +4810,10 @@ var image = (_options = {}) => {
4646
4810
  if (!tr.docChanged && !tr.selection) {
4647
4811
  return value;
4648
4812
  }
4649
- const cursor2 = tr.state.selection.main.head;
4813
+ const cursor = tr.state.selection.main.head;
4650
4814
  const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
4651
- let from = Math.min(cursor2, oldCursor);
4652
- let to = Math.max(cursor2, oldCursor);
4815
+ let from = Math.min(cursor, oldCursor);
4816
+ let to = Math.max(cursor, oldCursor);
4653
4817
  tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
4654
4818
  from = Math.min(from, fromB);
4655
4819
  to = Math.max(to, toB);
@@ -4663,19 +4827,19 @@ var image = (_options = {}) => {
4663
4827
  add: buildDecorations(tr.state, from, to)
4664
4828
  });
4665
4829
  },
4666
- provide: (field) => EditorView20.decorations.from(field)
4830
+ provide: (field) => EditorView21.decorations.from(field)
4667
4831
  })
4668
4832
  ];
4669
4833
  };
4670
4834
  var buildDecorations = (state, from, to) => {
4671
4835
  const decorations2 = [];
4672
- const cursor2 = state.selection.main.head;
4836
+ const cursor = state.selection.main.head;
4673
4837
  syntaxTree5(state).iterate({
4674
4838
  enter: (node) => {
4675
4839
  if (node.name === "Image") {
4676
4840
  const urlNode = node.node.getChild("URL");
4677
4841
  if (urlNode) {
4678
- const hide2 = state.readOnly || cursor2 < node.from || cursor2 > node.to || !state.field(focusField);
4842
+ const hide2 = state.readOnly || cursor < node.from || cursor > node.to || !state.field(focusField);
4679
4843
  const url = state.sliceDoc(urlNode.from, urlNode.to);
4680
4844
  if (url.match(/^https?:\/\//) === null && url.match(/^file?:\/\//) === null) {
4681
4845
  return;
@@ -4723,10 +4887,10 @@ var ImageWidget = class extends WidgetType5 {
4723
4887
  };
4724
4888
 
4725
4889
  // src/extensions/markdown/styles.ts
4726
- import { EditorView as EditorView21 } from "@codemirror/view";
4890
+ import { EditorView as EditorView22 } from "@codemirror/view";
4727
4891
  var bulletListIndentationWidth = 24;
4728
4892
  var orderedListIndentationWidth = 36;
4729
- var formattingStyles = EditorView21.theme({
4893
+ var formattingStyles = EditorView22.theme({
4730
4894
  /**
4731
4895
  * Horizontal rule.
4732
4896
  */
@@ -4735,7 +4899,7 @@ var formattingStyles = EditorView21.theme({
4735
4899
  width: "100%",
4736
4900
  height: "0",
4737
4901
  verticalAlign: "middle",
4738
- borderTop: "1px solid var(--dx-cmSeparator)",
4902
+ borderTop: "1px solid var(--color-cm-separator)",
4739
4903
  opacity: 0.5
4740
4904
  },
4741
4905
  /**
@@ -4758,19 +4922,44 @@ var formattingStyles = EditorView21.theme({
4758
4922
  * Blockquote.
4759
4923
  */
4760
4924
  "& .cm-blockquote": {
4761
- background: "var(--dx-cmCodeblock)",
4762
- borderLeft: "2px solid var(--dx-cmSeparator)",
4925
+ background: "var(--color-cm-codeblock)",
4926
+ borderLeft: "2px solid var(--color-cm-separator)",
4763
4927
  paddingLeft: "1rem",
4764
- margin: "0"
4928
+ margin: 0
4765
4929
  },
4766
4930
  /**
4767
4931
  * Code and codeblocks.
4768
4932
  */
4933
+ "& code": {
4934
+ fontFamily: fontMono,
4935
+ color: "var(--color-cm-code)",
4936
+ whiteSpace: "nowrap"
4937
+ },
4769
4938
  "& .cm-code": {
4770
- fontFamily: fontMono
4939
+ fontFamily: fontMono,
4940
+ color: "var(--color-cm-code)"
4941
+ },
4942
+ // Inline code spans (triggered by backticks) use `cm-code-inline` + `font-mono`.
4943
+ // Different monospace font metrics can slightly overflow the fixed CodeMirror line box,
4944
+ // so constrain them to the target 24px height.
4945
+ "& .cm-code-inline": {
4946
+ fontFamily: fontMono,
4947
+ height: "24px",
4948
+ // display: 'inline-flex',
4949
+ alignItems: "center",
4950
+ overflow: "hidden",
4951
+ whiteSpace: "nowrap",
4952
+ color: "var(--color-cm-code-inline)"
4953
+ },
4954
+ "& .cm-code-mark": {
4955
+ fontFamily: fontMono,
4956
+ height: "24px",
4957
+ display: "inline-flex",
4958
+ alignItems: "center",
4959
+ overflow: "hidden"
4771
4960
  },
4772
4961
  "& .cm-codeblock-line": {
4773
- background: "var(--dx-cmCodeblock)",
4962
+ background: "var(--color-cm-codeblock)",
4774
4963
  paddingInline: "1rem !important"
4775
4964
  },
4776
4965
  "& .cm-codeblock-start": {
@@ -4799,16 +4988,24 @@ var formattingStyles = EditorView21.theme({
4799
4988
  */
4800
4989
  ".cm-table *": {
4801
4990
  fontFamily: fontMono,
4991
+ lineHeight: 1.5,
4802
4992
  textDecoration: "none !important"
4803
4993
  },
4804
4994
  ".cm-table-head": {
4805
4995
  padding: "2px 16px 2px 0px",
4996
+ overflowWrap: "break-word",
4997
+ whiteSpace: "pre-wrap",
4998
+ wordBreak: "keep-all",
4806
4999
  textAlign: "left",
4807
- borderBottom: "1px solid var(--dx-cmSeparator)",
4808
- color: "var(--dx-subdued)"
5000
+ color: "var(--color-subdued)",
5001
+ borderBottom: "1px solid var(--color-cm-separator)"
4809
5002
  },
4810
5003
  ".cm-table-cell": {
4811
- padding: "2px 16px 2px 0px"
5004
+ padding: "2px 16px 2px 0px",
5005
+ overflowWrap: "break-word",
5006
+ whiteSpace: "pre-wrap",
5007
+ wordBreak: "keep-all",
5008
+ verticalAlign: "top"
4812
5009
  },
4813
5010
  /**
4814
5011
  * Image.
@@ -4824,12 +5021,12 @@ var formattingStyles = EditorView21.theme({
4824
5021
  },
4825
5022
  ".cm-image-with-loader": {
4826
5023
  display: "block",
4827
- opacity: "0",
5024
+ opacity: 0,
4828
5025
  transitionDuration: "350ms",
4829
5026
  transitionProperty: "opacity"
4830
5027
  },
4831
5028
  ".cm-image-with-loader.cm-loaded-image": {
4832
- opacity: "1"
5029
+ opacity: 1
4833
5030
  },
4834
5031
  ".cm-image-wrapper": {
4835
5032
  "grid-template-columns": "1fr",
@@ -4848,17 +5045,17 @@ var formattingStyles = EditorView21.theme({
4848
5045
  // src/extensions/markdown/table.ts
4849
5046
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4850
5047
  import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4851
- import { Decoration as Decoration10, EditorView as EditorView22, WidgetType as WidgetType6 } from "@codemirror/view";
5048
+ import { Decoration as Decoration10, EditorView as EditorView23, WidgetType as WidgetType6 } from "@codemirror/view";
4852
5049
  var table = (options = {}) => {
4853
5050
  return StateField8.define({
4854
5051
  create: (state) => update(state, options),
4855
5052
  update: (_, tr) => update(tr.state, options),
4856
- provide: (field) => EditorView22.decorations.from(field)
5053
+ provide: (field) => EditorView23.decorations.from(field)
4857
5054
  });
4858
5055
  };
4859
5056
  var update = (state, _options) => {
4860
5057
  const builder = new RangeSetBuilder4();
4861
- const cursor2 = state.selection.main.head;
5058
+ const cursor = state.selection.main.head;
4862
5059
  const tables = [];
4863
5060
  const getTable = () => tables[tables.length - 1];
4864
5061
  const getRow = () => {
@@ -4896,7 +5093,7 @@ var update = (state, _options) => {
4896
5093
  }
4897
5094
  });
4898
5095
  tables.forEach((table2) => {
4899
- const replace = state.readOnly || cursor2 < table2.from || cursor2 > table2.to;
5096
+ const replace = state.readOnly || cursor < table2.from || cursor > table2.to;
4900
5097
  if (replace) {
4901
5098
  builder.add(table2.from, table2.to, Decoration10.replace({
4902
5099
  block: true,
@@ -4910,6 +5107,26 @@ var update = (state, _options) => {
4910
5107
  });
4911
5108
  return builder.finish();
4912
5109
  };
5110
+ var renderCellContent = (el, text) => {
5111
+ const parts = text.split(/(`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_)/);
5112
+ for (const part of parts) {
5113
+ if (part.length > 2 && part.startsWith("`") && part.endsWith("`")) {
5114
+ const code = document.createElement("code");
5115
+ code.textContent = part.slice(1, -1);
5116
+ el.appendChild(code);
5117
+ } else if (part.startsWith("**") && part.endsWith("**") || part.startsWith("__") && part.endsWith("__")) {
5118
+ const strong = document.createElement("strong");
5119
+ strong.textContent = part.slice(2, -2);
5120
+ el.appendChild(strong);
5121
+ } else if (part.startsWith("*") && part.endsWith("*") || part.startsWith("_") && part.endsWith("_")) {
5122
+ const em = document.createElement("em");
5123
+ em.textContent = part.slice(1, -1);
5124
+ el.appendChild(em);
5125
+ } else {
5126
+ el.appendChild(document.createTextNode(part));
5127
+ }
5128
+ }
5129
+ };
4913
5130
  var TableWidget = class extends WidgetType6 {
4914
5131
  _table;
4915
5132
  constructor(_table) {
@@ -4929,7 +5146,7 @@ var TableWidget = class extends WidgetType6 {
4929
5146
  this._table.header?.forEach((cell) => {
4930
5147
  const th = document.createElement("th");
4931
5148
  th.setAttribute("class", "cm-table-head");
4932
- tr.appendChild(th).textContent = cell;
5149
+ renderCellContent(tr.appendChild(th), cell);
4933
5150
  });
4934
5151
  const body = table2.appendChild(document.createElement("tbody"));
4935
5152
  this._table.rows?.forEach((row) => {
@@ -4937,7 +5154,7 @@ var TableWidget = class extends WidgetType6 {
4937
5154
  row.forEach((cell) => {
4938
5155
  const td = document.createElement("td");
4939
5156
  td.setAttribute("class", "cm-table-cell");
4940
- tr2.appendChild(td).textContent = cell;
5157
+ renderCellContent(tr2.appendChild(td), cell);
4941
5158
  });
4942
5159
  });
4943
5160
  return div;
@@ -4945,7 +5162,7 @@ var TableWidget = class extends WidgetType6 {
4945
5162
  };
4946
5163
 
4947
5164
  // src/extensions/markdown/decorate.ts
4948
- var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/markdown/decorate.ts";
5165
+ var __dxlog_file12 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/markdown/decorate.ts";
4949
5166
  var Unicode = {
4950
5167
  emDash: "\u2014",
4951
5168
  bullet: "\u2022",
@@ -4968,7 +5185,6 @@ var LinkButton = class extends WidgetType7 {
4968
5185
  eq(other) {
4969
5186
  return this.url === other.url;
4970
5187
  }
4971
- // TODO(burdon): Create icon and link directly without react?
4972
5188
  toDOM(view) {
4973
5189
  const el = document.createElement("span");
4974
5190
  this.render(el, {
@@ -5045,10 +5261,10 @@ var fencedCodeLine = Decoration11.line({
5045
5261
  class: "cm-code cm-codeblock-line"
5046
5262
  });
5047
5263
  var fencedCodeLineFirst = Decoration11.line({
5048
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-start")
5264
+ class: "cm-code cm-codeblock-line cm-codeblock-start"
5049
5265
  });
5050
5266
  var fencedCodeLineLast = Decoration11.line({
5051
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-end")
5267
+ class: "cm-code cm-codeblock-line cm-codeblock-end"
5052
5268
  });
5053
5269
  var commentBlockLine = fencedCodeLine;
5054
5270
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -5081,8 +5297,8 @@ var buildDecorations2 = (view, options, focus2) => {
5081
5297
  const headerLevels = [];
5082
5298
  const getHeaderLevels = (node, level) => {
5083
5299
  invariant4(level > 0, void 0, {
5084
- F: __dxlog_file11,
5085
- L: 180,
5300
+ F: __dxlog_file12,
5301
+ L: 177,
5086
5302
  S: void 0,
5087
5303
  A: [
5088
5304
  "level > 0",
@@ -5120,8 +5336,8 @@ var buildDecorations2 = (view, options, focus2) => {
5120
5336
  };
5121
5337
  const getCurrentListLevel = () => {
5122
5338
  invariant4(listLevels.length, void 0, {
5123
- F: __dxlog_file11,
5124
- L: 202,
5339
+ F: __dxlog_file12,
5340
+ L: 199,
5125
5341
  S: void 0,
5126
5342
  A: [
5127
5343
  "listLevels.length",
@@ -5165,13 +5381,13 @@ var buildDecorations2 = (view, options, focus2) => {
5165
5381
  deco: hide
5166
5382
  });
5167
5383
  } else {
5168
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5384
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + "). ";
5169
5385
  if (num.length) {
5170
5386
  atomicDecoRanges.push({
5171
5387
  from: mark.from,
5172
5388
  to: mark.from + len,
5173
5389
  deco: Decoration11.replace({
5174
- widget: new TextWidget(num, markdownTheme.heading(level))
5390
+ widget: new TextWidget(num, markdownTheme.heading(level).className)
5175
5391
  })
5176
5392
  });
5177
5393
  }
@@ -5348,11 +5564,11 @@ var buildDecorations2 = (view, options, focus2) => {
5348
5564
  }
5349
5565
  decoRanges.push({
5350
5566
  from: marks[0].to,
5351
- to: marks[1].from,
5567
+ to: !editing && options.renderLinkButton ? node.to : marks[1].from,
5352
5568
  deco: Decoration11.mark({
5353
5569
  tagName: "a",
5354
5570
  attributes: {
5355
- class: "cm-link",
5571
+ class: options.renderLinkButton ? "cm-link cm-link-with-button" : "cm-link",
5356
5572
  href: url,
5357
5573
  rel: "noreferrer",
5358
5574
  target: "_blank"
@@ -5430,8 +5646,11 @@ var buildDecorations2 = (view, options, focus2) => {
5430
5646
  deco.add(from, to, d);
5431
5647
  }
5432
5648
  const atomicDeco = new RangeSetBuilder5();
5433
- for (const { from, to, deco: d } of atomicDecoRanges) {
5434
- atomicDeco.add(from, to, d);
5649
+ for (const { from, to, deco: deco2 } of atomicDecoRanges) {
5650
+ if (from < to && state.doc.lineAt(from).number !== state.doc.lineAt(to).number) {
5651
+ continue;
5652
+ }
5653
+ atomicDeco.add(from, to, deco2);
5435
5654
  }
5436
5655
  return {
5437
5656
  deco: deco.finish(),
@@ -5441,7 +5660,7 @@ var buildDecorations2 = (view, options, focus2) => {
5441
5660
  var forceUpdate = StateEffect7.define();
5442
5661
  var decorateMarkdown = (options = {}) => {
5443
5662
  return [
5444
- ViewPlugin14.fromClass(class {
5663
+ ViewPlugin15.fromClass(class {
5445
5664
  deco;
5446
5665
  atomicDeco;
5447
5666
  pendingUpdate;
@@ -5476,9 +5695,9 @@ var decorateMarkdown = (options = {}) => {
5476
5695
  }
5477
5696
  }, {
5478
5697
  provide: (plugin) => [
5479
- Prec4.low(EditorView23.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5480
- EditorView23.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5481
- EditorView23.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5698
+ Prec4.low(EditorView24.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5699
+ EditorView24.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5700
+ EditorView24.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5482
5701
  ]
5483
5702
  }),
5484
5703
  image(),
@@ -5510,8 +5729,7 @@ var linkTooltip = (renderTooltip) => {
5510
5729
  return {
5511
5730
  pos: link.from,
5512
5731
  end: link.to,
5513
- // NOTE: Forcing above causes the tooltip to flicker.
5514
- // above: true,
5732
+ above: true,
5515
5733
  create: () => {
5516
5734
  const el = document.createElement("div");
5517
5735
  el.className = tooltipContent({});
@@ -5527,16 +5745,13 @@ var linkTooltip = (renderTooltip) => {
5527
5745
  };
5528
5746
  }
5529
5747
  };
5530
- }, {
5531
- // NOTE: 0 = default of 300ms.
5532
- hoverTime: 1
5533
5748
  });
5534
5749
  };
5535
5750
 
5536
5751
  // src/extensions/mention.ts
5537
5752
  import { autocompletion } from "@codemirror/autocomplete";
5538
- import { log as log8 } from "@dxos/log";
5539
- var __dxlog_file12 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/mention.ts";
5753
+ import { log as log9 } from "@dxos/log";
5754
+ var __dxlog_file13 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/mention.ts";
5540
5755
  var mention = ({ debug, onSearch }) => {
5541
5756
  return autocompletion({
5542
5757
  // TODO(burdon): Not working.
@@ -5548,10 +5763,10 @@ var mention = ({ debug, onSearch }) => {
5548
5763
  icons: false,
5549
5764
  override: [
5550
5765
  (context) => {
5551
- log8.info("completion context", {
5766
+ log9.info("completion context", {
5552
5767
  context
5553
5768
  }, {
5554
- F: __dxlog_file12,
5769
+ F: __dxlog_file13,
5555
5770
  L: 27,
5556
5771
  S: void 0,
5557
5772
  C: (f, a) => f(...a)
@@ -5634,7 +5849,7 @@ import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5634
5849
  import { StateField as StateField10 } from "@codemirror/state";
5635
5850
  import { Facet as Facet2 } from "@codemirror/state";
5636
5851
  import { invariant as invariant5 } from "@dxos/invariant";
5637
- var __dxlog_file13 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/tree.ts";
5852
+ var __dxlog_file14 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/tree.ts";
5638
5853
  var itemToJSON = ({ type, index, level, lineRange, contentRange, children }) => {
5639
5854
  return {
5640
5855
  type,
@@ -5789,7 +6004,7 @@ var outlinerTree = (_options = {}) => {
5789
6004
  }
5790
6005
  case "BulletList": {
5791
6006
  invariant5(current, void 0, {
5792
- F: __dxlog_file13,
6007
+ F: __dxlog_file14,
5793
6008
  L: 219,
5794
6009
  S: void 0,
5795
6010
  A: [
@@ -5806,7 +6021,7 @@ var outlinerTree = (_options = {}) => {
5806
6021
  }
5807
6022
  case "ListItem": {
5808
6023
  invariant5(parent, void 0, {
5809
- F: __dxlog_file13,
6024
+ F: __dxlog_file14,
5810
6025
  L: 228,
5811
6026
  S: void 0,
5812
6027
  A: [
@@ -5848,7 +6063,7 @@ var outlinerTree = (_options = {}) => {
5848
6063
  }
5849
6064
  case "ListMark": {
5850
6065
  invariant5(current, void 0, {
5851
- F: __dxlog_file13,
6066
+ F: __dxlog_file14,
5852
6067
  L: 272,
5853
6068
  S: void 0,
5854
6069
  A: [
@@ -5862,7 +6077,7 @@ var outlinerTree = (_options = {}) => {
5862
6077
  }
5863
6078
  case "Task": {
5864
6079
  invariant5(current, void 0, {
5865
- F: __dxlog_file13,
6080
+ F: __dxlog_file14,
5866
6081
  L: 278,
5867
6082
  S: void 0,
5868
6083
  A: [
@@ -5875,7 +6090,7 @@ var outlinerTree = (_options = {}) => {
5875
6090
  }
5876
6091
  case "TaskMarker": {
5877
6092
  invariant5(current, void 0, {
5878
- F: __dxlog_file13,
6093
+ F: __dxlog_file14,
5879
6094
  L: 283,
5880
6095
  S: void 0,
5881
6096
  A: [
@@ -5891,7 +6106,7 @@ var outlinerTree = (_options = {}) => {
5891
6106
  leave: (node) => {
5892
6107
  if (node.name === "BulletList") {
5893
6108
  invariant5(parent, void 0, {
5894
- F: __dxlog_file13,
6109
+ F: __dxlog_file14,
5895
6110
  L: 291,
5896
6111
  S: void 0,
5897
6112
  A: [
@@ -5905,7 +6120,7 @@ var outlinerTree = (_options = {}) => {
5905
6120
  }
5906
6121
  });
5907
6122
  invariant5(tree, void 0, {
5908
- F: __dxlog_file13,
6123
+ F: __dxlog_file14,
5909
6124
  L: 298,
5910
6125
  S: void 0,
5911
6126
  A: [
@@ -6197,17 +6412,17 @@ var commands = () => keymap11.of([
6197
6412
 
6198
6413
  // src/extensions/outliner/outliner.ts
6199
6414
  import { Prec as Prec5 } from "@codemirror/state";
6200
- import { Decoration as Decoration12, EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6201
- import { mx as mx7 } from "@dxos/ui-theme";
6415
+ import { Decoration as Decoration12, EditorView as EditorView26, ViewPlugin as ViewPlugin18 } from "@codemirror/view";
6416
+ import { mx as mx6 } from "@dxos/ui-theme";
6202
6417
 
6203
6418
  // src/extensions/outliner/editor.ts
6204
6419
  import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
6205
- import { ViewPlugin as ViewPlugin15 } from "@codemirror/view";
6206
- import { log as log9 } from "@dxos/log";
6207
- var __dxlog_file14 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6420
+ import { ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6421
+ import { log as log10 } from "@dxos/log";
6422
+ var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6208
6423
  var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
6209
6424
  var initialize = () => {
6210
- return ViewPlugin15.fromClass(class {
6425
+ return ViewPlugin16.fromClass(class {
6211
6426
  constructor(view) {
6212
6427
  const first = view.state.doc.lineAt(0);
6213
6428
  const text = view.state.sliceDoc(first.from, first.to);
@@ -6336,7 +6551,7 @@ var editor = () => [
6336
6551
  cancel = true;
6337
6552
  return;
6338
6553
  }
6339
- log9("change", {
6554
+ log10("change", {
6340
6555
  item,
6341
6556
  line: {
6342
6557
  from: line.from,
@@ -6355,7 +6570,7 @@ var editor = () => [
6355
6570
  length: insert.length
6356
6571
  }
6357
6572
  }, {
6358
- F: __dxlog_file14,
6573
+ F: __dxlog_file15,
6359
6574
  L: 164,
6360
6575
  S: void 0,
6361
6576
  C: (f, a) => f(...a)
@@ -6363,10 +6578,10 @@ var editor = () => [
6363
6578
  }
6364
6579
  });
6365
6580
  if (changes.length > 0) {
6366
- log9("modified,", {
6581
+ log10("modified,", {
6367
6582
  changes
6368
6583
  }, {
6369
- F: __dxlog_file14,
6584
+ F: __dxlog_file15,
6370
6585
  L: 175,
6371
6586
  S: void 0,
6372
6587
  C: (f, a) => f(...a)
@@ -6377,8 +6592,8 @@ var editor = () => [
6377
6592
  }
6378
6593
  ];
6379
6594
  } else if (cancel) {
6380
- log9("cancel", void 0, {
6381
- F: __dxlog_file14,
6595
+ log10("cancel", void 0, {
6596
+ F: __dxlog_file15,
6382
6597
  L: 178,
6383
6598
  S: void 0,
6384
6599
  C: (f, a) => f(...a)
@@ -6390,10 +6605,10 @@ var editor = () => [
6390
6605
  ];
6391
6606
 
6392
6607
  // src/extensions/outliner/menu.ts
6393
- import { EditorView as EditorView24, ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6394
- import { addEventListener } from "@dxos/async";
6608
+ import { EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6609
+ import { addEventListener as addEventListener2 } from "@dxos/async";
6395
6610
  var menu = (options = {}) => [
6396
- ViewPlugin16.fromClass(class {
6611
+ ViewPlugin17.fromClass(class {
6397
6612
  view;
6398
6613
  tag;
6399
6614
  rafId;
@@ -6413,7 +6628,7 @@ var menu = (options = {}) => [
6413
6628
  }
6414
6629
  container.appendChild(this.tag);
6415
6630
  const handler = () => this.scheduleUpdate();
6416
- this.cleanup = addEventListener(container, "scroll", handler);
6631
+ this.cleanup = addEventListener2(container, "scroll", handler);
6417
6632
  this.scheduleUpdate();
6418
6633
  }
6419
6634
  destroy() {
@@ -6455,7 +6670,7 @@ var menu = (options = {}) => [
6455
6670
  this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
6456
6671
  }
6457
6672
  }),
6458
- EditorView24.theme({
6673
+ EditorView25.theme({
6459
6674
  ".cm-popover-trigger": {
6460
6675
  position: "fixed",
6461
6676
  padding: "0",
@@ -6491,12 +6706,12 @@ var outliner = (_options = {}) => [
6491
6706
  listPaddingLeft: 8
6492
6707
  }),
6493
6708
  // Researve space for menu.
6494
- EditorView25.contentAttributes.of({
6495
- class: "is-full !mr-[3rem]"
6709
+ EditorView26.contentAttributes.of({
6710
+ class: "w-full !mr-[3rem]"
6496
6711
  })
6497
6712
  ];
6498
6713
  var decorations = () => [
6499
- ViewPlugin17.fromClass(class {
6714
+ ViewPlugin18.fromClass(class {
6500
6715
  decorations = Decoration12.none;
6501
6716
  constructor(view) {
6502
6717
  this.updateDecorations(view.state, view);
@@ -6521,7 +6736,7 @@ var decorations = () => [
6521
6736
  const lineTo = doc.lineAt(item.contentRange.to);
6522
6737
  const isSelected = selection.includes(item.index) || item === current;
6523
6738
  decorations2.push(Decoration12.line({
6524
- class: mx7("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6739
+ class: mx6("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6525
6740
  }).range(line.from, line.from));
6526
6741
  }
6527
6742
  }
@@ -6531,7 +6746,7 @@ var decorations = () => [
6531
6746
  decorations: (v) => v.decorations
6532
6747
  }),
6533
6748
  // Theme.
6534
- EditorView25.theme(Object.assign({
6749
+ EditorView26.theme(Object.assign({
6535
6750
  ".cm-list-item": {
6536
6751
  borderLeftWidth: "1px",
6537
6752
  borderRightWidth: "1px",
@@ -6556,38 +6771,67 @@ var decorations = () => [
6556
6771
  marginBottom: "2px"
6557
6772
  },
6558
6773
  ".cm-list-item-focused": {
6559
- borderColor: "var(--dx-neutralFocusIndicator)"
6774
+ borderColor: "var(--color-neutral-focus-indicator)"
6560
6775
  },
6561
6776
  "&:focus-within .cm-list-item-selected": {
6562
- borderColor: "var(--dx-separator)"
6777
+ borderColor: "var(--color-separator)"
6563
6778
  }
6564
6779
  }))
6565
6780
  ];
6566
6781
 
6567
6782
  // src/extensions/preview/preview.ts
6568
6783
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
6569
- import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField11 } from "@codemirror/state";
6570
- import { Decoration as Decoration13, EditorView as EditorView26, WidgetType as WidgetType8 } from "@codemirror/view";
6784
+ import { RangeSetBuilder as RangeSetBuilder6, StateEffect as StateEffect9, StateField as StateField11 } from "@codemirror/state";
6785
+ import { Decoration as Decoration13, EditorView as EditorView27, ViewPlugin as ViewPlugin19, WidgetType as WidgetType8 } from "@codemirror/view";
6786
+ import { DXN, Entity } from "@dxos/echo";
6787
+ var labelResolvedEffect = StateEffect9.define();
6571
6788
  var preview = (options = {}) => {
6789
+ const viewRef = {
6790
+ current: void 0
6791
+ };
6572
6792
  return [
6573
6793
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
6574
6794
  // "Block decorations may not be specified via plugins".
6575
6795
  StateField11.define({
6576
- create: (state) => buildDecorations3(state, options),
6796
+ create: (state) => buildDecorations3(state, options, viewRef),
6577
6797
  update: (decorations2, tr) => {
6578
- if (tr.docChanged) {
6579
- return buildDecorations3(tr.state, options);
6798
+ if (tr.docChanged || tr.effects.some((effect) => effect.is(labelResolvedEffect))) {
6799
+ return buildDecorations3(tr.state, options, viewRef);
6580
6800
  }
6581
6801
  return decorations2.map(tr.changes);
6582
6802
  },
6583
6803
  provide: (field) => [
6584
- EditorView26.decorations.from(field),
6585
- EditorView26.atomicRanges.of((view) => view.state.field(field))
6804
+ EditorView27.decorations.from(field),
6805
+ EditorView27.atomicRanges.of((view) => view.state.field(field))
6586
6806
  ]
6807
+ }),
6808
+ ViewPlugin19.define((view) => {
6809
+ viewRef.current = view;
6810
+ return {
6811
+ destroy() {
6812
+ viewRef.current = void 0;
6813
+ }
6814
+ };
6587
6815
  })
6588
6816
  ];
6589
6817
  };
6590
- var buildDecorations3 = (state, options) => {
6818
+ var resolveLabel = (db, dxnStr, viewRef) => {
6819
+ const dxn = DXN.tryParse(dxnStr);
6820
+ if (!dxn) {
6821
+ return;
6822
+ }
6823
+ const ref = db.makeRef(dxn);
6824
+ const target = ref.target;
6825
+ if (target) {
6826
+ return Entity.getLabel(target);
6827
+ }
6828
+ void ref.tryLoad().then(() => {
6829
+ viewRef.current?.dispatch({
6830
+ effects: labelResolvedEffect.of(void 0)
6831
+ });
6832
+ });
6833
+ };
6834
+ var buildDecorations3 = (state, options, viewRef) => {
6591
6835
  const builder = new RangeSetBuilder6();
6592
6836
  syntaxTree10(state).iterate({
6593
6837
  enter: (node) => {
@@ -6599,8 +6843,13 @@ var buildDecorations3 = (state, options) => {
6599
6843
  case "Link": {
6600
6844
  const link = getLinkRef(state, node.node);
6601
6845
  if (link) {
6846
+ const resolved = options.db ? resolveLabel(options.db, link.dxn, viewRef) : void 0;
6847
+ const displayLink = resolved ? {
6848
+ ...link,
6849
+ label: resolved
6850
+ } : link;
6602
6851
  builder.add(node.from, node.to, Decoration13.replace({
6603
- widget: new PreviewInlineWidget(options, link),
6852
+ widget: new PreviewInlineWidget(options, displayLink),
6604
6853
  side: 1
6605
6854
  }));
6606
6855
  }
@@ -6631,13 +6880,13 @@ var getLinkRef = (state, node) => {
6631
6880
  const mark = node.getChildren("LinkMark");
6632
6881
  const urlNode = node.getChild("URL");
6633
6882
  if (mark && urlNode) {
6634
- const url = state.sliceDoc(urlNode.from, urlNode.to);
6635
- if (url.startsWith("dxn:")) {
6883
+ const dxn = state.sliceDoc(urlNode.from, urlNode.to);
6884
+ if (dxn.startsWith("dxn:")) {
6636
6885
  const label = state.sliceDoc(mark[0].to, mark[1].from);
6637
6886
  return {
6638
6887
  block: state.sliceDoc(mark[0].from, mark[0].from + 1) === "!",
6639
6888
  label,
6640
- ref: url
6889
+ dxn
6641
6890
  };
6642
6891
  }
6643
6892
  }
@@ -6652,13 +6901,13 @@ var PreviewInlineWidget = class extends WidgetType8 {
6652
6901
  // return false;
6653
6902
  // }
6654
6903
  eq(other) {
6655
- return this._link.ref === other._link.ref && this._link.label === other._link.label;
6904
+ return this._link.dxn === other._link.dxn && this._link.label === other._link.label;
6656
6905
  }
6657
6906
  toDOM(_view) {
6658
6907
  const root = document.createElement("dx-anchor");
6659
6908
  root.classList.add("dx-tag--anchor");
6660
6909
  root.textContent = this._link.label;
6661
- root.setAttribute("refId", this._link.ref);
6910
+ root.setAttribute("dxn", this._link.dxn);
6662
6911
  return root;
6663
6912
  }
6664
6913
  };
@@ -6672,11 +6921,11 @@ var PreviewBlockWidget = class extends WidgetType8 {
6672
6921
  // return true;
6673
6922
  // }
6674
6923
  eq(other) {
6675
- return this._link.ref === other._link.ref;
6924
+ return this._link.dxn === other._link.dxn;
6676
6925
  }
6677
6926
  toDOM(_view) {
6678
6927
  const root = document.createElement("div");
6679
- root.classList.add("cm-preview-block", "density-fine");
6928
+ root.classList.add("cm-preview-block", "dx-density-fine");
6680
6929
  this._options.addBlockContainer?.({
6681
6930
  link: this._link,
6682
6931
  el: root
@@ -6692,7 +6941,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6692
6941
  };
6693
6942
 
6694
6943
  // src/extensions/replacer.ts
6695
- import { EditorView as EditorView27 } from "@codemirror/view";
6944
+ import { EditorView as EditorView28 } from "@codemirror/view";
6696
6945
  var defaultReplacements = [
6697
6946
  {
6698
6947
  input: "--",
@@ -6755,7 +7004,7 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6755
7004
  const sortedReplacements = [
6756
7005
  ...replacements
6757
7006
  ].sort((a, b) => b.input.length - a.input.length);
6758
- return EditorView27.inputHandler.of((view, from, to, insert) => {
7007
+ return EditorView28.inputHandler.of((view, from, to, insert) => {
6759
7008
  if (insert.length !== 1) {
6760
7009
  return false;
6761
7010
  }
@@ -6839,6 +7088,7 @@ var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6839
7088
  // src/extensions/tags/extended-markdown.ts
6840
7089
  import { xmlLanguage } from "@codemirror/lang-xml";
6841
7090
  import { parseMixed } from "@lezer/common";
7091
+ var escapeRegExpSource = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6842
7092
  var extendedMarkdown = ({ registry } = {}) => {
6843
7093
  return [
6844
7094
  createMarkdownExtensions({
@@ -6850,29 +7100,77 @@ var extendedMarkdown = ({ registry } = {}) => {
6850
7100
  {
6851
7101
  name: "SetextHeading",
6852
7102
  parse: () => false
6853
- }
7103
+ },
7104
+ // Custom XML block parser that keeps registered tags as a single HTMLBlock
7105
+ // even when their content contains blank lines.
7106
+ ...xmlBlockParsers(registry)
6854
7107
  ]
6855
7108
  }
6856
7109
  ]
6857
7110
  })
6858
7111
  ];
6859
7112
  };
6860
- var mixedParser = (registry) => {
7113
+ var xmlBlockParsers = (registry) => {
6861
7114
  const customTags = Object.keys(registry ?? {});
6862
- const tagPattern = new RegExp(`<(${customTags.join("|")})`);
6863
- return parseMixed((node, input) => {
6864
- switch (node.name) {
6865
- // Ignore XML inside of fenced and inline code.
6866
- case "FencedCode":
6867
- case "InlineCode": {
6868
- return null;
6869
- }
6870
- // Parse multi-line HTML blocks.
6871
- // case 'XMLBlock':
6872
- case "HTMLBlock": {
6873
- return {
6874
- parser: xmlLanguage.parser
6875
- };
7115
+ if (customTags.length === 0) {
7116
+ return [];
7117
+ }
7118
+ const tagPattern = customTags.map(escapeRegExpSource).join("|");
7119
+ const openPattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?>|^\\s*<(${tagPattern})\\s*/>`);
7120
+ return [
7121
+ {
7122
+ name: "XMLBlock",
7123
+ before: "HTMLBlock",
7124
+ parse: (cx, line) => {
7125
+ const match = openPattern.exec(line.text);
7126
+ if (!match) {
7127
+ return false;
7128
+ }
7129
+ if (match[3]) {
7130
+ const end2 = cx.lineStart + line.text.length;
7131
+ cx.addElement(cx.elt("HTMLBlock", cx.lineStart, end2));
7132
+ cx.nextLine();
7133
+ return true;
7134
+ }
7135
+ const tagName = match[1];
7136
+ const closeTag = `</${tagName}>`;
7137
+ const start = cx.lineStart;
7138
+ if (line.text.includes(closeTag)) {
7139
+ cx.addElement(cx.elt("HTMLBlock", start, start + line.text.length));
7140
+ cx.nextLine();
7141
+ return true;
7142
+ }
7143
+ let end = cx.lineStart + line.text.length;
7144
+ while (cx.nextLine()) {
7145
+ end = cx.lineStart + line.text.length;
7146
+ if (line.text.includes(closeTag)) {
7147
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7148
+ cx.nextLine();
7149
+ return true;
7150
+ }
7151
+ }
7152
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7153
+ return true;
7154
+ }
7155
+ }
7156
+ ];
7157
+ };
7158
+ var mixedParser = (registry) => {
7159
+ const customTags = Object.keys(registry ?? {});
7160
+ const tagPattern = new RegExp(`<(${customTags.join("|")})`);
7161
+ return parseMixed((node, input) => {
7162
+ switch (node.name) {
7163
+ // Ignore XML inside of fenced and inline code.
7164
+ case "FencedCode":
7165
+ case "InlineCode": {
7166
+ return null;
7167
+ }
7168
+ // Parse multi-line HTML blocks.
7169
+ // case 'XMLBlock':
7170
+ case "HTMLBlock": {
7171
+ return {
7172
+ parser: xmlLanguage.parser
7173
+ };
6876
7174
  }
6877
7175
  // Parse paragraphs that contain custom XML tags.
6878
7176
  // TODO(burdon): Entire paragraph should be parsed as XML.
@@ -6890,212 +7188,560 @@ var mixedParser = (registry) => {
6890
7188
  });
6891
7189
  };
6892
7190
 
6893
- // src/extensions/tags/streamer.ts
6894
- import { StateEffect as StateEffect9, StateField as StateField12 } from "@codemirror/state";
6895
- import { Decoration as Decoration14, EditorView as EditorView28, ViewPlugin as ViewPlugin18, WidgetType as WidgetType9 } from "@codemirror/view";
6896
- import { Domino as Domino3 } from "@dxos/ui";
6897
- import { isTruthy as isTruthy4 } from "@dxos/util";
6898
- var BLINK_RATE = 2e3;
6899
- var streamer = (options = {}) => {
6900
- return [
6901
- options.cursor && cursor(),
6902
- options.fadeIn && fadeIn(typeof options.fadeIn === "object" ? options.fadeIn : {})
6903
- ].filter(isTruthy4);
6904
- };
6905
- var cursor = () => {
6906
- const hideCursor = StateEffect9.define();
6907
- const showCursor = StateField12.define({
6908
- create: () => true,
6909
- update: (value, tr) => {
7191
+ // src/extensions/tags/fader.ts
7192
+ import { StateEffect as StateEffect10, StateField as StateField12 } from "@codemirror/state";
7193
+ import { Decoration as Decoration14, EditorView as EditorView29, ViewPlugin as ViewPlugin20 } from "@codemirror/view";
7194
+ var DEFAULT_REMOVAL_DELAY = 5e3;
7195
+ var DEFAULT_COALESCE_WINDOW = 100;
7196
+ var CLEANUP_INTERVAL = 1e3;
7197
+ var fader = (options = {}) => {
7198
+ const removalDelay = DEFAULT_REMOVAL_DELAY;
7199
+ const coalesceWindow = options.coalesce ?? DEFAULT_COALESCE_WINDOW;
7200
+ let lastCount = -1;
7201
+ const log12 = (expiries) => {
7202
+ if (expiries.length !== lastCount) {
7203
+ lastCount = expiries.length;
7204
+ }
7205
+ };
7206
+ const dequeue = StateEffect10.define();
7207
+ const fadeField = StateField12.define({
7208
+ create: () => ({
7209
+ decorations: Decoration14.none,
7210
+ expiries: [],
7211
+ batchStart: 0
7212
+ }),
7213
+ update: ({ decorations: decorations2, expiries, batchStart }, tr) => {
6910
7214
  for (const effect of tr.effects) {
6911
- if (effect.is(hideCursor)) {
6912
- return false;
7215
+ if (effect.is(dequeue)) {
7216
+ const now2 = effect.value;
7217
+ let removeCount = 0;
7218
+ while (removeCount < expiries.length && expiries[removeCount] <= now2) {
7219
+ removeCount++;
7220
+ }
7221
+ if (removeCount > 0) {
7222
+ expiries = expiries.slice(removeCount);
7223
+ let skipped = 0;
7224
+ decorations2 = decorations2.update({
7225
+ filter: () => {
7226
+ if (skipped < removeCount) {
7227
+ skipped++;
7228
+ return false;
7229
+ }
7230
+ return true;
7231
+ }
7232
+ });
7233
+ }
6913
7234
  }
6914
7235
  }
6915
- if (tr.docChanged) {
6916
- return true;
7236
+ if (!tr.docChanged) {
7237
+ log12(expiries);
7238
+ return {
7239
+ decorations: decorations2,
7240
+ expiries,
7241
+ batchStart
7242
+ };
6917
7243
  }
6918
- return value;
6919
- }
7244
+ let isReset = tr.state.doc.length === 0;
7245
+ if (!isReset && tr.startState.doc.length > 0) {
7246
+ tr.changes.iterChanges((fromA, toA) => {
7247
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7248
+ isReset = true;
7249
+ }
7250
+ });
7251
+ }
7252
+ if (isReset) {
7253
+ log12([]);
7254
+ return {
7255
+ decorations: Decoration14.none,
7256
+ expiries: [],
7257
+ batchStart: 0
7258
+ };
7259
+ }
7260
+ const now = Date.now();
7261
+ const add = [];
7262
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7263
+ if (toA === tr.startState.doc.length && inserted.length > 0) {
7264
+ add.push({
7265
+ from: fromB,
7266
+ to: toB
7267
+ });
7268
+ }
7269
+ });
7270
+ if (add.length > 0) {
7271
+ const canCoalesce = expiries.length > 0 && batchStart > 0 && now - batchStart < coalesceWindow;
7272
+ if (canCoalesce) {
7273
+ let lastFrom = -1;
7274
+ let lastTo = -1;
7275
+ decorations2.between(0, tr.state.doc.length, (from, to) => {
7276
+ lastFrom = from;
7277
+ lastTo = to;
7278
+ });
7279
+ if (lastFrom >= 0) {
7280
+ decorations2 = decorations2.update({
7281
+ filter: (from, to) => !(from === lastFrom && to === lastTo)
7282
+ });
7283
+ const mergedFrom = Math.min(lastFrom, add[0].from);
7284
+ const mergedTo = add[add.length - 1].to;
7285
+ decorations2 = decorations2.update({
7286
+ add: [
7287
+ Decoration14.mark({
7288
+ class: "cm-fader"
7289
+ }).range(mergedFrom, mergedTo)
7290
+ ]
7291
+ });
7292
+ expiries = [
7293
+ ...expiries.slice(0, -1),
7294
+ now + removalDelay
7295
+ ];
7296
+ }
7297
+ } else {
7298
+ batchStart = now;
7299
+ expiries = [
7300
+ ...expiries,
7301
+ now + removalDelay
7302
+ ];
7303
+ decorations2 = decorations2.update({
7304
+ add: add.map(({ from, to }) => Decoration14.mark({
7305
+ class: "cm-fader"
7306
+ }).range(from, to))
7307
+ });
7308
+ }
7309
+ }
7310
+ log12(expiries);
7311
+ return {
7312
+ decorations: decorations2,
7313
+ expiries,
7314
+ batchStart
7315
+ };
7316
+ },
7317
+ provide: (f) => EditorView29.decorations.from(f, (value) => value.decorations)
6920
7318
  });
6921
- const timerPlugin = ViewPlugin18.fromClass(class {
7319
+ const cleanup = ViewPlugin20.fromClass(class {
6922
7320
  view;
6923
- timer;
7321
+ #timer;
6924
7322
  constructor(view) {
6925
7323
  this.view = view;
7324
+ this.#schedule();
6926
7325
  }
6927
- update(update2) {
6928
- if (update2.docChanged) {
6929
- clearTimeout(this.timer);
6930
- this.timer = setTimeout(() => {
6931
- this.view.dispatch({
6932
- effects: hideCursor.of(null)
6933
- });
6934
- }, BLINK_RATE);
7326
+ update() {
7327
+ this.#schedule();
7328
+ }
7329
+ #schedule() {
7330
+ const { expiries } = this.view.state.field(fadeField);
7331
+ if (expiries.length === 0) {
7332
+ clearTimeout(this.#timer);
7333
+ this.#timer = void 0;
7334
+ return;
6935
7335
  }
7336
+ if (this.#timer !== void 0) {
7337
+ return;
7338
+ }
7339
+ const delay = Math.max(CLEANUP_INTERVAL, expiries[0] - Date.now());
7340
+ this.#timer = setTimeout(() => {
7341
+ this.#timer = void 0;
7342
+ this.view.dispatch({
7343
+ effects: dequeue.of(Date.now())
7344
+ });
7345
+ }, delay);
6936
7346
  }
6937
7347
  destroy() {
6938
- clearTimeout(this.timer);
7348
+ clearTimeout(this.#timer);
6939
7349
  }
6940
7350
  });
6941
- const cursorDecoration = StateField12.define({
6942
- create: () => Decoration14.none,
6943
- update: (_decorations, tr) => {
6944
- const show = tr.state.field(showCursor);
6945
- if (!show) {
6946
- return Decoration14.none;
6947
- }
6948
- const endPos = tr.state.doc.length;
6949
- return Decoration14.set([
6950
- Decoration14.widget({
6951
- widget: new CursorWidget(),
6952
- side: 1
6953
- }).range(endPos)
6954
- ]);
6955
- },
6956
- provide: (f) => EditorView28.decorations.from(f)
6957
- });
6958
7351
  return [
6959
- showCursor,
6960
- timerPlugin,
6961
- cursorDecoration
7352
+ fadeField,
7353
+ cleanup,
7354
+ EditorView29.theme({
7355
+ ".cm-fader": {
7356
+ animation: "fader 1s ease-out forwards"
7357
+ },
7358
+ "@keyframes fader": {
7359
+ "0%": {
7360
+ textShadow: "0 0 16px rgba(100, 200, 255, 1), 0 0 32px rgba(100, 200, 255, 0.6)"
7361
+ },
7362
+ "100%": {}
7363
+ }
7364
+ })
6962
7365
  ];
6963
7366
  };
6964
- var CursorWidget = class extends WidgetType9 {
6965
- toDOM() {
6966
- const inner = Domino3.of("span").text("\u258F").style({
6967
- animation: "blink 2s infinite"
6968
- });
6969
- return Domino3.of("span").style({
6970
- opacity: "0.8"
6971
- }).children(inner).root;
6972
- }
6973
- };
6974
- var fadeIn = (options = {}) => {
6975
- const FADE_IN_DURATION = 1e3;
6976
- const DEFAULT_REMOVAL_DELAY = 5e3;
6977
- const removalDelay = options.removalDelay ?? DEFAULT_REMOVAL_DELAY;
6978
- const removeDecoration = StateEffect9.define();
6979
- const fadeField = StateField12.define({
6980
- create: () => Decoration14.none,
6981
- update: (decorations2, tr) => {
6982
- let next = decorations2;
7367
+
7368
+ // src/extensions/tags/wire.ts
7369
+ import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7370
+ import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7371
+ import { Domino as Domino3 } from "@dxos/ui";
7372
+ var wireBypass = Annotation3.define();
7373
+ var DEFAULT_RATE = 200;
7374
+ var CURSOR_LINGER = 3e3;
7375
+ var wire = (options = {}) => {
7376
+ const rate = options.rate ?? DEFAULT_RATE;
7377
+ const interval = 1e3 / rate;
7378
+ const streamingTags = options.streamingTags ?? /* @__PURE__ */ new Set();
7379
+ const suppressAppend = StateEffect11.define();
7380
+ const insertChunk = StateEffect11.define();
7381
+ const bufferField = StateField13.define({
7382
+ create: () => ({
7383
+ text: "",
7384
+ insertAt: 0
7385
+ }),
7386
+ update: (value, tr) => {
7387
+ let { text, insertAt } = value;
6983
7388
  for (const effect of tr.effects) {
6984
- if (effect.is(removeDecoration)) {
6985
- const target = effect.value;
6986
- next = next.update({
6987
- filter: (from, to) => !(from === target.from && to === target.to)
7389
+ if (effect.is(suppressAppend)) {
7390
+ text += effect.value.text;
7391
+ if (text.length === effect.value.text.length) {
7392
+ insertAt = effect.value.from;
7393
+ }
7394
+ }
7395
+ if (effect.is(insertChunk)) {
7396
+ text = text.slice(effect.value.text.length);
7397
+ insertAt = effect.value.from + effect.value.text.length;
7398
+ }
7399
+ }
7400
+ if (tr.docChanged) {
7401
+ let isReset = tr.state.doc.length === 0;
7402
+ if (!isReset && tr.startState.doc.length > 0) {
7403
+ tr.changes.iterChanges((fromA, toA) => {
7404
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7405
+ isReset = true;
7406
+ }
6988
7407
  });
6989
7408
  }
7409
+ if (isReset) {
7410
+ return {
7411
+ text: "",
7412
+ insertAt: 0
7413
+ };
7414
+ }
7415
+ if (!tr.effects.some((effect) => effect.is(insertChunk))) {
7416
+ insertAt = tr.changes.mapPos(Math.min(insertAt, tr.startState.doc.length));
7417
+ }
6990
7418
  }
6991
- if (!tr.docChanged) {
6992
- return next;
7419
+ return {
7420
+ text,
7421
+ insertAt
7422
+ };
7423
+ }
7424
+ });
7425
+ const filter = EditorState3.transactionFilter.of((tr) => {
7426
+ if (!tr.docChanged) {
7427
+ return tr;
7428
+ }
7429
+ if (tr.annotation(wireBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7430
+ return tr;
7431
+ }
7432
+ let appendedText = "";
7433
+ let appendFrom = -1;
7434
+ let isAppendOnly = true;
7435
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
7436
+ if (toA === tr.startState.doc.length && fromA === toA && inserted.length > 0) {
7437
+ appendedText += inserted.sliceString(0);
7438
+ if (appendFrom === -1) {
7439
+ appendFrom = fromA;
7440
+ }
7441
+ } else {
7442
+ isAppendOnly = false;
6993
7443
  }
6994
- let isReset = tr.state.doc.length === 0;
6995
- if (!isReset) {
6996
- tr.changes.iterChanges((fromA, toA) => {
6997
- if (fromA === 0 && toA === tr.startState.doc.length) {
6998
- isReset = true;
6999
- }
7000
- });
7444
+ });
7445
+ if (!isAppendOnly || appendedText.length === 0) {
7446
+ return tr;
7447
+ }
7448
+ return {
7449
+ changes: ChangeSet2.empty(tr.startState.doc.length),
7450
+ effects: suppressAppend.of({
7451
+ from: appendFrom,
7452
+ text: appendedText
7453
+ })
7454
+ };
7455
+ });
7456
+ const drainPlugin = ViewPlugin21.fromClass(class {
7457
+ view;
7458
+ #timer;
7459
+ #activeStreamTag = null;
7460
+ constructor(view) {
7461
+ this.view = view;
7462
+ this.#start();
7463
+ }
7464
+ update(update2) {
7465
+ const buffer = update2.state.field(bufferField);
7466
+ if (buffer.text.length === 0) {
7467
+ this.#activeStreamTag = null;
7001
7468
  }
7002
- if (isReset) {
7003
- return Decoration14.none;
7469
+ if (buffer.text.length > 0 && this.#timer === void 0) {
7470
+ this.#start();
7004
7471
  }
7005
- const add = [];
7006
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7007
- if (fromB === 0 && toB === inserted.length) {
7472
+ }
7473
+ #start() {
7474
+ this.#timer = setInterval(() => {
7475
+ const { text, insertAt } = this.view.state.field(bufferField);
7476
+ if (text.length === 0) {
7477
+ clearInterval(this.#timer);
7478
+ this.#timer = void 0;
7008
7479
  return;
7009
7480
  }
7010
- if (toA === tr.startState.doc.length && inserted.length > 0) {
7011
- add.push(Decoration14.mark({
7012
- class: "cm-fade-in"
7013
- }).range(fromB, toB));
7481
+ const result = flushable(text, streamingTags, this.#activeStreamTag);
7482
+ if (result.count === 0) {
7483
+ return;
7014
7484
  }
7015
- });
7016
- return next.update({
7017
- add
7018
- });
7485
+ if (result.enterTag) {
7486
+ this.#activeStreamTag = result.enterTag;
7487
+ }
7488
+ if (result.exitTag) {
7489
+ this.#activeStreamTag = null;
7490
+ }
7491
+ const chunk = text.slice(0, result.count);
7492
+ this.view.dispatch({
7493
+ changes: {
7494
+ from: insertAt,
7495
+ insert: chunk
7496
+ },
7497
+ effects: insertChunk.of({
7498
+ from: insertAt,
7499
+ text: chunk
7500
+ })
7501
+ });
7502
+ }, interval);
7503
+ }
7504
+ destroy() {
7505
+ clearInterval(this.#timer);
7506
+ }
7507
+ });
7508
+ return [
7509
+ bufferField,
7510
+ filter,
7511
+ drainPlugin,
7512
+ options.cursor && wireCursor(bufferField)
7513
+ ].filter(Boolean);
7514
+ };
7515
+ var wireCursor = (bufferField) => {
7516
+ const hideCursor = StateEffect11.define();
7517
+ const visibilityField = StateField13.define({
7518
+ create: () => ({
7519
+ visible: false,
7520
+ insertAt: 0,
7521
+ lastNonWsAt: 0
7522
+ }),
7523
+ update: (value, tr) => {
7524
+ const { text, insertAt } = tr.state.field(bufferField);
7525
+ if (text.length > 0) {
7526
+ let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));
7527
+ if (tr.docChanged) {
7528
+ tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
7529
+ const chunk = inserted.sliceString(0);
7530
+ if (chunk.trim().length > 0) {
7531
+ lastNonWsAt = _fromB + chunk.length;
7532
+ }
7533
+ });
7534
+ }
7535
+ return {
7536
+ visible: true,
7537
+ insertAt,
7538
+ lastNonWsAt
7539
+ };
7540
+ }
7541
+ for (const effect of tr.effects) {
7542
+ if (effect.is(hideCursor)) {
7543
+ return {
7544
+ ...value,
7545
+ visible: false
7546
+ };
7547
+ }
7548
+ }
7549
+ return value;
7550
+ }
7551
+ });
7552
+ const decorationField = StateField13.define({
7553
+ create: () => Decoration15.none,
7554
+ update: (_decorations, tr) => {
7555
+ const { visible, insertAt, lastNonWsAt } = tr.state.field(visibilityField);
7556
+ if (!visible) {
7557
+ return Decoration15.none;
7558
+ }
7559
+ const { text } = tr.state.field(bufferField);
7560
+ const cursorAt = text.length > 0 ? insertAt : lastNonWsAt;
7561
+ const pos = Math.min(cursorAt, tr.state.doc.length);
7562
+ return Decoration15.set([
7563
+ Decoration15.widget({
7564
+ widget: new CursorWidget(),
7565
+ side: 1
7566
+ }).range(pos)
7567
+ ]);
7019
7568
  },
7020
- provide: (f) => EditorView28.decorations.from(f)
7569
+ provide: (field) => EditorView30.decorations.from(field)
7021
7570
  });
7022
- const timerPlugin = ViewPlugin18.fromClass(class {
7571
+ const timerPlugin = ViewPlugin21.fromClass(class {
7023
7572
  view;
7024
- // Map a simple key "from-to" to timer id.
7025
- _timers = /* @__PURE__ */ new Map();
7573
+ #timer;
7026
7574
  constructor(view) {
7027
7575
  this.view = view;
7028
7576
  }
7029
7577
  update(update2) {
7030
- if (!update2.docChanged) {
7031
- return;
7032
- }
7033
- update2.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7034
- if (toA !== update2.startState.doc.length || inserted.length === 0) {
7035
- return;
7036
- }
7037
- const key = `${fromB}-${toB}`;
7038
- if (this._timers.has(key)) {
7039
- clearTimeout(this._timers.get(key));
7040
- }
7041
- const totalDelay = FADE_IN_DURATION + removalDelay;
7042
- const id = setTimeout(() => {
7578
+ const { text } = update2.state.field(bufferField);
7579
+ const { visible } = update2.state.field(visibilityField);
7580
+ if (text.length > 0) {
7581
+ clearTimeout(this.#timer);
7582
+ this.#timer = void 0;
7583
+ } else if (visible && this.#timer === void 0) {
7584
+ this.#timer = setTimeout(() => {
7043
7585
  this.view.dispatch({
7044
- effects: removeDecoration.of({
7045
- from: fromB,
7046
- to: toB
7047
- })
7586
+ effects: hideCursor.of(null)
7048
7587
  });
7049
- this._timers.delete(key);
7050
- }, totalDelay);
7051
- this._timers.set(key, id);
7052
- });
7588
+ this.#timer = void 0;
7589
+ }, CURSOR_LINGER);
7590
+ }
7053
7591
  }
7054
7592
  destroy() {
7055
- for (const id of this._timers.values()) {
7056
- clearTimeout(id);
7057
- }
7058
- this._timers.clear();
7593
+ clearTimeout(this.#timer);
7059
7594
  }
7060
7595
  });
7061
7596
  return [
7062
- fadeField,
7063
- timerPlugin,
7064
- EditorView28.theme({
7065
- ".cm-line > span": {
7066
- opacity: "0.8"
7067
- },
7068
- ".cm-fade-in": {
7069
- animation: "fade-in 3s ease-out forwards"
7070
- },
7071
- "@keyframes fade-in": {
7072
- "0%": {
7073
- opacity: "0"
7074
- },
7075
- "80%": {
7076
- opacity: "1"
7077
- },
7078
- "100%": {
7079
- opacity: "0.8"
7080
- }
7081
- }
7082
- })
7597
+ visibilityField,
7598
+ decorationField,
7599
+ timerPlugin
7083
7600
  ];
7084
7601
  };
7602
+ var CursorWidget = class extends WidgetType9 {
7603
+ toDOM() {
7604
+ const inner = Domino3.of("span").text("\u2217").style({
7605
+ animation: "blink 1s infinite",
7606
+ animationDelay: "250ms"
7607
+ });
7608
+ return Domino3.of("span").style({
7609
+ opacity: "0.8"
7610
+ }).append(inner).root;
7611
+ }
7612
+ };
7613
+ var OPENING_TAG_NAME = /^<([a-zA-Z][\w-]*)/;
7614
+ var escapeRegExpSource2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7615
+ var flushable = (buffer, streamingTags, activeStreamTag) => {
7616
+ if (buffer.length === 0) {
7617
+ return {
7618
+ count: 0
7619
+ };
7620
+ }
7621
+ if (activeStreamTag) {
7622
+ const closeTag = `</${activeStreamTag}>`;
7623
+ if (buffer.startsWith(closeTag)) {
7624
+ return {
7625
+ count: closeTag.length,
7626
+ exitTag: true
7627
+ };
7628
+ }
7629
+ if (buffer[0] === "<") {
7630
+ return {
7631
+ count: xmlElementLength(buffer)
7632
+ };
7633
+ }
7634
+ return {
7635
+ count: 1
7636
+ };
7637
+ }
7638
+ const ch = buffer[0];
7639
+ if (ch === "<") {
7640
+ const nameMatch = buffer.match(OPENING_TAG_NAME);
7641
+ if (nameMatch && streamingTags.has(nameMatch[1])) {
7642
+ const close = buffer.indexOf(">");
7643
+ if (close === -1) {
7644
+ return {
7645
+ count: 0
7646
+ };
7647
+ }
7648
+ if (buffer[close - 1] === "/") {
7649
+ return {
7650
+ count: close + 1
7651
+ };
7652
+ }
7653
+ return {
7654
+ count: close + 1,
7655
+ enterTag: nameMatch[1]
7656
+ };
7657
+ }
7658
+ return {
7659
+ count: xmlElementLength(buffer)
7660
+ };
7661
+ }
7662
+ if (ch === "!" && buffer.length > 1 && buffer[1] === "[") {
7663
+ return {
7664
+ count: linkLength(buffer, 1)
7665
+ };
7666
+ }
7667
+ if (ch === "[") {
7668
+ return {
7669
+ count: linkLength(buffer, 0)
7670
+ };
7671
+ }
7672
+ return {
7673
+ count: 1
7674
+ };
7675
+ };
7676
+ var xmlElementLength = (buffer) => {
7677
+ const close = buffer.indexOf(">");
7678
+ if (close === -1) {
7679
+ return 0;
7680
+ }
7681
+ if (buffer[close - 1] === "/") {
7682
+ return close + 1;
7683
+ }
7684
+ if (buffer[1] === "/") {
7685
+ return close + 1;
7686
+ }
7687
+ const nameMatch = buffer.match(OPENING_TAG_NAME);
7688
+ if (!nameMatch) {
7689
+ return 1;
7690
+ }
7691
+ const tagName = nameMatch[1];
7692
+ let depth = 0;
7693
+ const tagPattern = new RegExp(`<(/?)${escapeRegExpSource2(tagName)}(\\s[^>]*)?>`, "g");
7694
+ let match;
7695
+ while ((match = tagPattern.exec(buffer)) !== null) {
7696
+ const isSelfClosing = match[0].endsWith("/>");
7697
+ const isClosing = match[1] === "/";
7698
+ if (isSelfClosing) {
7699
+ if (depth === 0) {
7700
+ return match.index + match[0].length;
7701
+ }
7702
+ } else if (isClosing) {
7703
+ depth--;
7704
+ if (depth === 0) {
7705
+ return match.index + match[0].length;
7706
+ }
7707
+ } else {
7708
+ depth++;
7709
+ }
7710
+ }
7711
+ return 0;
7712
+ };
7713
+ var linkLength = (buffer, offset) => {
7714
+ const bracketClose = buffer.indexOf("]", offset + 1);
7715
+ if (bracketClose === -1) {
7716
+ return 0;
7717
+ }
7718
+ if (bracketClose + 1 >= buffer.length) {
7719
+ return 0;
7720
+ }
7721
+ if (buffer[bracketClose + 1] !== "(") {
7722
+ return 1;
7723
+ }
7724
+ const parenClose = buffer.indexOf(")", bracketClose + 2);
7725
+ if (parenClose === -1) {
7726
+ return 0;
7727
+ }
7728
+ return parenClose + 1;
7729
+ };
7085
7730
 
7086
7731
  // src/extensions/tags/xml-tags.ts
7087
7732
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7088
- import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect10, StateField as StateField13 } from "@codemirror/state";
7089
- import { Decoration as Decoration15, EditorView as EditorView29, ViewPlugin as ViewPlugin19, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7733
+ import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7734
+ import { Decoration as Decoration16, EditorView as EditorView31, ViewPlugin as ViewPlugin22, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7090
7735
  import { invariant as invariant7 } from "@dxos/invariant";
7091
- import { log as log10 } from "@dxos/log";
7736
+ import { log as log11 } from "@dxos/log";
7737
+ import { Domino as Domino4 } from "@dxos/ui";
7092
7738
 
7093
7739
  // src/extensions/tags/xml-util.ts
7094
7740
  import { invariant as invariant6 } from "@dxos/invariant";
7095
- var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7741
+ var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7096
7742
  var nodeToJson = (state, node) => {
7097
7743
  invariant6(node.type.name === "Element", "Node is not an Element", {
7098
- F: __dxlog_file15,
7744
+ F: __dxlog_file16,
7099
7745
  L: 18,
7100
7746
  S: void 0,
7101
7747
  A: [
@@ -7159,17 +7805,19 @@ var nodeToJson = (state, node) => {
7159
7805
  };
7160
7806
 
7161
7807
  // src/extensions/tags/xml-tags.ts
7162
- var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7163
- var navigatePreviousEffect = StateEffect10.define();
7164
- var navigateNextEffect = StateEffect10.define();
7808
+ var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7809
+ var navigatePreviousEffect = StateEffect12.define();
7810
+ var navigateNextEffect = StateEffect12.define();
7165
7811
  var getXmlTextChild = (children) => {
7166
7812
  const child = children?.[0];
7167
7813
  return typeof child === "string" ? child : null;
7168
7814
  };
7169
- var xmlTagContextEffect = StateEffect10.define();
7170
- var xmlTagResetEffect = StateEffect10.define();
7171
- var xmlTagUpdateEffect = StateEffect10.define();
7172
- var widgetContextStateField = StateField13.define({
7815
+ var xmlWidgetId = (explicit, fallback) => typeof explicit === "string" && explicit.length > 0 ? explicit : fallback;
7816
+ var escapeRegExpSource3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7817
+ var xmlTagContextEffect = StateEffect12.define();
7818
+ var xmlTagResetEffect = StateEffect12.define();
7819
+ var xmlTagUpdateEffect = StateEffect12.define();
7820
+ var widgetContextStateField = StateField14.define({
7173
7821
  create: () => void 0,
7174
7822
  update: (value, tr) => {
7175
7823
  for (const effect of tr.effects) {
@@ -7180,7 +7828,7 @@ var widgetContextStateField = StateField13.define({
7180
7828
  return value;
7181
7829
  }
7182
7830
  });
7183
- var widgetStateMapStateField = StateField13.define({
7831
+ var widgetStateMapStateField = StateField14.define({
7184
7832
  create: () => ({}),
7185
7833
  update: (map, tr) => {
7186
7834
  for (const effect of tr.effects) {
@@ -7189,12 +7837,12 @@ var widgetStateMapStateField = StateField13.define({
7189
7837
  }
7190
7838
  if (effect.is(xmlTagUpdateEffect)) {
7191
7839
  const { id, value } = effect.value;
7192
- log10("widget updated", {
7840
+ log11("widget updated", {
7193
7841
  id,
7194
7842
  value
7195
7843
  }, {
7196
- F: __dxlog_file16,
7197
- L: 153,
7844
+ F: __dxlog_file17,
7845
+ L: 184,
7198
7846
  S: void 0,
7199
7847
  C: (f, a) => f(...a)
7200
7848
  });
@@ -7224,12 +7872,12 @@ var createWidgetMap = (setWidgets) => {
7224
7872
  const widgets = /* @__PURE__ */ new Map();
7225
7873
  const notifier = {
7226
7874
  mounted: (state) => {
7227
- log10("widget mounted", {
7875
+ log11("widget mounted", {
7228
7876
  id: state.id,
7229
7877
  tag: state.props._tag
7230
7878
  }, {
7231
- F: __dxlog_file16,
7232
- L: 206,
7879
+ F: __dxlog_file17,
7880
+ L: 237,
7233
7881
  S: void 0,
7234
7882
  C: (f, a) => f(...a)
7235
7883
  });
@@ -7240,12 +7888,12 @@ var createWidgetMap = (setWidgets) => {
7240
7888
  },
7241
7889
  unmounted: (id) => {
7242
7890
  const state = widgets.get(id);
7243
- log10("widget unmounted", {
7891
+ log11("widget unmounted", {
7244
7892
  id,
7245
7893
  tag: state?.props._tag
7246
7894
  }, {
7247
- F: __dxlog_file16,
7248
- L: 212,
7895
+ F: __dxlog_file17,
7896
+ L: 243,
7249
7897
  S: void 0,
7250
7898
  C: (f, a) => f(...a)
7251
7899
  });
@@ -7278,7 +7926,7 @@ var keyHandlers = keymap13.of([
7278
7926
  }
7279
7927
  ]);
7280
7928
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7281
- return EditorView29.updateListener.of((update2) => {
7929
+ return EditorView31.updateListener.of((update2) => {
7282
7930
  update2.transactions.forEach((transaction) => {
7283
7931
  for (const effect of transaction.effects) {
7284
7932
  if (effect.is(navigatePreviousEffect)) {
@@ -7306,11 +7954,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7306
7954
  anchor: line.from,
7307
7955
  head: line.from
7308
7956
  },
7309
- effects: scrollToLineEffect.of({
7310
- line: line.number,
7311
- options: {
7312
- offset: -16
7313
- }
7957
+ effects: scrollerLineEffect.of({
7958
+ line: line.number - 1,
7959
+ offset: -16
7314
7960
  })
7315
7961
  });
7316
7962
  continue;
@@ -7341,11 +7987,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7341
7987
  anchor: line.to,
7342
7988
  head: line.to
7343
7989
  },
7344
- effects: scrollToLineEffect.of({
7345
- line: line.number,
7346
- options: {
7347
- offset: -16
7348
- }
7990
+ effects: scrollerLineEffect.of({
7991
+ line: line.number - 1,
7992
+ offset: -16
7349
7993
  })
7350
7994
  });
7351
7995
  } else {
@@ -7355,11 +7999,9 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7355
7999
  anchor: line.to,
7356
8000
  head: line.to
7357
8001
  },
7358
- effects: scrollToLineEffect.of({
7359
- line: line.number,
7360
- options: {
7361
- position: "end"
7362
- }
8002
+ effects: scrollerLineEffect.of({
8003
+ line: line.number - 1,
8004
+ position: "end"
7363
8005
  })
7364
8006
  });
7365
8007
  }
@@ -7369,7 +8011,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7369
8011
  });
7370
8012
  });
7371
8013
  };
7372
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin19.fromClass(class {
8014
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin22.fromClass(class {
7373
8015
  update(update2) {
7374
8016
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
7375
8017
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -7396,19 +8038,25 @@ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin1
7396
8038
  }
7397
8039
  }
7398
8040
  });
7399
- var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.define({
8041
+ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.define({
7400
8042
  create: (state) => {
7401
8043
  return buildDecorations4(state, {
7402
8044
  from: 0,
7403
8045
  to: state.doc.length
7404
8046
  }, registry, notifier);
7405
8047
  },
7406
- update: ({ from, decorations: decorations2 }, tr) => {
8048
+ update: ({ from, streamingFrom, decorations: decorations2 }, tr) => {
7407
8049
  for (const effect of tr.effects) {
7408
8050
  if (effect.is(xmlTagResetEffect)) {
8051
+ if (tr.docChanged) {
8052
+ return buildDecorations4(tr.state, {
8053
+ from: 0,
8054
+ to: tr.state.doc.length
8055
+ }, registry, notifier);
8056
+ }
7409
8057
  return {
7410
8058
  from: 0,
7411
- decorations: Decoration15.none
8059
+ decorations: Decoration16.none
7412
8060
  };
7413
8061
  }
7414
8062
  }
@@ -7416,12 +8064,12 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7416
8064
  const { state } = tr;
7417
8065
  const reset = tr.changes.touchesRange(0, from);
7418
8066
  if (reset) {
7419
- log10("document reset", {
8067
+ log11("document reset", {
7420
8068
  from,
7421
8069
  to: state.doc.length
7422
8070
  }, {
7423
- F: __dxlog_file16,
7424
- L: 371,
8071
+ F: __dxlog_file17,
8072
+ L: 405,
7425
8073
  S: void 0,
7426
8074
  C: (f, a) => f(...a)
7427
8075
  });
@@ -7430,13 +8078,17 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7430
8078
  to: state.doc.length
7431
8079
  }, registry, notifier);
7432
8080
  } else {
8081
+ const rebuildFrom = streamingFrom ?? from;
7433
8082
  const result = buildDecorations4(state, {
7434
- from,
8083
+ from: rebuildFrom,
7435
8084
  to: state.doc.length
7436
8085
  }, registry, notifier);
7437
8086
  return {
7438
8087
  from: result.from,
8088
+ streamingFrom: result.streamingFrom,
7439
8089
  decorations: decorations2.update({
8090
+ // Remove old streaming decorations — they are rebuilt each tick.
8091
+ filter: (_f, _t, deco) => !deco.spec.streaming,
7440
8092
  add: decorationSetToArray(result.decorations)
7441
8093
  })
7442
8094
  };
@@ -7444,12 +8096,13 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7444
8096
  }
7445
8097
  return {
7446
8098
  from,
8099
+ streamingFrom,
7447
8100
  decorations: decorations2
7448
8101
  };
7449
8102
  },
7450
8103
  provide: (field) => [
7451
- EditorView29.decorations.from(field, (v) => v.decorations),
7452
- EditorView29.atomicRanges.of((view) => view.state.field(field).decorations || Decoration15.none)
8104
+ EditorView31.decorations.from(field, (v) => v.decorations),
8105
+ EditorView31.atomicRanges.of((view) => view.state.field(field).decorations || Decoration16.none)
7453
8106
  ]
7454
8107
  });
7455
8108
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -7460,10 +8113,11 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7460
8113
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
7461
8114
  return {
7462
8115
  from: range.from,
7463
- decorations: Decoration15.none
8116
+ decorations: Decoration16.none
7464
8117
  };
7465
8118
  }
7466
8119
  let last = range.from;
8120
+ let streamingFrom;
7467
8121
  tree.iterate({
7468
8122
  from: range.from,
7469
8123
  to: range.to,
@@ -7476,21 +8130,26 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7476
8130
  if (args) {
7477
8131
  const def = registry[args._tag];
7478
8132
  if (def) {
8133
+ if (def.streaming && !node.node.getChild("CloseTag")) {
8134
+ return false;
8135
+ }
7479
8136
  const { block, factory, Component } = def;
7480
- const widgetState = args.id ? widgetStateMap[args.id] : void 0;
7481
8137
  const nodeRange = {
7482
8138
  from: node.node.from,
7483
8139
  to: node.node.to
7484
8140
  };
8141
+ const widgetId = xmlWidgetId(args.id, def.streaming ? `cm-xml-${nodeRange.from}` : `cm-xml-${nodeRange.from}-${nodeRange.to}`);
8142
+ const widgetState = widgetStateMap[widgetId];
7485
8143
  const props = {
7486
- context,
8144
+ id: widgetId,
7487
8145
  range: nodeRange,
8146
+ context,
7488
8147
  ...args,
7489
8148
  ...widgetState
7490
8149
  };
7491
- const widget = factory ? factory(props) : Component ? args.id && new PlaceholderWidget2(args.id, Component, props, notifier) : void 0;
8150
+ const widget = factory ? factory(props) ?? void 0 : Component ? new PlaceholderWidget2(widgetId, Component, props, notifier) : void 0;
7492
8151
  if (widget) {
7493
- builder.add(nodeRange.from, nodeRange.to, Decoration15.replace({
8152
+ builder.add(nodeRange.from, nodeRange.to, Decoration16.replace({
7494
8153
  widget,
7495
8154
  block,
7496
8155
  atomic: true,
@@ -7502,9 +8161,9 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7502
8161
  }
7503
8162
  }
7504
8163
  } catch (err) {
7505
- log10.catch(err, void 0, {
7506
- F: __dxlog_file16,
7507
- L: 456,
8164
+ log11.catch(err, void 0, {
8165
+ F: __dxlog_file17,
8166
+ L: 514,
7508
8167
  S: void 0,
7509
8168
  C: (f, a) => f(...a)
7510
8169
  });
@@ -7514,8 +8173,65 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7514
8173
  }
7515
8174
  }
7516
8175
  });
8176
+ const streamingTagNames = Object.entries(registry).filter(([, def]) => def.streaming).map(([name]) => name).sort((a, b) => b.length - a.length);
8177
+ if (streamingTagNames.length > 0) {
8178
+ const tailText = state.sliceDoc(range.from, range.to);
8179
+ const streamingPattern = streamingTagNames.map(escapeRegExpSource3).join("|");
8180
+ const tagPattern = new RegExp(`<(${streamingPattern})(\\s[^>]*)?>`, "g");
8181
+ let match;
8182
+ while ((match = tagPattern.exec(tailText)) !== null) {
8183
+ const tagName = match[1];
8184
+ const closeTag = `</${tagName}>`;
8185
+ const afterOpen = match.index + match[0].length;
8186
+ if (tailText.indexOf(closeTag, afterOpen) === -1) {
8187
+ const absoluteFrom = range.from + match.index;
8188
+ const contentFrom = range.from + afterOpen;
8189
+ const innerText = state.sliceDoc(contentFrom, range.to).trim();
8190
+ const def = registry[tagName];
8191
+ const props = {
8192
+ _tag: tagName,
8193
+ context,
8194
+ range: {
8195
+ from: absoluteFrom,
8196
+ to: range.to
8197
+ },
8198
+ children: innerText ? [
8199
+ innerText
8200
+ ] : void 0
8201
+ };
8202
+ const attrPattern = /(\w+)="([^"]*)"/g;
8203
+ let attrMatch;
8204
+ while ((attrMatch = attrPattern.exec(match[0])) !== null) {
8205
+ props[attrMatch[1]] = attrMatch[2];
8206
+ }
8207
+ const widgetId = xmlWidgetId(props.id, `cm-xml-${absoluteFrom}`);
8208
+ const widgetState = widgetStateMap[widgetId];
8209
+ const mergedProps = {
8210
+ ...props,
8211
+ id: widgetId,
8212
+ ...widgetState
8213
+ };
8214
+ const widget = def.factory ? def.factory(mergedProps) ?? void 0 : def.Component ? new PlaceholderWidget2(widgetId, def.Component, mergedProps, notifier, true) : void 0;
8215
+ if (widget) {
8216
+ builder.add(absoluteFrom, range.to, Decoration16.replace({
8217
+ widget,
8218
+ block: def.block,
8219
+ atomic: true,
8220
+ inclusive: true,
8221
+ tag: tagName,
8222
+ streaming: true,
8223
+ contentFrom
8224
+ }));
8225
+ streamingFrom = absoluteFrom;
8226
+ last = absoluteFrom;
8227
+ }
8228
+ break;
8229
+ }
8230
+ }
8231
+ }
7517
8232
  return {
7518
8233
  from: last,
8234
+ streamingFrom,
7519
8235
  decorations: builder.finish()
7520
8236
  };
7521
8237
  };
@@ -7524,12 +8240,14 @@ var PlaceholderWidget2 = class extends WidgetType10 {
7524
8240
  Component;
7525
8241
  props;
7526
8242
  notifier;
7527
- _root = null;
7528
- constructor(id, Component, props, notifier) {
7529
- super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier;
8243
+ streaming;
8244
+ #root = null;
8245
+ #view;
8246
+ constructor(id, Component, props, notifier, streaming) {
8247
+ super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier, this.streaming = streaming;
7530
8248
  invariant7(id, void 0, {
7531
- F: __dxlog_file16,
7532
- L: 482,
8249
+ F: __dxlog_file17,
8250
+ L: 617,
7533
8251
  S: this,
7534
8252
  A: [
7535
8253
  "id",
@@ -7538,27 +8256,48 @@ var PlaceholderWidget2 = class extends WidgetType10 {
7538
8256
  });
7539
8257
  }
7540
8258
  get root() {
7541
- return this._root;
8259
+ return this.#root;
7542
8260
  }
7543
8261
  eq(other) {
8262
+ if (this.streaming) {
8263
+ return false;
8264
+ }
7544
8265
  return this.id === other.id;
7545
8266
  }
7546
8267
  ignoreEvent() {
7547
8268
  return true;
7548
8269
  }
7549
- toDOM(_view) {
7550
- this._root = document.createElement("span");
8270
+ toDOM(view) {
8271
+ this.#view = view;
8272
+ this.#root = Domino4.of("div").classNames("min-h-[24px]").root;
8273
+ const props = Object.assign({}, this.props, {
8274
+ view
8275
+ });
7551
8276
  this.notifier.mounted({
7552
8277
  id: this.id,
7553
- root: this._root,
7554
- props: this.props,
8278
+ root: this.#root,
8279
+ props,
7555
8280
  Component: this.Component
7556
8281
  });
7557
- return this._root;
8282
+ return this.#root;
8283
+ }
8284
+ updateDOM(dom) {
8285
+ this.#root = dom;
8286
+ const props = Object.assign({}, this.props, {
8287
+ view: this.#view
8288
+ });
8289
+ this.notifier.mounted({
8290
+ id: this.id,
8291
+ root: this.#root,
8292
+ props,
8293
+ Component: this.Component
8294
+ });
8295
+ return true;
7558
8296
  }
7559
8297
  destroy(_dom) {
7560
8298
  this.notifier.unmounted(this.id);
7561
- this._root = null;
8299
+ this.#root = null;
8300
+ this.#view = void 0;
7562
8301
  }
7563
8302
  };
7564
8303
 
@@ -7622,8 +8361,8 @@ export {
7622
8361
  Cursor,
7623
8362
  EditorInputMode,
7624
8363
  EditorInputModes,
7625
- EditorState3 as EditorState,
7626
- EditorView30 as EditorView,
8364
+ EditorState4 as EditorState,
8365
+ EditorView32 as EditorView,
7627
8366
  EditorViewMode,
7628
8367
  EditorViewModes,
7629
8368
  Inline,
@@ -7642,6 +8381,7 @@ export {
7642
8381
  addStyle,
7643
8382
  annotations,
7644
8383
  autoScroll,
8384
+ autoScrollEffect,
7645
8385
  autocomplete,
7646
8386
  automerge,
7647
8387
  awareness,
@@ -7655,9 +8395,11 @@ export {
7655
8395
  commentClickedEffect,
7656
8396
  comments,
7657
8397
  commentsState,
8398
+ compactSlots,
7658
8399
  convertTreeToJson,
7659
8400
  createBasicExtensions,
7660
8401
  createComment,
8402
+ createCrawler,
7661
8403
  createDataExtensions,
7662
8404
  createEditorStateStore,
7663
8405
  createEditorStateTransaction,
@@ -7677,12 +8419,12 @@ export {
7677
8419
  defaultThemeSlots,
7678
8420
  deleteItem,
7679
8421
  documentId,
8422
+ documentSlots,
7680
8423
  dropFile,
8424
+ editorClassNames,
7681
8425
  editorInputMode,
7682
- editorSlots,
7683
- editorWidth,
7684
- editorWithToolbarLayout,
7685
8426
  extendedMarkdown,
8427
+ fader,
7686
8428
  filterChars,
7687
8429
  flattenRect,
7688
8430
  focus,
@@ -7737,10 +8479,12 @@ export {
7737
8479
  removeList,
7738
8480
  removeStyle,
7739
8481
  replacer,
8482
+ scrollPastEnd,
7740
8483
  scrollThreadIntoView,
7741
- scrollToBottomEffect,
7742
8484
  scrollToLine,
7743
- scrollToLineEffect,
8485
+ scroller,
8486
+ scrollerCrawlEffect,
8487
+ scrollerLineEffect,
7744
8488
  selectionState,
7745
8489
  setBlockquote,
7746
8490
  setComments,
@@ -7748,10 +8492,7 @@ export {
7748
8492
  setSelection,
7749
8493
  setStyle,
7750
8494
  singleValueFacet,
7751
- smoothScroll,
7752
- stackItemContentEditorClassNames,
7753
8495
  staticCompletion,
7754
- streamer,
7755
8496
  submit,
7756
8497
  tabbable,
7757
8498
  table,
@@ -7770,7 +8511,10 @@ export {
7770
8511
  treeFacet,
7771
8512
  typeahead,
7772
8513
  typewriter,
8514
+ wire,
8515
+ wireBypass,
7773
8516
  wrapWithCatch,
8517
+ xmlElementLength,
7774
8518
  xmlTagContextEffect,
7775
8519
  xmlTagResetEffect,
7776
8520
  xmlTagUpdateEffect,