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

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 (125) hide show
  1. package/dist/lib/browser/index.mjs +590 -508
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/types/index.mjs +26 -6
  5. package/dist/lib/browser/types/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/index.mjs +590 -507
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/lib/node-esm/types/index.mjs +27 -6
  10. package/dist/lib/node-esm/types/index.mjs.map +4 -4
  11. package/dist/types/src/defaults.d.ts +1 -0
  12. package/dist/types/src/defaults.d.ts.map +1 -1
  13. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  14. package/dist/types/src/extensions/auto-scroll.d.ts +12 -2
  15. package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -1
  16. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -1
  17. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -1
  18. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -1
  19. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  21. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  23. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  25. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  26. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  27. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  28. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  29. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  30. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  31. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  32. package/dist/types/src/extensions/factories.d.ts +2 -1
  33. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  34. package/dist/types/src/extensions/factories.test.d.ts +2 -0
  35. package/dist/types/src/extensions/factories.test.d.ts.map +1 -0
  36. package/dist/types/src/extensions/focus.d.ts +1 -1
  37. package/dist/types/src/extensions/index.d.ts +1 -1
  38. package/dist/types/src/extensions/index.d.ts.map +1 -1
  39. package/dist/types/src/extensions/json.d.ts.map +1 -1
  40. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  41. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  42. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  43. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  45. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  47. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  49. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  50. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  51. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -1
  52. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  53. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  54. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  55. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  56. package/dist/types/src/extensions/replacer.d.ts.map +1 -1
  57. package/dist/types/src/extensions/scroller.d.ts +13 -8
  58. package/dist/types/src/extensions/scroller.d.ts.map +1 -1
  59. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  60. package/dist/types/src/extensions/snippets.d.ts +10 -0
  61. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  62. package/dist/types/src/extensions/submit.d.ts.map +1 -1
  63. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
  64. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -1
  65. package/dist/types/src/extensions/tags/index.d.ts +3 -1
  66. package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
  67. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  68. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  69. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  70. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  71. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  72. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  73. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  74. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  75. package/dist/types/src/extensions/tags/xml-tags.d.ts +1 -8
  76. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  77. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -1
  78. package/dist/types/src/index.d.ts +0 -1
  79. package/dist/types/src/index.d.ts.map +1 -1
  80. package/dist/types/src/styles/theme.d.ts.map +1 -1
  81. package/dist/types/src/types/types.d.ts +4 -4
  82. package/dist/types/src/types/types.d.ts.map +1 -1
  83. package/dist/types/src/util/cursor.d.ts.map +1 -1
  84. package/dist/types/src/util/debug.d.ts.map +1 -1
  85. package/dist/types/src/util/decorations.d.ts.map +1 -1
  86. package/dist/types/src/util/dom.d.ts.map +1 -1
  87. package/dist/types/src/util/facet.d.ts.map +1 -1
  88. package/dist/types/src/util/util.d.ts.map +1 -1
  89. package/dist/types/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +27 -30
  91. package/src/defaults.ts +12 -3
  92. package/src/extensions/auto-scroll.ts +55 -2
  93. package/src/extensions/automerge/automerge.test.tsx +28 -8
  94. package/src/extensions/automerge/automerge.ts +2 -4
  95. package/src/extensions/factories.test.ts +88 -0
  96. package/src/extensions/factories.ts +24 -5
  97. package/src/extensions/index.ts +1 -1
  98. package/src/extensions/scroller.ts +43 -32
  99. package/src/extensions/snippets.ts +67 -0
  100. package/src/extensions/tags/index.ts +3 -1
  101. package/src/extensions/tags/testing/text.md +36 -0
  102. package/src/extensions/tags/testing/text.txt +35 -0
  103. package/src/extensions/tags/{wire.test.ts → typewriter.test.ts} +2 -2
  104. package/src/extensions/tags/typewriter.ts +594 -0
  105. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  106. package/src/extensions/tags/xml-formatting.ts +125 -0
  107. package/src/extensions/tags/xml-tags.ts +2 -28
  108. package/src/extensions/tags/xml-util.test.ts +90 -3
  109. package/src/extensions/tags/xml-util.ts +62 -5
  110. package/src/index.ts +0 -1
  111. package/src/styles/theme.ts +13 -4
  112. package/src/types/types.ts +10 -2
  113. package/src/typings.d.ts +8 -0
  114. package/dist/lib/browser/chunk-HL3YF6WC.mjs +0 -22
  115. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +0 -7
  116. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs +0 -24
  117. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +0 -7
  118. package/dist/types/src/extensions/tags/wire.d.ts +0 -23
  119. package/dist/types/src/extensions/tags/wire.d.ts.map +0 -1
  120. package/dist/types/src/extensions/tags/wire.test.d.ts +0 -2
  121. package/dist/types/src/extensions/tags/wire.test.d.ts.map +0 -1
  122. package/dist/types/src/extensions/typewriter.d.ts +0 -10
  123. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  124. package/src/extensions/tags/wire.ts +0 -459
  125. package/src/extensions/typewriter.ts +0 -68
@@ -1,13 +1,6 @@
1
- import {
2
- EditorInputMode,
3
- EditorInputModes,
4
- EditorViewMode,
5
- EditorViewModes
6
- } from "./chunk-HL3YF6WC.mjs";
7
-
8
1
  // src/index.ts
9
2
  import { EditorState as EditorState4 } from "@codemirror/state";
10
- import { EditorView as EditorView32, keymap as keymap15 } from "@codemirror/view";
3
+ import { EditorView as EditorView33, keymap as keymap15 } from "@codemirror/view";
11
4
  import { tags as tags2 } from "@lezer/highlight";
12
5
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
13
6
 
@@ -23,8 +16,11 @@ var documentSlots = {
23
16
  * NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).
24
17
  */
25
18
  className: mx(
26
- // NOTE: Container for widget sizing (must have `max-w-[100cqi]`).
27
- "dx-size-container",
19
+ // Inline-size container for widget sizing (children use `max-w-[100cqi]`).
20
+ // NOTE: Use inline-size, not full size containment — `container-type: size` on the
21
+ // editor content breaks CodeMirror's viewport measurement, leaving blank gaps during
22
+ // scroll until a click forces a re-measure.
23
+ "dx-inline-size-container",
28
24
  // Wider margin for web (vs. mobile).
29
25
  "pointer-fine:max-w-[min(50rem,100%-4rem)] pointer-coarse:max-w-[min(50rem,100%-2rem)]",
30
26
  "mx-auto! w-full"
@@ -33,7 +29,12 @@ var documentSlots = {
33
29
  };
34
30
  var compactSlots = {
35
31
  content: {
36
- className: "mx-2! w-full"
32
+ className: "mx-2!"
33
+ }
34
+ };
35
+ var mobileSlots = {
36
+ content: {
37
+ className: "mx-2!"
37
38
  }
38
39
  };
39
40
 
@@ -270,12 +271,7 @@ var wrapWithCatch = (fn, label) => {
270
271
  } catch (err) {
271
272
  log.catch(err, {
272
273
  label
273
- }, {
274
- F: __dxlog_file,
275
- L: 20,
276
- S: void 0,
277
- C: (f, a) => f(...a)
278
- });
274
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 13, S: void 0 });
279
275
  }
280
276
  };
281
277
  };
@@ -301,12 +297,7 @@ var logChanges = (trs) => {
301
297
  if (changes.length) {
302
298
  log("changes", {
303
299
  changes
304
- }, {
305
- F: __dxlog_file,
306
- L: 54,
307
- S: void 0,
308
- C: (f, a) => f(...a)
309
- });
300
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 44, S: void 0 });
310
301
  }
311
302
  };
312
303
 
@@ -597,24 +588,19 @@ var scroller = ({ overScroll = 0 } = {}) => {
597
588
  }
598
589
  }
599
590
  } catch (err) {
600
- log2.catch(err, void 0, {
601
- F: __dxlog_file2,
602
- L: 146,
603
- S: void 0,
604
- C: (f, a) => f(...a)
605
- });
591
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 91, S: void 0 });
606
592
  }
607
593
  });
608
594
  }),
609
595
  // Styles.
610
596
  EditorView4.theme({
611
- ".cm-content": {
612
- paddingBottom: `${overScroll}px`
613
- },
614
597
  ".cm-scroller": {
615
598
  overflowY: "scroll",
616
- overflowAnchor: "none",
617
- paddingBottom: "0"
599
+ // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks
600
+ // expanding their TogglePanel), the browser picks a stable element near the viewport
601
+ // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning
602
+ // logic still has the final word when pinned (forces scrollTop to scrollHeight).
603
+ overflowAnchor: "auto"
618
604
  },
619
605
  ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
620
606
  display: "none"
@@ -626,6 +612,16 @@ var scroller = ({ overScroll = 0 } = {}) => {
626
612
  "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
627
613
  background: "var(--color-scrollbar-thumb)"
628
614
  },
615
+ // Spacer below the last text line. Implemented as a real block pseudo-element
616
+ // (rather than `padding-bottom` on `.cm-content`) so it materializes in the
617
+ // scroller's `scrollHeight` regardless of how `padding` is reset by the base
618
+ // theme or downstream classes — this is what gives auto-scroll its head-room
619
+ // so the last line stays `overScroll` px above the viewport bottom.
620
+ ".cm-content::after": {
621
+ content: '""',
622
+ display: "block",
623
+ height: `${overScroll}px`
624
+ },
629
625
  ".cm-scroll-button": {
630
626
  position: "absolute",
631
627
  bottom: "0.5rem",
@@ -634,31 +630,28 @@ var scroller = ({ overScroll = 0 } = {}) => {
634
630
  })
635
631
  ];
636
632
  };
637
- function createCrawler(view, accel = 0.15, maxVelocity = 1, snapThreshold = 0.5) {
633
+ function createCrawler(view, omega = 5, snapThreshold = 5, snapVelocity = 50) {
638
634
  const el = view.scrollDOM;
639
635
  let currentTop = 0;
640
636
  let velocity = 0;
641
637
  let rafId = null;
642
- function frame() {
638
+ let lastTime = 0;
639
+ function frame(now) {
640
+ const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1e3);
641
+ lastTime = now;
643
642
  const targetTop = el.scrollHeight - el.clientHeight;
644
643
  const delta = targetTop - currentTop;
645
- const absDelta = Math.abs(delta);
646
- if (absDelta < snapThreshold && Math.abs(velocity) < snapThreshold) {
644
+ if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {
647
645
  el.scrollTop = targetTop;
648
646
  currentTop = targetTop;
649
647
  velocity = 0;
650
648
  rafId = null;
649
+ lastTime = 0;
651
650
  return;
652
651
  }
653
- const stoppingDistance = velocity * velocity / (2 * accel);
654
- const direction = Math.sign(delta);
655
- if (velocity !== 0 && (absDelta <= stoppingDistance || direction !== Math.sign(velocity))) {
656
- velocity -= Math.sign(velocity) * Math.min(accel, Math.abs(velocity));
657
- } else {
658
- velocity += direction * accel;
659
- velocity = Math.sign(velocity) * Math.min(Math.abs(velocity), maxVelocity);
660
- }
661
- currentTop += velocity;
652
+ const accel = omega * omega * delta - 2 * omega * velocity;
653
+ velocity += accel * dt;
654
+ currentTop += velocity * dt;
662
655
  el.scrollTop = currentTop;
663
656
  rafId = requestAnimationFrame(frame);
664
657
  }
@@ -666,14 +659,16 @@ function createCrawler(view, accel = 0.15, maxVelocity = 1, snapThreshold = 0.5)
666
659
  scroll: () => {
667
660
  if (rafId === null) {
668
661
  currentTop = el.scrollTop;
662
+ lastTime = 0;
669
663
  rafId = requestAnimationFrame(frame);
670
664
  }
671
665
  },
672
666
  cancel: () => {
673
667
  if (rafId !== null) {
674
668
  cancelAnimationFrame(rafId);
675
- rafId = null;
676
669
  velocity = 0;
670
+ lastTime = 0;
671
+ rafId = null;
677
672
  }
678
673
  }
679
674
  };
@@ -681,7 +676,7 @@ function createCrawler(view, accel = 0.15, maxVelocity = 1, snapThreshold = 0.5)
681
676
 
682
677
  // src/extensions/auto-scroll.ts
683
678
  var autoScrollEffect = StateEffect2.define();
684
- var autoScroll = (_ = {}) => {
679
+ var autoScroll = ({ scrollOnResize = true } = {}) => {
685
680
  let buttonContainer;
686
681
  let isPinned = true;
687
682
  let jumpPending = false;
@@ -747,6 +742,43 @@ var autoScroll = (_ = {}) => {
747
742
  }
748
743
  }
749
744
  }),
745
+ // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,
746
+ // window resize). Doc-driven height changes are handled by the updateListener above; this
747
+ // observer covers the case where the viewport changes while the doc length is unchanged.
748
+ scrollOnResize ? ViewPlugin6.fromClass(class {
749
+ observer;
750
+ firstObservation = true;
751
+ destroyed = false;
752
+ constructor(view) {
753
+ const onResize = throttle(() => {
754
+ if (this.destroyed || !enabled) {
755
+ return;
756
+ }
757
+ setPinned(true);
758
+ requestAnimationFrame(() => {
759
+ if (this.destroyed) {
760
+ return;
761
+ }
762
+ view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
763
+ view.dispatch({
764
+ effects: scrollerCrawlEffect.of(true)
765
+ });
766
+ });
767
+ }, 100);
768
+ this.observer = new ResizeObserver(() => {
769
+ if (this.firstObservation) {
770
+ this.firstObservation = false;
771
+ return;
772
+ }
773
+ onResize();
774
+ });
775
+ this.observer.observe(view.scrollDOM);
776
+ }
777
+ destroy() {
778
+ this.destroyed = true;
779
+ this.observer.disconnect();
780
+ }
781
+ }) : [],
750
782
  // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
751
783
  ViewPlugin6.fromClass(class {
752
784
  cleanup;
@@ -822,12 +854,7 @@ var cursorConverter = (accessor) => ({
822
854
  try {
823
855
  return toCursor(accessor, pos, assoc);
824
856
  } catch (err) {
825
- log3.catch(err, void 0, {
826
- F: __dxlog_file3,
827
- L: 15,
828
- S: void 0,
829
- C: (f, a) => f(...a)
830
- });
857
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 11, S: void 0 });
831
858
  return "";
832
859
  }
833
860
  },
@@ -835,12 +862,7 @@ var cursorConverter = (accessor) => ({
835
862
  try {
836
863
  return fromCursor(accessor, cursor);
837
864
  } catch (err) {
838
- log3.catch(err, void 0, {
839
- F: __dxlog_file3,
840
- L: 24,
841
- S: void 0,
842
- C: (f, a) => f(...a)
843
- });
865
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 19, S: void 0 });
844
866
  return 0;
845
867
  }
846
868
  }
@@ -1025,12 +1047,7 @@ var Syncer = class {
1025
1047
  this._pending = false;
1026
1048
  }
1027
1049
  onEditorChange(view) {
1028
- log4("onEditorChange", void 0, {
1029
- F: __dxlog_file4,
1030
- L: 45,
1031
- S: this,
1032
- C: (f, a) => f(...a)
1033
- });
1050
+ log4("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 35, S: this });
1034
1051
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
1035
1052
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
1036
1053
  if (newHeads) {
@@ -1041,12 +1058,7 @@ var Syncer = class {
1041
1058
  }
1042
1059
  }
1043
1060
  onAutomergeChange(view) {
1044
- log4("onAutomergeChange", void 0, {
1045
- F: __dxlog_file4,
1046
- L: 60,
1047
- S: this,
1048
- C: (f, a) => f(...a)
1049
- });
1061
+ log4("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 47, S: this });
1050
1062
  const oldHeads = getLastHeads(view.state, this._state);
1051
1063
  const newHeads = A2.getHeads(this._handle.doc());
1052
1064
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1108,14 +1120,16 @@ var automerge = (accessor) => {
1108
1120
  const value = DocAccessor.getValue(accessor);
1109
1121
  const current = this._view.state.doc.toString();
1110
1122
  if (value !== current) {
1111
- console.warn("ENABLING INITIAL SYNC -- THIS MAY BE A REGRESSION");
1112
1123
  this._view.dispatch({
1113
1124
  changes: {
1114
1125
  from: 0,
1115
1126
  to: this._view.state.doc.length,
1116
1127
  insert: value
1117
1128
  },
1118
- annotations: initialSync
1129
+ annotations: [
1130
+ initialSync,
1131
+ reconcileAnnotation.of(true)
1132
+ ]
1119
1133
  });
1120
1134
  }
1121
1135
  });
@@ -1167,10 +1181,7 @@ var awareness = (provider = dummyProvider) => {
1167
1181
  ];
1168
1182
  };
1169
1183
  var RemoteSelectionsDecorator = class {
1170
- _ctx = new Context(void 0, {
1171
- F: __dxlog_file5,
1172
- L: 80
1173
- });
1184
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 33 });
1174
1185
  _cursorConverter;
1175
1186
  _provider;
1176
1187
  _lastAnchor;
@@ -1401,10 +1412,7 @@ var SpaceAwarenessProvider = class {
1401
1412
  this._info = info;
1402
1413
  }
1403
1414
  open() {
1404
- this._ctx = new Context2(void 0, {
1405
- F: __dxlog_file6,
1406
- L: 57
1407
- });
1415
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 28 });
1408
1416
  this._postTask = new DeferredTask(this._ctx, async () => {
1409
1417
  if (this._localState) {
1410
1418
  await this._messenger.postMessage(this._channel, {
@@ -1431,12 +1439,7 @@ var SpaceAwarenessProvider = class {
1431
1439
  }).catch((err) => {
1432
1440
  log5.debug("failed to query awareness", {
1433
1441
  err
1434
- }, {
1435
- F: __dxlog_file6,
1436
- L: 91,
1437
- S: this,
1438
- C: (f, a) => f(...a)
1439
- });
1442
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 57, S: this });
1440
1443
  });
1441
1444
  }
1442
1445
  close() {
@@ -1448,15 +1451,7 @@ var SpaceAwarenessProvider = class {
1448
1451
  return Array.from(this._remoteStates.values());
1449
1452
  }
1450
1453
  update(position) {
1451
- invariant(this._postTask, void 0, {
1452
- F: __dxlog_file6,
1453
- L: 106,
1454
- S: this,
1455
- A: [
1456
- "this._postTask",
1457
- ""
1458
- ]
1459
- });
1454
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 71, S: this, A: ["this._postTask", ""] });
1460
1455
  this._localState = {
1461
1456
  peerId: this._peerId,
1462
1457
  position,
@@ -1465,27 +1460,11 @@ var SpaceAwarenessProvider = class {
1465
1460
  this._postTask.schedule();
1466
1461
  }
1467
1462
  _handleQueryMessage() {
1468
- invariant(this._postTask, void 0, {
1469
- F: __dxlog_file6,
1470
- L: 117,
1471
- S: this,
1472
- A: [
1473
- "this._postTask",
1474
- ""
1475
- ]
1476
- });
1463
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 80, S: this, A: ["this._postTask", ""] });
1477
1464
  this._postTask.schedule();
1478
1465
  }
1479
1466
  _handlePostMessage(message) {
1480
- invariant(message.kind === "post", void 0, {
1481
- F: __dxlog_file6,
1482
- L: 122,
1483
- S: this,
1484
- A: [
1485
- "message.kind === 'post'",
1486
- ""
1487
- ]
1488
- });
1467
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1489
1468
  this._remoteStates.set(message.state.peerId, message.state);
1490
1469
  this.remoteStateChange.emit();
1491
1470
  }
@@ -1614,15 +1593,7 @@ var Blaster = class {
1614
1593
  return this._node;
1615
1594
  }
1616
1595
  initialize() {
1617
- invariant2(!this._canvas && !this._ctx, void 0, {
1618
- F: __dxlog_file7,
1619
- L: 142,
1620
- S: this,
1621
- A: [
1622
- "!this._canvas && !this._ctx",
1623
- ""
1624
- ]
1625
- });
1596
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1626
1597
  this._canvas = document.createElement("canvas");
1627
1598
  this._canvas.id = "code-blast-canvas";
1628
1599
  this._canvas.style.position = "absolute";
@@ -1651,15 +1622,7 @@ var Blaster = class {
1651
1622
  }
1652
1623
  }
1653
1624
  start() {
1654
- invariant2(this._canvas && this._ctx, void 0, {
1655
- F: __dxlog_file7,
1656
- L: 181,
1657
- S: this,
1658
- A: [
1659
- "this._canvas && this._ctx",
1660
- ""
1661
- ]
1662
- });
1625
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1663
1626
  this._running = true;
1664
1627
  this.loop();
1665
1628
  }
@@ -1909,12 +1872,7 @@ var bookmarks = () => {
1909
1872
  key: "Mod-ArrowUp",
1910
1873
  run: (view) => {
1911
1874
  const bookmarks2 = view.state.field(bookmarksField);
1912
- log6("up", bookmarks2, {
1913
- F: __dxlog_file8,
1914
- L: 29,
1915
- S: void 0,
1916
- C: (f, a) => f(...a)
1917
- });
1875
+ log6("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 18, S: void 0 });
1918
1876
  return true;
1919
1877
  }
1920
1878
  },
@@ -1922,12 +1880,7 @@ var bookmarks = () => {
1922
1880
  key: "Mod-ArrowDown",
1923
1881
  run: (view) => {
1924
1882
  const bookmarks2 = view.state.field(bookmarksField);
1925
- log6("down", bookmarks2, {
1926
- F: __dxlog_file8,
1927
- L: 37,
1928
- S: void 0,
1929
- C: (f, a) => f(...a)
1930
- });
1883
+ log6("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0 });
1931
1884
  return true;
1932
1885
  }
1933
1886
  }
@@ -1992,28 +1945,12 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1992
1945
  };
1993
1946
  var createEditorStateStore = (keyPrefix) => ({
1994
1947
  getState: (id) => {
1995
- invariant3(id, void 0, {
1996
- F: __dxlog_file9,
1997
- L: 47,
1998
- S: void 0,
1999
- A: [
2000
- "id",
2001
- ""
2002
- ]
2003
- });
1948
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 26, S: void 0, A: ["id", ""] });
2004
1949
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
2005
1950
  return state ? JSON.parse(state) : void 0;
2006
1951
  },
2007
1952
  setState: (id, state) => {
2008
- invariant3(id, void 0, {
2009
- F: __dxlog_file9,
2010
- L: 53,
2011
- S: void 0,
2012
- A: [
2013
- "id",
2014
- ""
2015
- ]
2016
- });
1953
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 31, S: void 0, A: ["id", ""] });
2017
1954
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
2018
1955
  }
2019
1956
  });
@@ -2135,12 +2072,7 @@ var commentsDecorations = EditorView11.decorations.compute([
2135
2072
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2136
2073
  const range = comment.range;
2137
2074
  if (!range) {
2138
- log7.warn("Invalid range:", range, {
2139
- F: __dxlog_file10,
2140
- L: 139,
2141
- S: void 0,
2142
- C: (f, a) => f(...a)
2143
- });
2075
+ log7.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 93, S: void 0 });
2144
2076
  return void 0;
2145
2077
  } else if (range.from === range.to) {
2146
2078
  return void 0;
@@ -2600,12 +2532,21 @@ var baseTheme = EditorView13.baseTheme({
2600
2532
  * Scroller
2601
2533
  */
2602
2534
  ".cm-scroller": {
2603
- overflowAnchor: "none"
2535
+ // Browser scroll-anchoring: see comment in `scroller.ts`. `auto` lets the browser pin a
2536
+ // stable element near the viewport top so widget resizes (e.g. tool-block TogglePanel
2537
+ // open/close) don't jump the user's view.
2538
+ overflowAnchor: "auto"
2604
2539
  },
2605
2540
  ".cm-scroller::-webkit-scrollbar": {
2606
- width: "8px"
2541
+ width: "var(--scrollbar-size,8px)",
2542
+ height: "var(--scrollbar-size,8px)"
2543
+ },
2544
+ ".cm-scroller::-webkit-scrollbar-corner": {
2545
+ background: "transparent"
2546
+ },
2547
+ ".cm-scroller::-webkit-scrollbar-track": {
2548
+ background: "transparent"
2607
2549
  },
2608
- ".cm-scroller::-webkit-scrollbar-track": {},
2609
2550
  ".cm-scroller::-webkit-scrollbar-thumb": {
2610
2551
  background: "transparent",
2611
2552
  transition: "background 0.15s"
@@ -2642,7 +2583,7 @@ var baseTheme = EditorView13.baseTheme({
2642
2583
  * Height is set to match the corresponding line (which may have wrapped).
2643
2584
  */
2644
2585
  ".cm-gutterElement": {
2645
- lineHeight: 1.5,
2586
+ lineHeight: "24px",
2646
2587
  fontSize: "12px"
2647
2588
  },
2648
2589
  /**
@@ -2957,12 +2898,7 @@ var createBasicExtensions = (propsProp) => {
2957
2898
  return [
2958
2899
  // NOTE: Doesn't catch errors in keymap functions.
2959
2900
  EditorView16.exceptionSink.of((err) => {
2960
- log8.catch(err, void 0, {
2961
- F: __dxlog_file11,
2962
- L: 130,
2963
- S: void 0,
2964
- C: (f, a) => f(...a)
2965
- });
2901
+ log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
2966
2902
  }),
2967
2903
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2968
2904
  props.bracketMatching && bracketMatching(),
@@ -2982,6 +2918,13 @@ var createBasicExtensions = (propsProp) => {
2982
2918
  props.lineWrapping && EditorView16.lineWrapping,
2983
2919
  props.placeholder && placeholder2(props.placeholder),
2984
2920
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2921
+ // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
2922
+ // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that
2923
+ // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits
2924
+ // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)
2925
+ // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers
2926
+ // depend on being able to populate the doc themselves.
2927
+ props.readOnly && EditorState.transactionFilter.of((tr) => tr.docChanged && (tr.isUserEvent("input") || tr.isUserEvent("delete") || tr.isUserEvent("undo") || tr.isUserEvent("redo")) ? [] : tr),
2985
2928
  props.scrollPastEnd && scrollPastEnd(),
2986
2929
  props.tabbable && tabbable,
2987
2930
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -3024,7 +2967,7 @@ var defaultStyles = {
3024
2967
  dark: vscodeDarkStyle,
3025
2968
  light: vscodeLightStyle
3026
2969
  };
3027
- var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp } = {}) => {
2970
+ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp, themeMode } = {}) => {
3028
2971
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
3029
2972
  return [
3030
2973
  baseTheme,
@@ -3039,9 +2982,14 @@ var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHig
3039
2982
  slots.content?.className && EditorView16.contentAttributes.of({
3040
2983
  class: slots.content.className
3041
2984
  }),
3042
- slots.scroll?.className && ViewPlugin12.fromClass(class {
2985
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
3043
2986
  constructor(view) {
3044
- view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
2987
+ if (slots.scroller?.className) {
2988
+ view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
2989
+ }
2990
+ if (scrollbarThin) {
2991
+ view.scrollDOM.style.setProperty("--scrollbar-size", "4px");
2992
+ }
3045
2993
  }
3046
2994
  })
3047
2995
  ].filter(isTruthy2);
@@ -5298,15 +5246,7 @@ var buildDecorations2 = (view, options, focus2) => {
5298
5246
  const { state } = view;
5299
5247
  const headerLevels = [];
5300
5248
  const getHeaderLevels = (node, level) => {
5301
- invariant4(level > 0, void 0, {
5302
- F: __dxlog_file12,
5303
- L: 177,
5304
- S: void 0,
5305
- A: [
5306
- "level > 0",
5307
- ""
5308
- ]
5309
- });
5249
+ invariant4(level > 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 160, S: void 0, A: ["level > 0", ""] });
5310
5250
  if (level > headerLevels.length) {
5311
5251
  const len = headerLevels.length;
5312
5252
  headerLevels.length = level;
@@ -5337,15 +5277,7 @@ var buildDecorations2 = (view, options, focus2) => {
5337
5277
  listLevels.pop();
5338
5278
  };
5339
5279
  const getCurrentListLevel = () => {
5340
- invariant4(listLevels.length, void 0, {
5341
- F: __dxlog_file12,
5342
- L: 199,
5343
- S: void 0,
5344
- A: [
5345
- "listLevels.length",
5346
- ""
5347
- ]
5348
- });
5280
+ invariant4(listLevels.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 192, S: void 0, A: ["listLevels.length", ""] });
5349
5281
  return listLevels[listLevels.length - 1];
5350
5282
  };
5351
5283
  const enterNode = (node) => {
@@ -5767,12 +5699,7 @@ var mention = ({ debug, onSearch }) => {
5767
5699
  (context) => {
5768
5700
  log9.info("completion context", {
5769
5701
  context
5770
- }, {
5771
- F: __dxlog_file13,
5772
- L: 27,
5773
- S: void 0,
5774
- C: (f, a) => f(...a)
5775
- });
5702
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 18, S: void 0 });
5776
5703
  const match = context.matchBefore(/@(\w+)?/);
5777
5704
  if (!match || match.from === match.to && !context.explicit) {
5778
5705
  return null;
@@ -6005,15 +5932,7 @@ var outlinerTree = (_options = {}) => {
6005
5932
  break;
6006
5933
  }
6007
5934
  case "BulletList": {
6008
- invariant5(current, void 0, {
6009
- F: __dxlog_file14,
6010
- L: 219,
6011
- S: void 0,
6012
- A: [
6013
- "current",
6014
- ""
6015
- ]
6016
- });
5935
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 169, S: void 0, A: ["current", ""] });
6017
5936
  parent = current;
6018
5937
  if (current) {
6019
5938
  current.lineRange.to = current.node.from;
@@ -6022,15 +5941,7 @@ var outlinerTree = (_options = {}) => {
6022
5941
  break;
6023
5942
  }
6024
5943
  case "ListItem": {
6025
- invariant5(parent, void 0, {
6026
- F: __dxlog_file14,
6027
- L: 228,
6028
- S: void 0,
6029
- A: [
6030
- "parent",
6031
- ""
6032
- ]
6033
- });
5944
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 179, S: void 0, A: ["parent", ""] });
6034
5945
  const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
6035
5946
  const docRange = {
6036
5947
  from: state.doc.lineAt(node.from).from,
@@ -6064,42 +5975,18 @@ var outlinerTree = (_options = {}) => {
6064
5975
  break;
6065
5976
  }
6066
5977
  case "ListMark": {
6067
- invariant5(current, void 0, {
6068
- F: __dxlog_file14,
6069
- L: 272,
6070
- S: void 0,
6071
- A: [
6072
- "current",
6073
- ""
6074
- ]
6075
- });
5978
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 219, S: void 0, A: ["current", ""] });
6076
5979
  current.type = "bullet";
6077
5980
  current.contentRange.from = node.from + "- ".length;
6078
5981
  break;
6079
5982
  }
6080
5983
  case "Task": {
6081
- invariant5(current, void 0, {
6082
- F: __dxlog_file14,
6083
- L: 278,
6084
- S: void 0,
6085
- A: [
6086
- "current",
6087
- ""
6088
- ]
6089
- });
5984
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 226, S: void 0, A: ["current", ""] });
6090
5985
  current.type = "task";
6091
5986
  break;
6092
5987
  }
6093
5988
  case "TaskMarker": {
6094
- invariant5(current, void 0, {
6095
- F: __dxlog_file14,
6096
- L: 283,
6097
- S: void 0,
6098
- A: [
6099
- "current",
6100
- ""
6101
- ]
6102
- });
5989
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 232, S: void 0, A: ["current", ""] });
6103
5990
  current.contentRange.from = node.from + "[ ] ".length;
6104
5991
  break;
6105
5992
  }
@@ -6107,29 +5994,13 @@ var outlinerTree = (_options = {}) => {
6107
5994
  },
6108
5995
  leave: (node) => {
6109
5996
  if (node.name === "BulletList") {
6110
- invariant5(parent, void 0, {
6111
- F: __dxlog_file14,
6112
- L: 291,
6113
- S: void 0,
6114
- A: [
6115
- "parent",
6116
- ""
6117
- ]
6118
- });
5997
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 240, S: void 0, A: ["parent", ""] });
6119
5998
  prevSiblings[level--] = void 0;
6120
5999
  parent = parent.parent;
6121
6000
  }
6122
6001
  }
6123
6002
  });
6124
- invariant5(tree, void 0, {
6125
- F: __dxlog_file14,
6126
- L: 298,
6127
- S: void 0,
6128
- A: [
6129
- "tree",
6130
- ""
6131
- ]
6132
- });
6003
+ invariant5(tree, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 246, S: void 0, A: ["tree", ""] });
6133
6004
  return tree;
6134
6005
  };
6135
6006
  return [
@@ -6571,35 +6442,20 @@ var editor = () => [
6571
6442
  text: insert.toString(),
6572
6443
  length: insert.length
6573
6444
  }
6574
- }, {
6575
- F: __dxlog_file15,
6576
- L: 164,
6577
- S: void 0,
6578
- C: (f, a) => f(...a)
6579
- });
6445
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 174, S: void 0 });
6580
6446
  }
6581
6447
  });
6582
6448
  if (changes.length > 0) {
6583
6449
  log10("modified,", {
6584
6450
  changes
6585
- }, {
6586
- F: __dxlog_file15,
6587
- L: 175,
6588
- S: void 0,
6589
- C: (f, a) => f(...a)
6590
- });
6451
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 196, S: void 0 });
6591
6452
  return [
6592
6453
  {
6593
6454
  changes
6594
6455
  }
6595
6456
  ];
6596
6457
  } else if (cancel) {
6597
- log10("cancel", void 0, {
6598
- F: __dxlog_file15,
6599
- L: 178,
6600
- S: void 0,
6601
- C: (f, a) => f(...a)
6602
- });
6458
+ log10("cancel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 205, S: void 0 });
6603
6459
  return [];
6604
6460
  }
6605
6461
  return tr;
@@ -7040,12 +6896,69 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
7040
6896
  });
7041
6897
  };
7042
6898
 
6899
+ // src/extensions/snippets.ts
6900
+ import { keymap as keymap12 } from "@codemirror/view";
6901
+ var defaultItems = [
6902
+ "hello world!",
6903
+ "this is a test.",
6904
+ "this is [DXOS](https://dxos.org)"
6905
+ ];
6906
+ var snippets2 = ({ delay = 75, items = defaultItems } = {}) => {
6907
+ let timer;
6908
+ let index = 0;
6909
+ return [
6910
+ keymap12.of([
6911
+ {
6912
+ // Reset.
6913
+ key: "alt-meta-'",
6914
+ run: () => {
6915
+ clearTimeout(timer);
6916
+ index = 0;
6917
+ return true;
6918
+ }
6919
+ },
6920
+ {
6921
+ // Next snippet.
6922
+ // TODO(burdon): Press 1-9 to select snippet?
6923
+ key: "Shift-Meta-'",
6924
+ run: (view) => {
6925
+ clearTimeout(timer);
6926
+ const text = items[index++];
6927
+ if (index === items?.length) {
6928
+ index = 0;
6929
+ }
6930
+ let offset = 0;
6931
+ const insert = (delayMs = 0) => {
6932
+ timer = setTimeout(() => {
6933
+ const pos = view.state.selection.main.head;
6934
+ view.dispatch({
6935
+ changes: {
6936
+ from: pos,
6937
+ insert: text[offset++]
6938
+ },
6939
+ selection: {
6940
+ anchor: pos + 1
6941
+ }
6942
+ });
6943
+ if (offset < text.length) {
6944
+ insert(Math.random() * delay * (text[offset] === " " ? 2 : 1));
6945
+ }
6946
+ }, delayMs);
6947
+ };
6948
+ insert();
6949
+ return true;
6950
+ }
6951
+ }
6952
+ ])
6953
+ ];
6954
+ };
6955
+
7043
6956
  // src/extensions/submit.ts
7044
6957
  import { Prec as Prec6 } from "@codemirror/state";
7045
- import { keymap as keymap12 } from "@codemirror/view";
6958
+ import { keymap as keymap13 } from "@codemirror/view";
7046
6959
  var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
7047
6960
  return [
7048
- Prec6.highest(keymap12.of([
6961
+ Prec6.highest(keymap13.of([
7049
6962
  {
7050
6963
  key: "Enter",
7051
6964
  preventDefault: true,
@@ -7371,36 +7284,46 @@ var fader = (options = {}) => {
7371
7284
  ];
7372
7285
  };
7373
7286
 
7374
- // src/extensions/tags/wire.ts
7287
+ // src/extensions/tags/typewriter.ts
7375
7288
  import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7376
7289
  import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7377
7290
  import { Domino as Domino3 } from "@dxos/ui";
7378
- var wireBypass = Annotation3.define();
7379
- var DEFAULT_RATE = 200;
7291
+ var typewriterBypass = Annotation3.define();
7292
+ var typewriterDrainingEffect = StateEffect11.define();
7380
7293
  var CURSOR_LINGER = 3e3;
7381
- var wire = (options = {}) => {
7382
- const rate = options.rate ?? DEFAULT_RATE;
7383
- const interval = 1e3 / rate;
7294
+ var FRAME_BUDGET_MS = 4;
7295
+ var CHARS_PER_FRAME = 5;
7296
+ var FLUSH_THRESHOLD = 2e3;
7297
+ var COMPACT_HEAD_THRESHOLD = 4096;
7298
+ var typewriter = (options = {}) => {
7384
7299
  const streamingTags = options.streamingTags ?? /* @__PURE__ */ new Set();
7300
+ const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;
7301
+ const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;
7302
+ const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;
7385
7303
  const suppressAppend = StateEffect11.define();
7386
7304
  const insertChunk = StateEffect11.define();
7387
7305
  const bufferField = StateField13.define({
7388
7306
  create: () => ({
7389
7307
  text: "",
7308
+ head: 0,
7390
7309
  insertAt: 0
7391
7310
  }),
7392
7311
  update: (value, tr) => {
7393
- let { text, insertAt } = value;
7312
+ let { text, head, insertAt } = value;
7394
7313
  for (const effect of tr.effects) {
7395
7314
  if (effect.is(suppressAppend)) {
7396
- text += effect.value.text;
7397
- if (text.length === effect.value.text.length) {
7315
+ if (text.length === head) {
7398
7316
  insertAt = effect.value.from;
7399
7317
  }
7318
+ text += effect.value.text;
7400
7319
  }
7401
7320
  if (effect.is(insertChunk)) {
7402
- text = text.slice(effect.value.text.length);
7321
+ head += effect.value.text.length;
7403
7322
  insertAt = effect.value.from + effect.value.text.length;
7323
+ if (head >= COMPACT_HEAD_THRESHOLD || head > 0 && head * 2 >= text.length) {
7324
+ text = text.slice(head);
7325
+ head = 0;
7326
+ }
7404
7327
  }
7405
7328
  }
7406
7329
  if (tr.docChanged) {
@@ -7415,6 +7338,7 @@ var wire = (options = {}) => {
7415
7338
  if (isReset) {
7416
7339
  return {
7417
7340
  text: "",
7341
+ head: 0,
7418
7342
  insertAt: 0
7419
7343
  };
7420
7344
  }
@@ -7424,6 +7348,7 @@ var wire = (options = {}) => {
7424
7348
  }
7425
7349
  return {
7426
7350
  text,
7351
+ head,
7427
7352
  insertAt
7428
7353
  };
7429
7354
  }
@@ -7432,7 +7357,7 @@ var wire = (options = {}) => {
7432
7357
  if (!tr.docChanged) {
7433
7358
  return tr;
7434
7359
  }
7435
- if (tr.annotation(wireBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7360
+ if (tr.annotation(typewriterBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7436
7361
  return tr;
7437
7362
  }
7438
7363
  let appendedText = "";
@@ -7461,40 +7386,82 @@ var wire = (options = {}) => {
7461
7386
  });
7462
7387
  const drainPlugin = ViewPlugin21.fromClass(class {
7463
7388
  view;
7464
- #timer;
7465
- #activeStreamTag = null;
7389
+ _raf;
7390
+ _activeStreamTag = null;
7466
7391
  constructor(view) {
7467
7392
  this.view = view;
7468
- this.#start();
7469
7393
  }
7470
7394
  update(update2) {
7471
- const buffer = update2.state.field(bufferField);
7472
- if (buffer.text.length === 0) {
7473
- this.#activeStreamTag = null;
7395
+ const { text, head } = update2.state.field(bufferField);
7396
+ const pending = text.length - head;
7397
+ if (pending === 0) {
7398
+ this._activeStreamTag = null;
7474
7399
  }
7475
- if (buffer.text.length > 0 && this.#timer === void 0) {
7476
- this.#start();
7400
+ if (pending > 0 && this._raf === void 0) {
7401
+ this._start();
7477
7402
  }
7478
7403
  }
7479
- #start() {
7480
- this.#timer = setInterval(() => {
7481
- const { text, insertAt } = this.view.state.field(bufferField);
7482
- if (text.length === 0) {
7483
- clearInterval(this.#timer);
7484
- this.#timer = void 0;
7485
- return;
7486
- }
7487
- const result = flushable(text, streamingTags, this.#activeStreamTag);
7404
+ _start() {
7405
+ queueMicrotask(() => {
7406
+ this.view.dispatch({
7407
+ effects: typewriterDrainingEffect.of(true),
7408
+ annotations: typewriterBypass.of(true)
7409
+ });
7410
+ });
7411
+ this._raf = requestAnimationFrame(this._tick);
7412
+ }
7413
+ _tick = () => {
7414
+ const { text, head, insertAt } = this.view.state.field(bufferField);
7415
+ const pending = text.length - head;
7416
+ if (pending === 0) {
7417
+ this.view.dispatch({
7418
+ effects: typewriterDrainingEffect.of(false),
7419
+ annotations: typewriterBypass.of(true)
7420
+ });
7421
+ this._raf = void 0;
7422
+ return;
7423
+ }
7424
+ if (pending > flushThreshold) {
7425
+ const chunk = text.slice(head);
7426
+ this._activeStreamTag = null;
7427
+ this.view.dispatch({
7428
+ changes: {
7429
+ from: insertAt,
7430
+ insert: chunk
7431
+ },
7432
+ effects: insertChunk.of({
7433
+ from: insertAt,
7434
+ text: chunk
7435
+ })
7436
+ });
7437
+ this._raf = requestAnimationFrame(this._tick);
7438
+ return;
7439
+ }
7440
+ const startTime = performance.now();
7441
+ let pos = head;
7442
+ let activeTag = this._activeStreamTag;
7443
+ let charsEmitted = 0;
7444
+ while (pos < text.length && performance.now() - startTime < frameBudgetMs) {
7445
+ const result = flushable(text, pos, streamingTags, activeTag);
7488
7446
  if (result.count === 0) {
7489
- return;
7447
+ break;
7448
+ }
7449
+ if (charsEmitted > 0 && charsEmitted + result.count > charsPerFrame) {
7450
+ break;
7490
7451
  }
7491
7452
  if (result.enterTag) {
7492
- this.#activeStreamTag = result.enterTag;
7453
+ activeTag = result.enterTag;
7493
7454
  }
7494
7455
  if (result.exitTag) {
7495
- this.#activeStreamTag = null;
7456
+ activeTag = null;
7496
7457
  }
7497
- const chunk = text.slice(0, result.count);
7458
+ pos += result.count;
7459
+ charsEmitted += result.count;
7460
+ }
7461
+ const totalCount = pos - head;
7462
+ if (totalCount > 0) {
7463
+ const chunk = text.slice(head, head + totalCount);
7464
+ this._activeStreamTag = activeTag;
7498
7465
  this.view.dispatch({
7499
7466
  changes: {
7500
7467
  from: insertAt,
@@ -7505,20 +7472,23 @@ var wire = (options = {}) => {
7505
7472
  text: chunk
7506
7473
  })
7507
7474
  });
7508
- }, interval);
7509
- }
7475
+ }
7476
+ this._raf = requestAnimationFrame(this._tick);
7477
+ };
7510
7478
  destroy() {
7511
- clearInterval(this.#timer);
7479
+ if (this._raf !== void 0) {
7480
+ cancelAnimationFrame(this._raf);
7481
+ }
7512
7482
  }
7513
7483
  });
7514
7484
  return [
7515
7485
  bufferField,
7516
7486
  filter,
7517
7487
  drainPlugin,
7518
- options.cursor && wireCursor(bufferField)
7488
+ options.cursor && typewriterCursor(bufferField)
7519
7489
  ].filter(Boolean);
7520
7490
  };
7521
- var wireCursor = (bufferField) => {
7491
+ var typewriterCursor = (bufferField) => {
7522
7492
  const hideCursor = StateEffect11.define();
7523
7493
  const visibilityField = StateField13.define({
7524
7494
  create: () => ({
@@ -7527,8 +7497,9 @@ var wireCursor = (bufferField) => {
7527
7497
  lastNonWsAt: 0
7528
7498
  }),
7529
7499
  update: (value, tr) => {
7530
- const { text, insertAt } = tr.state.field(bufferField);
7531
- if (text.length > 0) {
7500
+ const { text, head, insertAt } = tr.state.field(bufferField);
7501
+ const pending = text.length - head;
7502
+ if (pending > 0) {
7532
7503
  let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));
7533
7504
  if (tr.docChanged) {
7534
7505
  tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
@@ -7562,8 +7533,8 @@ var wireCursor = (bufferField) => {
7562
7533
  if (!visible) {
7563
7534
  return Decoration15.none;
7564
7535
  }
7565
- const { text } = tr.state.field(bufferField);
7566
- const cursorAt = text.length > 0 ? insertAt : lastNonWsAt;
7536
+ const { text, head } = tr.state.field(bufferField);
7537
+ const cursorAt = text.length > head ? insertAt : lastNonWsAt;
7567
7538
  const pos = Math.min(cursorAt, tr.state.doc.length);
7568
7539
  return Decoration15.set([
7569
7540
  Decoration15.widget({
@@ -7576,27 +7547,28 @@ var wireCursor = (bufferField) => {
7576
7547
  });
7577
7548
  const timerPlugin = ViewPlugin21.fromClass(class {
7578
7549
  view;
7579
- #timer;
7550
+ _timer;
7580
7551
  constructor(view) {
7581
7552
  this.view = view;
7582
7553
  }
7583
7554
  update(update2) {
7584
- const { text } = update2.state.field(bufferField);
7555
+ const { text, head } = update2.state.field(bufferField);
7585
7556
  const { visible } = update2.state.field(visibilityField);
7586
- if (text.length > 0) {
7587
- clearTimeout(this.#timer);
7588
- this.#timer = void 0;
7589
- } else if (visible && this.#timer === void 0) {
7590
- this.#timer = setTimeout(() => {
7557
+ const pending = text.length - head;
7558
+ if (pending > 0) {
7559
+ clearTimeout(this._timer);
7560
+ this._timer = void 0;
7561
+ } else if (visible && this._timer === void 0) {
7562
+ this._timer = setTimeout(() => {
7591
7563
  this.view.dispatch({
7592
7564
  effects: hideCursor.of(null)
7593
7565
  });
7594
- this.#timer = void 0;
7566
+ this._timer = void 0;
7595
7567
  }, CURSOR_LINGER);
7596
7568
  }
7597
7569
  }
7598
7570
  destroy() {
7599
- clearTimeout(this.#timer);
7571
+ clearTimeout(this._timer);
7600
7572
  }
7601
7573
  });
7602
7574
  return [
@@ -7605,7 +7577,12 @@ var wireCursor = (bufferField) => {
7605
7577
  timerPlugin
7606
7578
  ];
7607
7579
  };
7608
- var CursorWidget = class extends WidgetType9 {
7580
+ var CursorWidget = class _CursorWidget extends WidgetType9 {
7581
+ // All instances are interchangeable — let CM reuse the existing DOM across drips so
7582
+ // the blink animation isn't restarted on every transaction.
7583
+ eq(other) {
7584
+ return other instanceof _CursorWidget;
7585
+ }
7609
7586
  toDOM() {
7610
7587
  const inner = Domino3.of("span").text("\u2217").style({
7611
7588
  animation: "blink 1s infinite",
@@ -7617,35 +7594,37 @@ var CursorWidget = class extends WidgetType9 {
7617
7594
  }
7618
7595
  };
7619
7596
  var OPENING_TAG_NAME = /^<([a-zA-Z][\w-]*)/;
7597
+ var TAG_NAME_PROBE = 64;
7620
7598
  var escapeRegExpSource2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7621
- var flushable = (buffer, streamingTags, activeStreamTag) => {
7622
- if (buffer.length === 0) {
7599
+ var flushable = (buffer, start, streamingTags, activeStreamTag) => {
7600
+ if (start >= buffer.length) {
7623
7601
  return {
7624
7602
  count: 0
7625
7603
  };
7626
7604
  }
7627
7605
  if (activeStreamTag) {
7628
7606
  const closeTag = `</${activeStreamTag}>`;
7629
- if (buffer.startsWith(closeTag)) {
7607
+ if (buffer.startsWith(closeTag, start)) {
7630
7608
  return {
7631
7609
  count: closeTag.length,
7632
7610
  exitTag: true
7633
7611
  };
7634
7612
  }
7635
- if (buffer[0] === "<") {
7613
+ if (buffer[start] === "<") {
7636
7614
  return {
7637
- count: xmlElementLength(buffer)
7615
+ count: xmlElementLength(buffer, start)
7638
7616
  };
7639
7617
  }
7640
7618
  return {
7641
7619
  count: 1
7642
7620
  };
7643
7621
  }
7644
- const ch = buffer[0];
7622
+ const ch = buffer[start];
7645
7623
  if (ch === "<") {
7646
- const nameMatch = buffer.match(OPENING_TAG_NAME);
7624
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7625
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7647
7626
  if (nameMatch && streamingTags.has(nameMatch[1])) {
7648
- const close = buffer.indexOf(">");
7627
+ const close = buffer.indexOf(">", start);
7649
7628
  if (close === -1) {
7650
7629
  return {
7651
7630
  count: 0
@@ -7653,62 +7632,64 @@ var flushable = (buffer, streamingTags, activeStreamTag) => {
7653
7632
  }
7654
7633
  if (buffer[close - 1] === "/") {
7655
7634
  return {
7656
- count: close + 1
7635
+ count: close + 1 - start
7657
7636
  };
7658
7637
  }
7659
7638
  return {
7660
- count: close + 1,
7639
+ count: close + 1 - start,
7661
7640
  enterTag: nameMatch[1]
7662
7641
  };
7663
7642
  }
7664
7643
  return {
7665
- count: xmlElementLength(buffer)
7644
+ count: xmlElementLength(buffer, start)
7666
7645
  };
7667
7646
  }
7668
- if (ch === "!" && buffer.length > 1 && buffer[1] === "[") {
7647
+ if (ch === "!" && buffer.length > start + 1 && buffer[start + 1] === "[") {
7669
7648
  return {
7670
- count: linkLength(buffer, 1)
7649
+ count: linkLength(buffer, start, start + 1)
7671
7650
  };
7672
7651
  }
7673
7652
  if (ch === "[") {
7674
7653
  return {
7675
- count: linkLength(buffer, 0)
7654
+ count: linkLength(buffer, start, start)
7676
7655
  };
7677
7656
  }
7678
7657
  return {
7679
7658
  count: 1
7680
7659
  };
7681
7660
  };
7682
- var xmlElementLength = (buffer) => {
7683
- const close = buffer.indexOf(">");
7661
+ var xmlElementLength = (buffer, start = 0) => {
7662
+ const close = buffer.indexOf(">", start);
7684
7663
  if (close === -1) {
7685
7664
  return 0;
7686
7665
  }
7687
7666
  if (buffer[close - 1] === "/") {
7688
- return close + 1;
7667
+ return close + 1 - start;
7689
7668
  }
7690
- if (buffer[1] === "/") {
7691
- return close + 1;
7669
+ if (buffer[start + 1] === "/") {
7670
+ return close + 1 - start;
7692
7671
  }
7693
- const nameMatch = buffer.match(OPENING_TAG_NAME);
7672
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7673
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7694
7674
  if (!nameMatch) {
7695
7675
  return 1;
7696
7676
  }
7697
7677
  const tagName = nameMatch[1];
7698
7678
  let depth = 0;
7699
7679
  const tagPattern = new RegExp(`<(/?)${escapeRegExpSource2(tagName)}(\\s[^>]*)?>`, "g");
7680
+ tagPattern.lastIndex = start;
7700
7681
  let match;
7701
7682
  while ((match = tagPattern.exec(buffer)) !== null) {
7702
7683
  const isSelfClosing = match[0].endsWith("/>");
7703
7684
  const isClosing = match[1] === "/";
7704
7685
  if (isSelfClosing) {
7705
7686
  if (depth === 0) {
7706
- return match.index + match[0].length;
7687
+ return match.index + match[0].length - start;
7707
7688
  }
7708
7689
  } else if (isClosing) {
7709
7690
  depth--;
7710
7691
  if (depth === 0) {
7711
- return match.index + match[0].length;
7692
+ return match.index + match[0].length - start;
7712
7693
  }
7713
7694
  } else {
7714
7695
  depth++;
@@ -7716,8 +7697,8 @@ var xmlElementLength = (buffer) => {
7716
7697
  }
7717
7698
  return 0;
7718
7699
  };
7719
- var linkLength = (buffer, offset) => {
7720
- const bracketClose = buffer.indexOf("]", offset + 1);
7700
+ var linkLength = (buffer, start, bracketAt) => {
7701
+ const bracketClose = buffer.indexOf("]", bracketAt + 1);
7721
7702
  if (bracketClose === -1) {
7722
7703
  return 0;
7723
7704
  }
@@ -7731,13 +7712,179 @@ var linkLength = (buffer, offset) => {
7731
7712
  if (parenClose === -1) {
7732
7713
  return 0;
7733
7714
  }
7734
- return parenClose + 1;
7715
+ return parenClose + 1 - start;
7716
+ };
7717
+
7718
+ // src/extensions/tags/xml-block-decoration.ts
7719
+ import { xmlLanguage as xmlLanguage2 } from "@codemirror/lang-xml";
7720
+ import { Decoration as Decoration16, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7721
+ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7722
+ const lineDecoration = lineClass ? Decoration16.line({
7723
+ class: lineClass
7724
+ }) : void 0;
7725
+ const contentDecoration = contentClass ? Decoration16.mark({
7726
+ class: contentClass
7727
+ }) : void 0;
7728
+ const hideDecoration = hideTags ? Decoration16.replace({}) : void 0;
7729
+ const buildDecorations5 = (view) => {
7730
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7731
+ if (!text.includes(`<${tag}`)) {
7732
+ return Decoration16.none;
7733
+ }
7734
+ const tree = xmlLanguage2.parser.parse(text);
7735
+ const ranges = [];
7736
+ tree.iterate({
7737
+ enter: (node) => {
7738
+ if (node.type.name !== "Element") {
7739
+ return;
7740
+ }
7741
+ const openTag = node.node.getChild("OpenTag");
7742
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7743
+ const tagNameNode = openTag?.getChild("TagName");
7744
+ if (!openTag || !tagNameNode) {
7745
+ return;
7746
+ }
7747
+ if (text.slice(tagNameNode.from, tagNameNode.to) !== tag) {
7748
+ return;
7749
+ }
7750
+ const contentFrom = openTag.to;
7751
+ const contentTo = closeTag?.from ?? node.node.to;
7752
+ if (hideDecoration) {
7753
+ ranges.push(hideDecoration.range(openTag.from, openTag.to));
7754
+ if (closeTag) {
7755
+ ranges.push(hideDecoration.range(closeTag.from, closeTag.to));
7756
+ }
7757
+ }
7758
+ if (contentDecoration && contentFrom < contentTo) {
7759
+ ranges.push(contentDecoration.range(contentFrom, contentTo));
7760
+ }
7761
+ if (lineDecoration && contentFrom <= view.state.doc.length) {
7762
+ let pos = contentFrom;
7763
+ while (pos <= contentTo && pos <= view.state.doc.length) {
7764
+ const line = view.state.doc.lineAt(pos);
7765
+ ranges.push(lineDecoration.range(line.from));
7766
+ if (line.to >= contentTo) {
7767
+ break;
7768
+ }
7769
+ pos = line.to + 1;
7770
+ }
7771
+ }
7772
+ }
7773
+ });
7774
+ return Decoration16.set(ranges, true);
7775
+ };
7776
+ return ViewPlugin22.fromClass(class {
7777
+ decorations;
7778
+ constructor(view) {
7779
+ this.decorations = buildDecorations5(view);
7780
+ }
7781
+ update(update2) {
7782
+ if (update2.docChanged) {
7783
+ this.decorations = buildDecorations5(update2.view);
7784
+ }
7785
+ }
7786
+ }, {
7787
+ decorations: (instance) => instance.decorations
7788
+ });
7789
+ };
7790
+
7791
+ // src/extensions/tags/xml-formatting.ts
7792
+ import { xmlLanguage as xmlLanguage3 } from "@codemirror/lang-xml";
7793
+ import { Decoration as Decoration17, EditorView as EditorView31, ViewPlugin as ViewPlugin23 } from "@codemirror/view";
7794
+ var XML_TAG_NODES = /* @__PURE__ */ new Set([
7795
+ "OpenTag",
7796
+ "CloseTag",
7797
+ "SelfClosingTag",
7798
+ "MismatchedCloseTag"
7799
+ ]);
7800
+ var xmlElementMark = Decoration17.mark({
7801
+ class: "cm-xml-element"
7802
+ });
7803
+ var xmlTagMark = Decoration17.mark({
7804
+ class: "cm-xml-tag"
7805
+ });
7806
+ var xmlContentMark = Decoration17.mark({
7807
+ class: "cm-xml-content"
7808
+ });
7809
+ var xmlFormatting = ({ skip } = {}) => {
7810
+ const skipSet = skip && skip.length > 0 ? new Set(skip) : void 0;
7811
+ const buildDecorations5 = (view) => {
7812
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7813
+ if (!text.includes("<")) {
7814
+ return Decoration17.none;
7815
+ }
7816
+ const tagNameAt = (node) => text.slice(node.from, node.to);
7817
+ const tree = xmlLanguage3.parser.parse(text);
7818
+ const ranges = [];
7819
+ tree.iterate({
7820
+ enter: (node) => {
7821
+ const name = node.type.name;
7822
+ if (name === "SelfClosingTag" && node.from < node.to) {
7823
+ if (skipSet) {
7824
+ const tagNameNode = node.node.getChild("TagName");
7825
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7826
+ return false;
7827
+ }
7828
+ }
7829
+ ranges.push(xmlElementMark.range(node.from, node.to));
7830
+ ranges.push(xmlTagMark.range(node.from, node.to));
7831
+ return;
7832
+ }
7833
+ if (XML_TAG_NODES.has(name) && node.from < node.to) {
7834
+ ranges.push(xmlTagMark.range(node.from, node.to));
7835
+ return;
7836
+ }
7837
+ if (name === "Element" && node.from < node.to) {
7838
+ const openTag = node.node.getChild("OpenTag");
7839
+ if (openTag && skipSet) {
7840
+ const tagNameNode = openTag.getChild("TagName");
7841
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7842
+ return false;
7843
+ }
7844
+ }
7845
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7846
+ ranges.push(xmlElementMark.range(node.from, node.to));
7847
+ if (openTag && closeTag && openTag.to < closeTag.from) {
7848
+ ranges.push(xmlContentMark.range(openTag.to, closeTag.from));
7849
+ }
7850
+ }
7851
+ }
7852
+ });
7853
+ return Decoration17.set(ranges, true);
7854
+ };
7855
+ return [
7856
+ ViewPlugin23.fromClass(class {
7857
+ decorations;
7858
+ constructor(view) {
7859
+ this.decorations = buildDecorations5(view);
7860
+ }
7861
+ update(update2) {
7862
+ if (update2.docChanged) {
7863
+ this.decorations = buildDecorations5(update2.view);
7864
+ }
7865
+ }
7866
+ }, {
7867
+ decorations: (instance) => instance.decorations
7868
+ }),
7869
+ EditorView31.baseTheme({
7870
+ ".cm-xml-element": {
7871
+ backgroundColor: "var(--color-active-surface)",
7872
+ borderRadius: "0.25rem",
7873
+ padding: "0.25rem"
7874
+ },
7875
+ ".cm-xml-tag": {
7876
+ color: "var(--color-blue-500)",
7877
+ fontFamily: "var(--font-mono)"
7878
+ },
7879
+ ".cm-xml-content": {}
7880
+ })
7881
+ ];
7735
7882
  };
7736
7883
 
7737
7884
  // src/extensions/tags/xml-tags.ts
7738
7885
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7739
7886
  import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7740
- import { Decoration as Decoration16, EditorView as EditorView31, ViewPlugin as ViewPlugin22, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7887
+ import { Decoration as Decoration18, EditorView as EditorView32, ViewPlugin as ViewPlugin24, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
7741
7888
  import { invariant as invariant7 } from "@dxos/invariant";
7742
7889
  import { log as log11 } from "@dxos/log";
7743
7890
  import { Domino as Domino4 } from "@dxos/ui";
@@ -7746,15 +7893,7 @@ import { Domino as Domino4 } from "@dxos/ui";
7746
7893
  import { invariant as invariant6 } from "@dxos/invariant";
7747
7894
  var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7748
7895
  var nodeToJson = (state, node) => {
7749
- invariant6(node.type.name === "Element", "Node is not an Element", {
7750
- F: __dxlog_file16,
7751
- L: 18,
7752
- S: void 0,
7753
- A: [
7754
- "node.type.name === 'Element'",
7755
- "'Node is not an Element'"
7756
- ]
7757
- });
7896
+ invariant6(node.type.name === "Element", "Node is not an Element", { "~LogMeta": "~LogMeta", F: __dxlog_file16, L: 8, S: void 0, A: ["node.type.name === 'Element'", "'Node is not an Element'"] });
7758
7897
  const openTag = node.node.getChild("OpenTag") || node.node.getChild("SelfClosingTag");
7759
7898
  if (openTag) {
7760
7899
  const tagName = openTag.getChild("TagName");
@@ -7786,13 +7925,23 @@ var nodeToJson = (state, node) => {
7786
7925
  if (node.type.name === "Element" && openTag.type.name !== "SelfClosingTag") {
7787
7926
  const children = [];
7788
7927
  let child = node.node.firstChild;
7928
+ const appendText = (raw) => {
7929
+ if (raw.length === 0) {
7930
+ return;
7931
+ }
7932
+ const last = children[children.length - 1];
7933
+ if (typeof last === "string") {
7934
+ children[children.length - 1] = last + raw;
7935
+ } else {
7936
+ children.push(raw);
7937
+ }
7938
+ };
7789
7939
  while (child) {
7790
7940
  if (child.type.name !== "OpenTag" && child.type.name !== "CloseTag") {
7791
7941
  if (child.type.name === "Text") {
7792
- const text = state.doc.sliceString(child.from, child.to).trim();
7793
- if (text) {
7794
- children.push(text);
7795
- }
7942
+ appendText(state.doc.sliceString(child.from, child.to));
7943
+ } else if (child.type.name === "EntityReference" || child.type.name === "CharacterReference") {
7944
+ appendText(decodeXmlEntity(state.doc.sliceString(child.from, child.to)));
7796
7945
  } else if (child.type.name === "Element") {
7797
7946
  const data = nodeToJson(state, child);
7798
7947
  if (data) {
@@ -7802,13 +7951,48 @@ var nodeToJson = (state, node) => {
7802
7951
  }
7803
7952
  child = child.nextSibling;
7804
7953
  }
7954
+ if (children.length > 0 && typeof children[0] === "string") {
7955
+ children[0] = children[0].trimStart();
7956
+ }
7805
7957
  if (children.length > 0) {
7806
- tag.children = children;
7958
+ const lastIndex = children.length - 1;
7959
+ const last = children[lastIndex];
7960
+ if (typeof last === "string") {
7961
+ children[lastIndex] = last.trimEnd();
7962
+ }
7963
+ }
7964
+ const trimmed = children.filter((value) => typeof value !== "string" || value.length > 0);
7965
+ if (trimmed.length > 0) {
7966
+ tag.children = trimmed;
7807
7967
  }
7808
7968
  }
7809
7969
  return tag;
7810
7970
  }
7811
7971
  };
7972
+ var XML_NAMED_ENTITIES = {
7973
+ "&lt;": "<",
7974
+ "&gt;": ">",
7975
+ "&amp;": "&",
7976
+ "&quot;": '"',
7977
+ "&apos;": "'"
7978
+ };
7979
+ var decodeXmlEntity = (raw) => {
7980
+ const named = XML_NAMED_ENTITIES[raw];
7981
+ if (named !== void 0) {
7982
+ return named;
7983
+ }
7984
+ const numeric = /^&#(x?)([0-9a-fA-F]+);$/.exec(raw);
7985
+ if (numeric) {
7986
+ const code = parseInt(numeric[2], numeric[1] ? 16 : 10);
7987
+ if (Number.isFinite(code)) {
7988
+ try {
7989
+ return String.fromCodePoint(code);
7990
+ } catch {
7991
+ }
7992
+ }
7993
+ }
7994
+ return raw;
7995
+ };
7812
7996
 
7813
7997
  // src/extensions/tags/xml-tags.ts
7814
7998
  var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
@@ -7846,12 +8030,7 @@ var widgetStateMapStateField = StateField14.define({
7846
8030
  log11("widget updated", {
7847
8031
  id,
7848
8032
  value
7849
- }, {
7850
- F: __dxlog_file17,
7851
- L: 184,
7852
- S: void 0,
7853
- C: (f, a) => f(...a)
7854
- });
8033
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 59, S: void 0 });
7855
8034
  const state = typeof value === "function" ? value(map[id]) : value;
7856
8035
  return {
7857
8036
  ...map,
@@ -7862,25 +8041,16 @@ var widgetStateMapStateField = StateField14.define({
7862
8041
  return map;
7863
8042
  }
7864
8043
  });
7865
- var XML_WIDGET_DATA_ATTR = "data-xml-widget";
7866
- var xmlTags = ({ registry, setWidgets, bookmarks: bookmarks2, paragraphToWidgetGapRem } = {}) => {
8044
+ var xmlTags = ({ registry, setWidgets, bookmarks: bookmarks2 } = {}) => {
7867
8045
  const notifier = createWidgetMap(setWidgets);
7868
8046
  const widgetDecorationsField = createWidgetDecorationsField(registry, notifier);
7869
- const paragraphGapTheme = paragraphToWidgetGapRem != null && paragraphToWidgetGapRem > 0 ? EditorView31.baseTheme({
7870
- [`& .cm-content > .cm-line:not(:has([${XML_WIDGET_DATA_ATTR}])) + .cm-line:has([${XML_WIDGET_DATA_ATTR}])`]: {
7871
- marginTop: `${paragraphToWidgetGapRem}rem`
7872
- }
7873
- }) : null;
7874
8047
  return [
7875
8048
  widgetContextStateField,
7876
8049
  widgetStateMapStateField,
7877
8050
  widgetDecorationsField,
7878
8051
  createWidgetUpdatePlugin(widgetDecorationsField, notifier),
7879
8052
  createNavigationEffectPlugin(widgetDecorationsField, bookmarks2),
7880
- bookmarks2?.length ? Prec7.highest(keyHandlers) : [],
7881
- ...paragraphGapTheme ? [
7882
- paragraphGapTheme
7883
- ] : []
8053
+ bookmarks2?.length ? Prec7.highest(keyHandlers) : []
7884
8054
  ];
7885
8055
  };
7886
8056
  var createWidgetMap = (setWidgets) => {
@@ -7890,12 +8060,7 @@ var createWidgetMap = (setWidgets) => {
7890
8060
  log11("widget mounted", {
7891
8061
  id: state.id,
7892
8062
  tag: state.props._tag
7893
- }, {
7894
- F: __dxlog_file17,
7895
- L: 261,
7896
- S: void 0,
7897
- C: (f, a) => f(...a)
7898
- });
8063
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 101, S: void 0 });
7899
8064
  widgets.set(state.id, state);
7900
8065
  setWidgets?.([
7901
8066
  ...widgets.values()
@@ -7906,12 +8071,7 @@ var createWidgetMap = (setWidgets) => {
7906
8071
  log11("widget unmounted", {
7907
8072
  id,
7908
8073
  tag: state?.props._tag
7909
- }, {
7910
- F: __dxlog_file17,
7911
- L: 267,
7912
- S: void 0,
7913
- C: (f, a) => f(...a)
7914
- });
8074
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 112, S: void 0 });
7915
8075
  widgets.delete(id);
7916
8076
  setWidgets?.([
7917
8077
  ...widgets.values()
@@ -7920,7 +8080,7 @@ var createWidgetMap = (setWidgets) => {
7920
8080
  };
7921
8081
  return notifier;
7922
8082
  };
7923
- var keyHandlers = keymap13.of([
8083
+ var keyHandlers = keymap14.of([
7924
8084
  {
7925
8085
  key: "Mod-ArrowUp",
7926
8086
  run: (view) => {
@@ -7941,7 +8101,7 @@ var keyHandlers = keymap13.of([
7941
8101
  }
7942
8102
  ]);
7943
8103
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7944
- return EditorView31.updateListener.of((update2) => {
8104
+ return EditorView32.updateListener.of((update2) => {
7945
8105
  update2.transactions.forEach((transaction) => {
7946
8106
  for (const effect of transaction.effects) {
7947
8107
  if (effect.is(navigatePreviousEffect)) {
@@ -8026,7 +8186,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
8026
8186
  });
8027
8187
  });
8028
8188
  };
8029
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin22.fromClass(class {
8189
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
8030
8190
  update(update2) {
8031
8191
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
8032
8192
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -8071,7 +8231,7 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.def
8071
8231
  }
8072
8232
  return {
8073
8233
  from: 0,
8074
- decorations: Decoration16.none
8234
+ decorations: Decoration18.none
8075
8235
  };
8076
8236
  }
8077
8237
  }
@@ -8082,12 +8242,7 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.def
8082
8242
  log11("document reset", {
8083
8243
  from,
8084
8244
  to: state.doc.length
8085
- }, {
8086
- F: __dxlog_file17,
8087
- L: 429,
8088
- S: void 0,
8089
- C: (f, a) => f(...a)
8090
- });
8245
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 298, S: void 0 });
8091
8246
  return buildDecorations4(state, {
8092
8247
  from: 0,
8093
8248
  to: state.doc.length
@@ -8116,8 +8271,8 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.def
8116
8271
  };
8117
8272
  },
8118
8273
  provide: (field) => [
8119
- EditorView31.decorations.from(field, (v) => v.decorations),
8120
- EditorView31.atomicRanges.of((view) => view.state.field(field).decorations || Decoration16.none)
8274
+ EditorView32.decorations.from(field, (v) => v.decorations),
8275
+ EditorView32.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
8121
8276
  ]
8122
8277
  });
8123
8278
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -8128,7 +8283,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8128
8283
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
8129
8284
  return {
8130
8285
  from: range.from,
8131
- decorations: Decoration16.none
8286
+ decorations: Decoration18.none
8132
8287
  };
8133
8288
  }
8134
8289
  let last = range.from;
@@ -8164,7 +8319,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8164
8319
  };
8165
8320
  const widget = factory ? factory(props) ?? void 0 : Component ? new PlaceholderWidget2(widgetId, Component, props, notifier) : void 0;
8166
8321
  if (widget) {
8167
- builder.add(nodeRange.from, nodeRange.to, Decoration16.replace({
8322
+ builder.add(nodeRange.from, nodeRange.to, Decoration18.replace({
8168
8323
  widget,
8169
8324
  block,
8170
8325
  atomic: true,
@@ -8176,12 +8331,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8176
8331
  }
8177
8332
  }
8178
8333
  } catch (err) {
8179
- log11.catch(err, void 0, {
8180
- F: __dxlog_file17,
8181
- L: 538,
8182
- S: void 0,
8183
- C: (f, a) => f(...a)
8184
- });
8334
+ log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 401, S: void 0 });
8185
8335
  }
8186
8336
  return false;
8187
8337
  }
@@ -8228,7 +8378,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8228
8378
  };
8229
8379
  const widget = def.factory ? def.factory(mergedProps) ?? void 0 : def.Component ? new PlaceholderWidget2(widgetId, def.Component, mergedProps, notifier, true) : void 0;
8230
8380
  if (widget) {
8231
- builder.add(absoluteFrom, range.to, Decoration16.replace({
8381
+ builder.add(absoluteFrom, range.to, Decoration18.replace({
8232
8382
  widget,
8233
8383
  block: def.block,
8234
8384
  atomic: true,
@@ -8260,15 +8410,7 @@ var PlaceholderWidget2 = class extends WidgetType10 {
8260
8410
  #view;
8261
8411
  constructor(id, Component, props, notifier, streaming) {
8262
8412
  super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier, this.streaming = streaming;
8263
- invariant7(id, void 0, {
8264
- F: __dxlog_file17,
8265
- L: 641,
8266
- S: this,
8267
- A: [
8268
- "id",
8269
- ""
8270
- ]
8271
- });
8413
+ invariant7(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 488, S: this, A: ["id", ""] });
8272
8414
  }
8273
8415
  get root() {
8274
8416
  return this.#root;
@@ -8284,9 +8426,7 @@ var PlaceholderWidget2 = class extends WidgetType10 {
8284
8426
  }
8285
8427
  toDOM(view) {
8286
8428
  this.#view = view;
8287
- this.#root = Domino4.of("div").classNames("min-h-[24px]").attributes({
8288
- [XML_WIDGET_DATA_ATTR]: ""
8289
- }).root;
8429
+ this.#root = Domino4.of("div").classNames("min-h-[24px]").root;
8290
8430
  const props = Object.assign({}, this.props, {
8291
8431
  view
8292
8432
  });
@@ -8317,71 +8457,10 @@ var PlaceholderWidget2 = class extends WidgetType10 {
8317
8457
  this.#view = void 0;
8318
8458
  }
8319
8459
  };
8320
-
8321
- // src/extensions/typewriter.ts
8322
- import { keymap as keymap14 } from "@codemirror/view";
8323
- var defaultItems = [
8324
- "hello world!",
8325
- "this is a test.",
8326
- "this is [DXOS](https://dxos.org)"
8327
- ];
8328
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
8329
- let t;
8330
- let idx = 0;
8331
- return [
8332
- keymap14.of([
8333
- {
8334
- // Reset.
8335
- key: "alt-meta-'",
8336
- run: () => {
8337
- clearTimeout(t);
8338
- idx = 0;
8339
- return true;
8340
- }
8341
- },
8342
- {
8343
- // Next prompt.
8344
- // TODO(burdon): Press 1-9 to select prompt?
8345
- key: "Shift-Meta-'",
8346
- run: (view) => {
8347
- clearTimeout(t);
8348
- const text = items[idx++];
8349
- if (idx === items?.length) {
8350
- idx = 0;
8351
- }
8352
- let i = 0;
8353
- const insert = (d = 0) => {
8354
- t = setTimeout(() => {
8355
- const pos = view.state.selection.main.head;
8356
- view.dispatch({
8357
- changes: {
8358
- from: pos,
8359
- insert: text[i++]
8360
- },
8361
- selection: {
8362
- anchor: pos + 1
8363
- }
8364
- });
8365
- if (i < text.length) {
8366
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
8367
- }
8368
- }, d);
8369
- };
8370
- insert();
8371
- return true;
8372
- }
8373
- }
8374
- ])
8375
- ];
8376
- };
8377
8460
  export {
8378
8461
  Cursor,
8379
- EditorInputMode,
8380
- EditorInputModes,
8381
8462
  EditorState4 as EditorState,
8382
- EditorView32 as EditorView,
8383
- EditorViewMode,
8384
- EditorViewModes,
8463
+ EditorView33 as EditorView,
8385
8464
  Inline,
8386
8465
  InputModeExtensions,
8387
8466
  List,
@@ -8390,7 +8469,6 @@ export {
8390
8469
  SpaceAwarenessProvider,
8391
8470
  TextKind,
8392
8471
  Tree,
8393
- XML_WIDGET_DATA_ATTR,
8394
8472
  addBlockquote,
8395
8473
  addBookmark,
8396
8474
  addCodeblock,
@@ -8478,6 +8556,7 @@ export {
8478
8556
  markdownTagsExtensions,
8479
8557
  matchCompletion,
8480
8558
  mention,
8559
+ mobileSlots,
8481
8560
  modalStateEffect,
8482
8561
  modalStateField,
8483
8562
  moveItemDown,
@@ -8510,6 +8589,7 @@ export {
8510
8589
  setSelection,
8511
8590
  setStyle,
8512
8591
  singleValueFacet,
8592
+ snippets2 as snippets,
8513
8593
  staticCompletion,
8514
8594
  submit,
8515
8595
  tabbable,
@@ -8529,10 +8609,12 @@ export {
8529
8609
  treeFacet,
8530
8610
  typeahead,
8531
8611
  typewriter,
8532
- wire,
8533
- wireBypass,
8612
+ typewriterBypass,
8613
+ typewriterDrainingEffect,
8534
8614
  wrapWithCatch,
8615
+ xmlBlockDecoration,
8535
8616
  xmlElementLength,
8617
+ xmlFormatting,
8536
8618
  xmlTagContextEffect,
8537
8619
  xmlTagResetEffect,
8538
8620
  xmlTagUpdateEffect,