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

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