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