@dxos/ui-editor 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe

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