@dxos/ui-editor 0.8.4-main.422d1c7879 → 0.8.4-main.4f23b4e393

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 (122) hide show
  1. package/dist/lib/browser/index.mjs +538 -493
  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 +538 -492
  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.map +1 -1
  12. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  13. package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -1
  14. package/dist/types/src/extensions/autocomplete/autocomplete.d.ts.map +1 -1
  15. package/dist/types/src/extensions/autocomplete/match.d.ts.map +1 -1
  16. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +5 -2
  17. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -1
  18. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  21. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  23. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  25. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  26. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  27. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  28. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  29. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  30. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  31. package/dist/types/src/extensions/factories.d.ts +2 -1
  32. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  33. package/dist/types/src/extensions/factories.test.d.ts +2 -0
  34. package/dist/types/src/extensions/factories.test.d.ts.map +1 -0
  35. package/dist/types/src/extensions/focus.d.ts +1 -1
  36. package/dist/types/src/extensions/index.d.ts +1 -1
  37. package/dist/types/src/extensions/index.d.ts.map +1 -1
  38. package/dist/types/src/extensions/json.d.ts.map +1 -1
  39. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  40. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  41. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  42. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  43. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  45. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  47. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  49. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  50. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -1
  51. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  52. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  53. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  54. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  55. package/dist/types/src/extensions/replacer.d.ts.map +1 -1
  56. package/dist/types/src/extensions/scroller.d.ts.map +1 -1
  57. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  58. package/dist/types/src/extensions/snippets.d.ts +10 -0
  59. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  60. package/dist/types/src/extensions/submit.d.ts.map +1 -1
  61. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
  62. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -1
  63. package/dist/types/src/extensions/tags/index.d.ts +3 -1
  64. package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
  65. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  66. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  67. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  68. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  69. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  70. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  71. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  72. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  73. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  74. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -1
  75. package/dist/types/src/index.d.ts +0 -1
  76. package/dist/types/src/index.d.ts.map +1 -1
  77. package/dist/types/src/styles/theme.d.ts.map +1 -1
  78. package/dist/types/src/types/types.d.ts +2 -2
  79. package/dist/types/src/types/types.d.ts.map +1 -1
  80. package/dist/types/src/util/cursor.d.ts.map +1 -1
  81. package/dist/types/src/util/debug.d.ts.map +1 -1
  82. package/dist/types/src/util/decorations.d.ts.map +1 -1
  83. package/dist/types/src/util/dom.d.ts.map +1 -1
  84. package/dist/types/src/util/facet.d.ts.map +1 -1
  85. package/dist/types/src/util/util.d.ts.map +1 -1
  86. package/dist/types/tsconfig.tsbuildinfo +1 -1
  87. package/package.json +32 -32
  88. package/src/defaults.ts +5 -2
  89. package/src/extensions/auto-scroll.ts +9 -7
  90. package/src/extensions/autocomplete/placeholder.ts +37 -18
  91. package/src/extensions/automerge/automerge.test.tsx +35 -9
  92. package/src/extensions/factories.test.ts +88 -0
  93. package/src/extensions/factories.ts +20 -2
  94. package/src/extensions/index.ts +1 -1
  95. package/src/extensions/outliner/outliner.ts +1 -1
  96. package/src/extensions/scroller.ts +1 -1
  97. package/src/extensions/snippets.ts +67 -0
  98. package/src/extensions/tags/index.ts +3 -1
  99. package/src/extensions/tags/testing/text.md +36 -0
  100. package/src/extensions/tags/testing/text.txt +35 -0
  101. package/src/extensions/tags/{wire.test.ts → typewriter.test.ts} +2 -2
  102. package/src/extensions/tags/typewriter.ts +594 -0
  103. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  104. package/src/extensions/tags/xml-formatting.ts +125 -0
  105. package/src/extensions/tags/xml-tags.ts +0 -1
  106. package/src/extensions/tags/xml-util.test.ts +90 -3
  107. package/src/extensions/tags/xml-util.ts +62 -5
  108. package/src/index.ts +0 -1
  109. package/src/styles/theme.ts +15 -9
  110. package/src/typings.d.ts +8 -0
  111. package/dist/lib/browser/chunk-D724USEC.mjs +0 -34
  112. package/dist/lib/browser/chunk-D724USEC.mjs.map +0 -7
  113. package/dist/lib/node-esm/chunk-JRVJWKQF.mjs +0 -36
  114. package/dist/lib/node-esm/chunk-JRVJWKQF.mjs.map +0 -7
  115. package/dist/types/src/extensions/tags/wire.d.ts +0 -23
  116. package/dist/types/src/extensions/tags/wire.d.ts.map +0 -1
  117. package/dist/types/src/extensions/tags/wire.test.d.ts +0 -2
  118. package/dist/types/src/extensions/tags/wire.test.d.ts.map +0 -1
  119. package/dist/types/src/extensions/typewriter.d.ts +0 -10
  120. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  121. package/src/extensions/tags/wire.ts +0 -459
  122. package/src/extensions/typewriter.ts +0 -68
@@ -1,13 +1,6 @@
1
- import {
2
- EditorInputMode,
3
- EditorInputModes,
4
- EditorViewMode,
5
- EditorViewModes
6
- } from "./chunk-D724USEC.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"
@@ -275,12 +271,7 @@ var wrapWithCatch = (fn, label) => {
275
271
  } catch (err) {
276
272
  log.catch(err, {
277
273
  label
278
- }, {
279
- F: __dxlog_file,
280
- L: 20,
281
- S: void 0,
282
- C: (f, a) => f(...a)
283
- });
274
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 13, S: void 0 });
284
275
  }
285
276
  };
286
277
  };
@@ -306,12 +297,7 @@ var logChanges = (trs) => {
306
297
  if (changes.length) {
307
298
  log("changes", {
308
299
  changes
309
- }, {
310
- F: __dxlog_file,
311
- L: 54,
312
- S: void 0,
313
- C: (f, a) => f(...a)
314
- });
300
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 44, S: void 0 });
315
301
  }
316
302
  };
317
303
 
@@ -377,30 +363,37 @@ var insertAtLineStart = (view, from, insert) => {
377
363
  };
378
364
 
379
365
  // src/extensions/autocomplete/placeholder.ts
380
- var placeholder = ({ content, delay = 3e3 }) => {
366
+ var placeholder = ({ content, delay = 3e3, focusOnly = false }) => {
381
367
  const plugin = ViewPlugin3.fromClass(class {
382
368
  _timeout;
383
369
  _decorations = Decoration3.none;
384
370
  update(update2) {
371
+ if (!update2.docChanged && !update2.selectionSet && !update2.focusChanged) {
372
+ return;
373
+ }
385
374
  if (this._timeout) {
386
375
  window.clearTimeout(this._timeout);
387
376
  this._timeout = void 0;
388
377
  }
378
+ this._decorations = Decoration3.none;
379
+ if (focusOnly && !update2.view.hasFocus) {
380
+ return;
381
+ }
389
382
  const activeLine = update2.view.state.doc.lineAt(update2.view.state.selection.main.head);
390
- const isEmpty = activeLine.text.trim() === "";
391
- if (isEmpty) {
392
- const lineStart = activeLine.from;
393
- this._timeout = setTimeout(() => {
394
- this._decorations = Decoration3.set([
395
- Decoration3.widget({
396
- widget: new PlaceholderWidget(content),
397
- side: 1
398
- }).range(lineStart)
399
- ]);
400
- update2.view.update([]);
401
- }, delay);
383
+ if (activeLine.text.trim() !== "") {
384
+ return;
402
385
  }
403
- this._decorations = Decoration3.none;
386
+ const lineStart = activeLine.from;
387
+ const view = update2.view;
388
+ this._timeout = setTimeout(() => {
389
+ this._decorations = Decoration3.set([
390
+ Decoration3.widget({
391
+ widget: new PlaceholderWidget(content),
392
+ side: 1
393
+ }).range(lineStart)
394
+ ]);
395
+ view.update([]);
396
+ }, delay);
404
397
  }
405
398
  destroy() {
406
399
  if (this._timeout) {
@@ -587,7 +580,7 @@ var scroller = ({ overScroll = 0 } = {}) => {
587
580
  });
588
581
  return [
589
582
  scrollPlugin,
590
- // Listen for effect.s
583
+ // Listen for effect.
591
584
  EditorView4.updateListener.of((update2) => {
592
585
  update2.transactions.forEach((transaction) => {
593
586
  try {
@@ -602,12 +595,7 @@ var scroller = ({ overScroll = 0 } = {}) => {
602
595
  }
603
596
  }
604
597
  } catch (err) {
605
- log2.catch(err, void 0, {
606
- F: __dxlog_file2,
607
- L: 146,
608
- S: void 0,
609
- C: (f, a) => f(...a)
610
- });
598
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 91, S: void 0 });
611
599
  }
612
600
  });
613
601
  }),
@@ -778,12 +766,15 @@ var autoScroll = ({ scrollOnResize = true } = {}) => {
778
766
  if (this.destroyed) {
779
767
  return;
780
768
  }
781
- view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
769
+ view.scrollDOM.scrollTo({
770
+ top: view.scrollDOM.scrollHeight,
771
+ behavior: "instant"
772
+ });
782
773
  view.dispatch({
783
- effects: scrollerCrawlEffect.of(true)
774
+ effects: scrollerCrawlEffect.of(false)
784
775
  });
785
776
  });
786
- }, 100);
777
+ }, 50);
787
778
  this.observer = new ResizeObserver(() => {
788
779
  if (this.firstObservation) {
789
780
  this.firstObservation = false;
@@ -873,12 +864,7 @@ var cursorConverter = (accessor) => ({
873
864
  try {
874
865
  return toCursor(accessor, pos, assoc);
875
866
  } catch (err) {
876
- log3.catch(err, void 0, {
877
- F: __dxlog_file3,
878
- L: 15,
879
- S: void 0,
880
- C: (f, a) => f(...a)
881
- });
867
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 11, S: void 0 });
882
868
  return "";
883
869
  }
884
870
  },
@@ -886,12 +872,7 @@ var cursorConverter = (accessor) => ({
886
872
  try {
887
873
  return fromCursor(accessor, cursor);
888
874
  } catch (err) {
889
- log3.catch(err, void 0, {
890
- F: __dxlog_file3,
891
- L: 24,
892
- S: void 0,
893
- C: (f, a) => f(...a)
894
- });
875
+ log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 19, S: void 0 });
895
876
  return 0;
896
877
  }
897
878
  }
@@ -1076,12 +1057,7 @@ var Syncer = class {
1076
1057
  this._pending = false;
1077
1058
  }
1078
1059
  onEditorChange(view) {
1079
- log4("onEditorChange", void 0, {
1080
- F: __dxlog_file4,
1081
- L: 45,
1082
- S: this,
1083
- C: (f, a) => f(...a)
1084
- });
1060
+ log4("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 35, S: this });
1085
1061
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
1086
1062
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
1087
1063
  if (newHeads) {
@@ -1092,12 +1068,7 @@ var Syncer = class {
1092
1068
  }
1093
1069
  }
1094
1070
  onAutomergeChange(view) {
1095
- log4("onAutomergeChange", void 0, {
1096
- F: __dxlog_file4,
1097
- L: 60,
1098
- S: this,
1099
- C: (f, a) => f(...a)
1100
- });
1071
+ log4("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 47, S: this });
1101
1072
  const oldHeads = getLastHeads(view.state, this._state);
1102
1073
  const newHeads = A2.getHeads(this._handle.doc());
1103
1074
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1220,10 +1191,7 @@ var awareness = (provider = dummyProvider) => {
1220
1191
  ];
1221
1192
  };
1222
1193
  var RemoteSelectionsDecorator = class {
1223
- _ctx = new Context(void 0, {
1224
- F: __dxlog_file5,
1225
- L: 80
1226
- });
1194
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 33 });
1227
1195
  _cursorConverter;
1228
1196
  _provider;
1229
1197
  _lastAnchor;
@@ -1454,10 +1422,7 @@ var SpaceAwarenessProvider = class {
1454
1422
  this._info = info;
1455
1423
  }
1456
1424
  open() {
1457
- this._ctx = new Context2(void 0, {
1458
- F: __dxlog_file6,
1459
- L: 57
1460
- });
1425
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 28 });
1461
1426
  this._postTask = new DeferredTask(this._ctx, async () => {
1462
1427
  if (this._localState) {
1463
1428
  await this._messenger.postMessage(this._channel, {
@@ -1484,12 +1449,7 @@ var SpaceAwarenessProvider = class {
1484
1449
  }).catch((err) => {
1485
1450
  log5.debug("failed to query awareness", {
1486
1451
  err
1487
- }, {
1488
- F: __dxlog_file6,
1489
- L: 91,
1490
- S: this,
1491
- C: (f, a) => f(...a)
1492
- });
1452
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 57, S: this });
1493
1453
  });
1494
1454
  }
1495
1455
  close() {
@@ -1501,15 +1461,7 @@ var SpaceAwarenessProvider = class {
1501
1461
  return Array.from(this._remoteStates.values());
1502
1462
  }
1503
1463
  update(position) {
1504
- invariant(this._postTask, void 0, {
1505
- F: __dxlog_file6,
1506
- L: 106,
1507
- S: this,
1508
- A: [
1509
- "this._postTask",
1510
- ""
1511
- ]
1512
- });
1464
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 71, S: this, A: ["this._postTask", ""] });
1513
1465
  this._localState = {
1514
1466
  peerId: this._peerId,
1515
1467
  position,
@@ -1518,27 +1470,11 @@ var SpaceAwarenessProvider = class {
1518
1470
  this._postTask.schedule();
1519
1471
  }
1520
1472
  _handleQueryMessage() {
1521
- invariant(this._postTask, void 0, {
1522
- F: __dxlog_file6,
1523
- L: 117,
1524
- S: this,
1525
- A: [
1526
- "this._postTask",
1527
- ""
1528
- ]
1529
- });
1473
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 80, S: this, A: ["this._postTask", ""] });
1530
1474
  this._postTask.schedule();
1531
1475
  }
1532
1476
  _handlePostMessage(message) {
1533
- invariant(message.kind === "post", void 0, {
1534
- F: __dxlog_file6,
1535
- L: 122,
1536
- S: this,
1537
- A: [
1538
- "message.kind === 'post'",
1539
- ""
1540
- ]
1541
- });
1477
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1542
1478
  this._remoteStates.set(message.state.peerId, message.state);
1543
1479
  this.remoteStateChange.emit();
1544
1480
  }
@@ -1667,15 +1603,7 @@ var Blaster = class {
1667
1603
  return this._node;
1668
1604
  }
1669
1605
  initialize() {
1670
- invariant2(!this._canvas && !this._ctx, void 0, {
1671
- F: __dxlog_file7,
1672
- L: 142,
1673
- S: this,
1674
- A: [
1675
- "!this._canvas && !this._ctx",
1676
- ""
1677
- ]
1678
- });
1606
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1679
1607
  this._canvas = document.createElement("canvas");
1680
1608
  this._canvas.id = "code-blast-canvas";
1681
1609
  this._canvas.style.position = "absolute";
@@ -1704,15 +1632,7 @@ var Blaster = class {
1704
1632
  }
1705
1633
  }
1706
1634
  start() {
1707
- invariant2(this._canvas && this._ctx, void 0, {
1708
- F: __dxlog_file7,
1709
- L: 181,
1710
- S: this,
1711
- A: [
1712
- "this._canvas && this._ctx",
1713
- ""
1714
- ]
1715
- });
1635
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1716
1636
  this._running = true;
1717
1637
  this.loop();
1718
1638
  }
@@ -1962,12 +1882,7 @@ var bookmarks = () => {
1962
1882
  key: "Mod-ArrowUp",
1963
1883
  run: (view) => {
1964
1884
  const bookmarks2 = view.state.field(bookmarksField);
1965
- log6("up", bookmarks2, {
1966
- F: __dxlog_file8,
1967
- L: 29,
1968
- S: void 0,
1969
- C: (f, a) => f(...a)
1970
- });
1885
+ log6("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 18, S: void 0 });
1971
1886
  return true;
1972
1887
  }
1973
1888
  },
@@ -1975,12 +1890,7 @@ var bookmarks = () => {
1975
1890
  key: "Mod-ArrowDown",
1976
1891
  run: (view) => {
1977
1892
  const bookmarks2 = view.state.field(bookmarksField);
1978
- log6("down", bookmarks2, {
1979
- F: __dxlog_file8,
1980
- L: 37,
1981
- S: void 0,
1982
- C: (f, a) => f(...a)
1983
- });
1893
+ log6("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0 });
1984
1894
  return true;
1985
1895
  }
1986
1896
  }
@@ -2045,28 +1955,12 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
2045
1955
  };
2046
1956
  var createEditorStateStore = (keyPrefix) => ({
2047
1957
  getState: (id) => {
2048
- invariant3(id, void 0, {
2049
- F: __dxlog_file9,
2050
- L: 47,
2051
- S: void 0,
2052
- A: [
2053
- "id",
2054
- ""
2055
- ]
2056
- });
1958
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 26, S: void 0, A: ["id", ""] });
2057
1959
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
2058
1960
  return state ? JSON.parse(state) : void 0;
2059
1961
  },
2060
1962
  setState: (id, state) => {
2061
- invariant3(id, void 0, {
2062
- F: __dxlog_file9,
2063
- L: 53,
2064
- S: void 0,
2065
- A: [
2066
- "id",
2067
- ""
2068
- ]
2069
- });
1963
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 31, S: void 0, A: ["id", ""] });
2070
1964
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
2071
1965
  }
2072
1966
  });
@@ -2188,12 +2082,7 @@ var commentsDecorations = EditorView11.decorations.compute([
2188
2082
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2189
2083
  const range = comment.range;
2190
2084
  if (!range) {
2191
- log7.warn("Invalid range:", range, {
2192
- F: __dxlog_file10,
2193
- L: 139,
2194
- S: void 0,
2195
- C: (f, a) => f(...a)
2196
- });
2085
+ log7.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 93, S: void 0 });
2197
2086
  return void 0;
2198
2087
  } else if (range.from === range.to) {
2199
2088
  return void 0;
@@ -2659,9 +2548,15 @@ var baseTheme = EditorView13.baseTheme({
2659
2548
  overflowAnchor: "auto"
2660
2549
  },
2661
2550
  ".cm-scroller::-webkit-scrollbar": {
2662
- width: "8px"
2551
+ width: "var(--scrollbar-size,8px)",
2552
+ height: "var(--scrollbar-size,8px)"
2553
+ },
2554
+ ".cm-scroller::-webkit-scrollbar-corner": {
2555
+ background: "transparent"
2556
+ },
2557
+ ".cm-scroller::-webkit-scrollbar-track": {
2558
+ background: "transparent"
2663
2559
  },
2664
- ".cm-scroller::-webkit-scrollbar-track": {},
2665
2560
  ".cm-scroller::-webkit-scrollbar-thumb": {
2666
2561
  background: "transparent",
2667
2562
  transition: "background 0.15s"
@@ -2698,7 +2593,7 @@ var baseTheme = EditorView13.baseTheme({
2698
2593
  * Height is set to match the corresponding line (which may have wrapped).
2699
2594
  */
2700
2595
  ".cm-gutterElement": {
2701
- lineHeight: 1.5,
2596
+ lineHeight: "24px",
2702
2597
  fontSize: "12px"
2703
2598
  },
2704
2599
  /**
@@ -2796,12 +2691,12 @@ var baseTheme = EditorView13.baseTheme({
2796
2691
  padding: "4px"
2797
2692
  },
2798
2693
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2799
- background: "var(--color-active-surface)",
2800
- color: "var(--color-base-surface-text)"
2694
+ background: "var(--color-current-surface)",
2695
+ color: "var(--color-base-foreground)"
2801
2696
  },
2802
2697
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2803
2698
  paddingLeft: "4px !important",
2804
- color: "var(--color-base-surface-text)"
2699
+ color: "var(--color-base-foreground)"
2805
2700
  },
2806
2701
  /**
2807
2702
  * Completion info.
@@ -2820,7 +2715,7 @@ var baseTheme = EditorView13.baseTheme({
2820
2715
  padding: "0 4px"
2821
2716
  },
2822
2717
  ".cm-completionMatchedText": {
2823
- color: "var(--color-base-surface-text)",
2718
+ color: "var(--color-base-foreground)",
2824
2719
  textDecoration: "none !important"
2825
2720
  },
2826
2721
  /**
@@ -2855,7 +2750,7 @@ var baseTheme = EditorView13.baseTheme({
2855
2750
  backgroundColor: "var(--color-input-surface)"
2856
2751
  },
2857
2752
  ".cm-panel input:focus, .cm-panel button:focus": {
2858
- outline: "1px solid var(--color-neutral-focus-indicator)"
2753
+ outline: "1px solid var(--color-focus-ring-subtle)"
2859
2754
  },
2860
2755
  ".cm-panel label": {
2861
2756
  display: "inline-flex",
@@ -2868,7 +2763,7 @@ var baseTheme = EditorView13.baseTheme({
2868
2763
  height: "8px",
2869
2764
  marginRight: "6px !important",
2870
2765
  padding: "2px !important",
2871
- color: "var(--color-neutral-focus-indicator)"
2766
+ color: "var(--color-focus-ring-subtle)"
2872
2767
  },
2873
2768
  ".cm-panel button": {
2874
2769
  "&:hover": {
@@ -3013,12 +2908,7 @@ var createBasicExtensions = (propsProp) => {
3013
2908
  return [
3014
2909
  // NOTE: Doesn't catch errors in keymap functions.
3015
2910
  EditorView16.exceptionSink.of((err) => {
3016
- log8.catch(err, void 0, {
3017
- F: __dxlog_file11,
3018
- L: 130,
3019
- S: void 0,
3020
- C: (f, a) => f(...a)
3021
- });
2911
+ log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
3022
2912
  }),
3023
2913
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
3024
2914
  props.bracketMatching && bracketMatching(),
@@ -3038,6 +2928,13 @@ var createBasicExtensions = (propsProp) => {
3038
2928
  props.lineWrapping && EditorView16.lineWrapping,
3039
2929
  props.placeholder && placeholder2(props.placeholder),
3040
2930
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2931
+ // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
2932
+ // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that
2933
+ // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits
2934
+ // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)
2935
+ // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers
2936
+ // depend on being able to populate the doc themselves.
2937
+ props.readOnly && EditorState.transactionFilter.of((tr) => tr.docChanged && (tr.isUserEvent("input") || tr.isUserEvent("delete") || tr.isUserEvent("undo") || tr.isUserEvent("redo")) ? [] : tr),
3041
2938
  props.scrollPastEnd && scrollPastEnd(),
3042
2939
  props.tabbable && tabbable,
3043
2940
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -3080,7 +2977,7 @@ var defaultStyles = {
3080
2977
  dark: vscodeDarkStyle,
3081
2978
  light: vscodeLightStyle
3082
2979
  };
3083
- var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp } = {}) => {
2980
+ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp, themeMode } = {}) => {
3084
2981
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
3085
2982
  return [
3086
2983
  baseTheme,
@@ -3095,11 +2992,14 @@ var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHig
3095
2992
  slots.content?.className && EditorView16.contentAttributes.of({
3096
2993
  class: slots.content.className
3097
2994
  }),
3098
- slots.scroller?.className && ViewPlugin12.fromClass(class {
2995
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
3099
2996
  constructor(view) {
3100
2997
  if (slots.scroller?.className) {
3101
2998
  view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
3102
2999
  }
3000
+ if (scrollbarThin) {
3001
+ view.scrollDOM.style.setProperty("--scrollbar-size", "4px");
3002
+ }
3103
3003
  }
3104
3004
  })
3105
3005
  ].filter(isTruthy2);
@@ -5356,15 +5256,7 @@ var buildDecorations2 = (view, options, focus2) => {
5356
5256
  const { state } = view;
5357
5257
  const headerLevels = [];
5358
5258
  const getHeaderLevels = (node, level) => {
5359
- invariant4(level > 0, void 0, {
5360
- F: __dxlog_file12,
5361
- L: 177,
5362
- S: void 0,
5363
- A: [
5364
- "level > 0",
5365
- ""
5366
- ]
5367
- });
5259
+ invariant4(level > 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 160, S: void 0, A: ["level > 0", ""] });
5368
5260
  if (level > headerLevels.length) {
5369
5261
  const len = headerLevels.length;
5370
5262
  headerLevels.length = level;
@@ -5395,15 +5287,7 @@ var buildDecorations2 = (view, options, focus2) => {
5395
5287
  listLevels.pop();
5396
5288
  };
5397
5289
  const getCurrentListLevel = () => {
5398
- invariant4(listLevels.length, void 0, {
5399
- F: __dxlog_file12,
5400
- L: 199,
5401
- S: void 0,
5402
- A: [
5403
- "listLevels.length",
5404
- ""
5405
- ]
5406
- });
5290
+ invariant4(listLevels.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 192, S: void 0, A: ["listLevels.length", ""] });
5407
5291
  return listLevels[listLevels.length - 1];
5408
5292
  };
5409
5293
  const enterNode = (node) => {
@@ -5825,12 +5709,7 @@ var mention = ({ debug, onSearch }) => {
5825
5709
  (context) => {
5826
5710
  log9.info("completion context", {
5827
5711
  context
5828
- }, {
5829
- F: __dxlog_file13,
5830
- L: 27,
5831
- S: void 0,
5832
- C: (f, a) => f(...a)
5833
- });
5712
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 18, S: void 0 });
5834
5713
  const match = context.matchBefore(/@(\w+)?/);
5835
5714
  if (!match || match.from === match.to && !context.explicit) {
5836
5715
  return null;
@@ -6063,15 +5942,7 @@ var outlinerTree = (_options = {}) => {
6063
5942
  break;
6064
5943
  }
6065
5944
  case "BulletList": {
6066
- invariant5(current, void 0, {
6067
- F: __dxlog_file14,
6068
- L: 219,
6069
- S: void 0,
6070
- A: [
6071
- "current",
6072
- ""
6073
- ]
6074
- });
5945
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 169, S: void 0, A: ["current", ""] });
6075
5946
  parent = current;
6076
5947
  if (current) {
6077
5948
  current.lineRange.to = current.node.from;
@@ -6080,15 +5951,7 @@ var outlinerTree = (_options = {}) => {
6080
5951
  break;
6081
5952
  }
6082
5953
  case "ListItem": {
6083
- invariant5(parent, void 0, {
6084
- F: __dxlog_file14,
6085
- L: 228,
6086
- S: void 0,
6087
- A: [
6088
- "parent",
6089
- ""
6090
- ]
6091
- });
5954
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 179, S: void 0, A: ["parent", ""] });
6092
5955
  const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
6093
5956
  const docRange = {
6094
5957
  from: state.doc.lineAt(node.from).from,
@@ -6122,42 +5985,18 @@ var outlinerTree = (_options = {}) => {
6122
5985
  break;
6123
5986
  }
6124
5987
  case "ListMark": {
6125
- invariant5(current, void 0, {
6126
- F: __dxlog_file14,
6127
- L: 272,
6128
- S: void 0,
6129
- A: [
6130
- "current",
6131
- ""
6132
- ]
6133
- });
5988
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 219, S: void 0, A: ["current", ""] });
6134
5989
  current.type = "bullet";
6135
5990
  current.contentRange.from = node.from + "- ".length;
6136
5991
  break;
6137
5992
  }
6138
5993
  case "Task": {
6139
- invariant5(current, void 0, {
6140
- F: __dxlog_file14,
6141
- L: 278,
6142
- S: void 0,
6143
- A: [
6144
- "current",
6145
- ""
6146
- ]
6147
- });
5994
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 226, S: void 0, A: ["current", ""] });
6148
5995
  current.type = "task";
6149
5996
  break;
6150
5997
  }
6151
5998
  case "TaskMarker": {
6152
- invariant5(current, void 0, {
6153
- F: __dxlog_file14,
6154
- L: 283,
6155
- S: void 0,
6156
- A: [
6157
- "current",
6158
- ""
6159
- ]
6160
- });
5999
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 232, S: void 0, A: ["current", ""] });
6161
6000
  current.contentRange.from = node.from + "[ ] ".length;
6162
6001
  break;
6163
6002
  }
@@ -6165,29 +6004,13 @@ var outlinerTree = (_options = {}) => {
6165
6004
  },
6166
6005
  leave: (node) => {
6167
6006
  if (node.name === "BulletList") {
6168
- invariant5(parent, void 0, {
6169
- F: __dxlog_file14,
6170
- L: 291,
6171
- S: void 0,
6172
- A: [
6173
- "parent",
6174
- ""
6175
- ]
6176
- });
6007
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 240, S: void 0, A: ["parent", ""] });
6177
6008
  prevSiblings[level--] = void 0;
6178
6009
  parent = parent.parent;
6179
6010
  }
6180
6011
  }
6181
6012
  });
6182
- invariant5(tree, void 0, {
6183
- F: __dxlog_file14,
6184
- L: 298,
6185
- S: void 0,
6186
- A: [
6187
- "tree",
6188
- ""
6189
- ]
6190
- });
6013
+ invariant5(tree, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 246, S: void 0, A: ["tree", ""] });
6191
6014
  return tree;
6192
6015
  };
6193
6016
  return [
@@ -6629,35 +6452,20 @@ var editor = () => [
6629
6452
  text: insert.toString(),
6630
6453
  length: insert.length
6631
6454
  }
6632
- }, {
6633
- F: __dxlog_file15,
6634
- L: 164,
6635
- S: void 0,
6636
- C: (f, a) => f(...a)
6637
- });
6455
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 174, S: void 0 });
6638
6456
  }
6639
6457
  });
6640
6458
  if (changes.length > 0) {
6641
6459
  log10("modified,", {
6642
6460
  changes
6643
- }, {
6644
- F: __dxlog_file15,
6645
- L: 175,
6646
- S: void 0,
6647
- C: (f, a) => f(...a)
6648
- });
6461
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 196, S: void 0 });
6649
6462
  return [
6650
6463
  {
6651
6464
  changes
6652
6465
  }
6653
6466
  ];
6654
6467
  } else if (cancel) {
6655
- log10("cancel", void 0, {
6656
- F: __dxlog_file15,
6657
- L: 178,
6658
- S: void 0,
6659
- C: (f, a) => f(...a)
6660
- });
6468
+ log10("cancel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 205, S: void 0 });
6661
6469
  return [];
6662
6470
  }
6663
6471
  return tr;
@@ -6831,7 +6639,7 @@ var decorations = () => [
6831
6639
  marginBottom: "2px"
6832
6640
  },
6833
6641
  ".cm-list-item-focused": {
6834
- borderColor: "var(--color-neutral-focus-indicator)"
6642
+ borderColor: "var(--color-focus-ring-subtle)"
6835
6643
  },
6836
6644
  "&:focus-within .cm-list-item-selected": {
6837
6645
  borderColor: "var(--color-separator)"
@@ -7098,12 +6906,69 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
7098
6906
  });
7099
6907
  };
7100
6908
 
6909
+ // src/extensions/snippets.ts
6910
+ import { keymap as keymap12 } from "@codemirror/view";
6911
+ var defaultItems = [
6912
+ "hello world!",
6913
+ "this is a test.",
6914
+ "this is [DXOS](https://dxos.org)"
6915
+ ];
6916
+ var snippets2 = ({ delay = 75, items = defaultItems } = {}) => {
6917
+ let timer;
6918
+ let index = 0;
6919
+ return [
6920
+ keymap12.of([
6921
+ {
6922
+ // Reset.
6923
+ key: "alt-meta-'",
6924
+ run: () => {
6925
+ clearTimeout(timer);
6926
+ index = 0;
6927
+ return true;
6928
+ }
6929
+ },
6930
+ {
6931
+ // Next snippet.
6932
+ // TODO(burdon): Press 1-9 to select snippet?
6933
+ key: "Shift-Meta-'",
6934
+ run: (view) => {
6935
+ clearTimeout(timer);
6936
+ const text = items[index++];
6937
+ if (index === items?.length) {
6938
+ index = 0;
6939
+ }
6940
+ let offset = 0;
6941
+ const insert = (delayMs = 0) => {
6942
+ timer = setTimeout(() => {
6943
+ const pos = view.state.selection.main.head;
6944
+ view.dispatch({
6945
+ changes: {
6946
+ from: pos,
6947
+ insert: text[offset++]
6948
+ },
6949
+ selection: {
6950
+ anchor: pos + 1
6951
+ }
6952
+ });
6953
+ if (offset < text.length) {
6954
+ insert(Math.random() * delay * (text[offset] === " " ? 2 : 1));
6955
+ }
6956
+ }, delayMs);
6957
+ };
6958
+ insert();
6959
+ return true;
6960
+ }
6961
+ }
6962
+ ])
6963
+ ];
6964
+ };
6965
+
7101
6966
  // src/extensions/submit.ts
7102
6967
  import { Prec as Prec6 } from "@codemirror/state";
7103
- import { keymap as keymap12 } from "@codemirror/view";
6968
+ import { keymap as keymap13 } from "@codemirror/view";
7104
6969
  var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
7105
6970
  return [
7106
- Prec6.highest(keymap12.of([
6971
+ Prec6.highest(keymap13.of([
7107
6972
  {
7108
6973
  key: "Enter",
7109
6974
  preventDefault: true,
@@ -7429,36 +7294,46 @@ var fader = (options = {}) => {
7429
7294
  ];
7430
7295
  };
7431
7296
 
7432
- // src/extensions/tags/wire.ts
7297
+ // src/extensions/tags/typewriter.ts
7433
7298
  import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7434
7299
  import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7435
7300
  import { Domino as Domino3 } from "@dxos/ui";
7436
- var wireBypass = Annotation3.define();
7437
- var DEFAULT_RATE = 200;
7301
+ var typewriterBypass = Annotation3.define();
7302
+ var typewriterDrainingEffect = StateEffect11.define();
7438
7303
  var CURSOR_LINGER = 3e3;
7439
- var wire = (options = {}) => {
7440
- const rate = options.rate ?? DEFAULT_RATE;
7441
- const interval = 1e3 / rate;
7304
+ var FRAME_BUDGET_MS = 4;
7305
+ var CHARS_PER_FRAME = 5;
7306
+ var FLUSH_THRESHOLD = 2e3;
7307
+ var COMPACT_HEAD_THRESHOLD = 4096;
7308
+ var typewriter = (options = {}) => {
7442
7309
  const streamingTags = options.streamingTags ?? /* @__PURE__ */ new Set();
7310
+ const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;
7311
+ const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;
7312
+ const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;
7443
7313
  const suppressAppend = StateEffect11.define();
7444
7314
  const insertChunk = StateEffect11.define();
7445
7315
  const bufferField = StateField13.define({
7446
7316
  create: () => ({
7447
7317
  text: "",
7318
+ head: 0,
7448
7319
  insertAt: 0
7449
7320
  }),
7450
7321
  update: (value, tr) => {
7451
- let { text, insertAt } = value;
7322
+ let { text, head, insertAt } = value;
7452
7323
  for (const effect of tr.effects) {
7453
7324
  if (effect.is(suppressAppend)) {
7454
- text += effect.value.text;
7455
- if (text.length === effect.value.text.length) {
7325
+ if (text.length === head) {
7456
7326
  insertAt = effect.value.from;
7457
7327
  }
7328
+ text += effect.value.text;
7458
7329
  }
7459
7330
  if (effect.is(insertChunk)) {
7460
- text = text.slice(effect.value.text.length);
7331
+ head += effect.value.text.length;
7461
7332
  insertAt = effect.value.from + effect.value.text.length;
7333
+ if (head >= COMPACT_HEAD_THRESHOLD || head > 0 && head * 2 >= text.length) {
7334
+ text = text.slice(head);
7335
+ head = 0;
7336
+ }
7462
7337
  }
7463
7338
  }
7464
7339
  if (tr.docChanged) {
@@ -7473,6 +7348,7 @@ var wire = (options = {}) => {
7473
7348
  if (isReset) {
7474
7349
  return {
7475
7350
  text: "",
7351
+ head: 0,
7476
7352
  insertAt: 0
7477
7353
  };
7478
7354
  }
@@ -7482,6 +7358,7 @@ var wire = (options = {}) => {
7482
7358
  }
7483
7359
  return {
7484
7360
  text,
7361
+ head,
7485
7362
  insertAt
7486
7363
  };
7487
7364
  }
@@ -7490,7 +7367,7 @@ var wire = (options = {}) => {
7490
7367
  if (!tr.docChanged) {
7491
7368
  return tr;
7492
7369
  }
7493
- if (tr.annotation(wireBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7370
+ if (tr.annotation(typewriterBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7494
7371
  return tr;
7495
7372
  }
7496
7373
  let appendedText = "";
@@ -7519,40 +7396,82 @@ var wire = (options = {}) => {
7519
7396
  });
7520
7397
  const drainPlugin = ViewPlugin21.fromClass(class {
7521
7398
  view;
7522
- #timer;
7523
- #activeStreamTag = null;
7399
+ _raf;
7400
+ _activeStreamTag = null;
7524
7401
  constructor(view) {
7525
7402
  this.view = view;
7526
- this.#start();
7527
7403
  }
7528
7404
  update(update2) {
7529
- const buffer = update2.state.field(bufferField);
7530
- if (buffer.text.length === 0) {
7531
- this.#activeStreamTag = null;
7405
+ const { text, head } = update2.state.field(bufferField);
7406
+ const pending = text.length - head;
7407
+ if (pending === 0) {
7408
+ this._activeStreamTag = null;
7532
7409
  }
7533
- if (buffer.text.length > 0 && this.#timer === void 0) {
7534
- this.#start();
7410
+ if (pending > 0 && this._raf === void 0) {
7411
+ this._start();
7535
7412
  }
7536
7413
  }
7537
- #start() {
7538
- this.#timer = setInterval(() => {
7539
- const { text, insertAt } = this.view.state.field(bufferField);
7540
- if (text.length === 0) {
7541
- clearInterval(this.#timer);
7542
- this.#timer = void 0;
7543
- return;
7544
- }
7545
- const result = flushable(text, streamingTags, this.#activeStreamTag);
7414
+ _start() {
7415
+ queueMicrotask(() => {
7416
+ this.view.dispatch({
7417
+ effects: typewriterDrainingEffect.of(true),
7418
+ annotations: typewriterBypass.of(true)
7419
+ });
7420
+ });
7421
+ this._raf = requestAnimationFrame(this._tick);
7422
+ }
7423
+ _tick = () => {
7424
+ const { text, head, insertAt } = this.view.state.field(bufferField);
7425
+ const pending = text.length - head;
7426
+ if (pending === 0) {
7427
+ this.view.dispatch({
7428
+ effects: typewriterDrainingEffect.of(false),
7429
+ annotations: typewriterBypass.of(true)
7430
+ });
7431
+ this._raf = void 0;
7432
+ return;
7433
+ }
7434
+ if (pending > flushThreshold) {
7435
+ const chunk = text.slice(head);
7436
+ this._activeStreamTag = null;
7437
+ this.view.dispatch({
7438
+ changes: {
7439
+ from: insertAt,
7440
+ insert: chunk
7441
+ },
7442
+ effects: insertChunk.of({
7443
+ from: insertAt,
7444
+ text: chunk
7445
+ })
7446
+ });
7447
+ this._raf = requestAnimationFrame(this._tick);
7448
+ return;
7449
+ }
7450
+ const startTime = performance.now();
7451
+ let pos = head;
7452
+ let activeTag = this._activeStreamTag;
7453
+ let charsEmitted = 0;
7454
+ while (pos < text.length && performance.now() - startTime < frameBudgetMs) {
7455
+ const result = flushable(text, pos, streamingTags, activeTag);
7546
7456
  if (result.count === 0) {
7547
- return;
7457
+ break;
7458
+ }
7459
+ if (charsEmitted > 0 && charsEmitted + result.count > charsPerFrame) {
7460
+ break;
7548
7461
  }
7549
7462
  if (result.enterTag) {
7550
- this.#activeStreamTag = result.enterTag;
7463
+ activeTag = result.enterTag;
7551
7464
  }
7552
7465
  if (result.exitTag) {
7553
- this.#activeStreamTag = null;
7466
+ activeTag = null;
7554
7467
  }
7555
- const chunk = text.slice(0, result.count);
7468
+ pos += result.count;
7469
+ charsEmitted += result.count;
7470
+ }
7471
+ const totalCount = pos - head;
7472
+ if (totalCount > 0) {
7473
+ const chunk = text.slice(head, head + totalCount);
7474
+ this._activeStreamTag = activeTag;
7556
7475
  this.view.dispatch({
7557
7476
  changes: {
7558
7477
  from: insertAt,
@@ -7563,20 +7482,23 @@ var wire = (options = {}) => {
7563
7482
  text: chunk
7564
7483
  })
7565
7484
  });
7566
- }, interval);
7567
- }
7485
+ }
7486
+ this._raf = requestAnimationFrame(this._tick);
7487
+ };
7568
7488
  destroy() {
7569
- clearInterval(this.#timer);
7489
+ if (this._raf !== void 0) {
7490
+ cancelAnimationFrame(this._raf);
7491
+ }
7570
7492
  }
7571
7493
  });
7572
7494
  return [
7573
7495
  bufferField,
7574
7496
  filter,
7575
7497
  drainPlugin,
7576
- options.cursor && wireCursor(bufferField)
7498
+ options.cursor && typewriterCursor(bufferField)
7577
7499
  ].filter(Boolean);
7578
7500
  };
7579
- var wireCursor = (bufferField) => {
7501
+ var typewriterCursor = (bufferField) => {
7580
7502
  const hideCursor = StateEffect11.define();
7581
7503
  const visibilityField = StateField13.define({
7582
7504
  create: () => ({
@@ -7585,8 +7507,9 @@ var wireCursor = (bufferField) => {
7585
7507
  lastNonWsAt: 0
7586
7508
  }),
7587
7509
  update: (value, tr) => {
7588
- const { text, insertAt } = tr.state.field(bufferField);
7589
- if (text.length > 0) {
7510
+ const { text, head, insertAt } = tr.state.field(bufferField);
7511
+ const pending = text.length - head;
7512
+ if (pending > 0) {
7590
7513
  let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));
7591
7514
  if (tr.docChanged) {
7592
7515
  tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
@@ -7620,8 +7543,8 @@ var wireCursor = (bufferField) => {
7620
7543
  if (!visible) {
7621
7544
  return Decoration15.none;
7622
7545
  }
7623
- const { text } = tr.state.field(bufferField);
7624
- const cursorAt = text.length > 0 ? insertAt : lastNonWsAt;
7546
+ const { text, head } = tr.state.field(bufferField);
7547
+ const cursorAt = text.length > head ? insertAt : lastNonWsAt;
7625
7548
  const pos = Math.min(cursorAt, tr.state.doc.length);
7626
7549
  return Decoration15.set([
7627
7550
  Decoration15.widget({
@@ -7634,27 +7557,28 @@ var wireCursor = (bufferField) => {
7634
7557
  });
7635
7558
  const timerPlugin = ViewPlugin21.fromClass(class {
7636
7559
  view;
7637
- #timer;
7560
+ _timer;
7638
7561
  constructor(view) {
7639
7562
  this.view = view;
7640
7563
  }
7641
7564
  update(update2) {
7642
- const { text } = update2.state.field(bufferField);
7565
+ const { text, head } = update2.state.field(bufferField);
7643
7566
  const { visible } = update2.state.field(visibilityField);
7644
- if (text.length > 0) {
7645
- clearTimeout(this.#timer);
7646
- this.#timer = void 0;
7647
- } else if (visible && this.#timer === void 0) {
7648
- this.#timer = setTimeout(() => {
7567
+ const pending = text.length - head;
7568
+ if (pending > 0) {
7569
+ clearTimeout(this._timer);
7570
+ this._timer = void 0;
7571
+ } else if (visible && this._timer === void 0) {
7572
+ this._timer = setTimeout(() => {
7649
7573
  this.view.dispatch({
7650
7574
  effects: hideCursor.of(null)
7651
7575
  });
7652
- this.#timer = void 0;
7576
+ this._timer = void 0;
7653
7577
  }, CURSOR_LINGER);
7654
7578
  }
7655
7579
  }
7656
7580
  destroy() {
7657
- clearTimeout(this.#timer);
7581
+ clearTimeout(this._timer);
7658
7582
  }
7659
7583
  });
7660
7584
  return [
@@ -7663,7 +7587,12 @@ var wireCursor = (bufferField) => {
7663
7587
  timerPlugin
7664
7588
  ];
7665
7589
  };
7666
- var CursorWidget = class extends WidgetType9 {
7590
+ var CursorWidget = class _CursorWidget extends WidgetType9 {
7591
+ // All instances are interchangeable — let CM reuse the existing DOM across drips so
7592
+ // the blink animation isn't restarted on every transaction.
7593
+ eq(other) {
7594
+ return other instanceof _CursorWidget;
7595
+ }
7667
7596
  toDOM() {
7668
7597
  const inner = Domino3.of("span").text("\u2217").style({
7669
7598
  animation: "blink 1s infinite",
@@ -7675,35 +7604,37 @@ var CursorWidget = class extends WidgetType9 {
7675
7604
  }
7676
7605
  };
7677
7606
  var OPENING_TAG_NAME = /^<([a-zA-Z][\w-]*)/;
7607
+ var TAG_NAME_PROBE = 64;
7678
7608
  var escapeRegExpSource2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7679
- var flushable = (buffer, streamingTags, activeStreamTag) => {
7680
- if (buffer.length === 0) {
7609
+ var flushable = (buffer, start, streamingTags, activeStreamTag) => {
7610
+ if (start >= buffer.length) {
7681
7611
  return {
7682
7612
  count: 0
7683
7613
  };
7684
7614
  }
7685
7615
  if (activeStreamTag) {
7686
7616
  const closeTag = `</${activeStreamTag}>`;
7687
- if (buffer.startsWith(closeTag)) {
7617
+ if (buffer.startsWith(closeTag, start)) {
7688
7618
  return {
7689
7619
  count: closeTag.length,
7690
7620
  exitTag: true
7691
7621
  };
7692
7622
  }
7693
- if (buffer[0] === "<") {
7623
+ if (buffer[start] === "<") {
7694
7624
  return {
7695
- count: xmlElementLength(buffer)
7625
+ count: xmlElementLength(buffer, start)
7696
7626
  };
7697
7627
  }
7698
7628
  return {
7699
7629
  count: 1
7700
7630
  };
7701
7631
  }
7702
- const ch = buffer[0];
7632
+ const ch = buffer[start];
7703
7633
  if (ch === "<") {
7704
- const nameMatch = buffer.match(OPENING_TAG_NAME);
7634
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7635
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7705
7636
  if (nameMatch && streamingTags.has(nameMatch[1])) {
7706
- const close = buffer.indexOf(">");
7637
+ const close = buffer.indexOf(">", start);
7707
7638
  if (close === -1) {
7708
7639
  return {
7709
7640
  count: 0
@@ -7711,62 +7642,64 @@ var flushable = (buffer, streamingTags, activeStreamTag) => {
7711
7642
  }
7712
7643
  if (buffer[close - 1] === "/") {
7713
7644
  return {
7714
- count: close + 1
7645
+ count: close + 1 - start
7715
7646
  };
7716
7647
  }
7717
7648
  return {
7718
- count: close + 1,
7649
+ count: close + 1 - start,
7719
7650
  enterTag: nameMatch[1]
7720
7651
  };
7721
7652
  }
7722
7653
  return {
7723
- count: xmlElementLength(buffer)
7654
+ count: xmlElementLength(buffer, start)
7724
7655
  };
7725
7656
  }
7726
- if (ch === "!" && buffer.length > 1 && buffer[1] === "[") {
7657
+ if (ch === "!" && buffer.length > start + 1 && buffer[start + 1] === "[") {
7727
7658
  return {
7728
- count: linkLength(buffer, 1)
7659
+ count: linkLength(buffer, start, start + 1)
7729
7660
  };
7730
7661
  }
7731
7662
  if (ch === "[") {
7732
7663
  return {
7733
- count: linkLength(buffer, 0)
7664
+ count: linkLength(buffer, start, start)
7734
7665
  };
7735
7666
  }
7736
7667
  return {
7737
7668
  count: 1
7738
7669
  };
7739
7670
  };
7740
- var xmlElementLength = (buffer) => {
7741
- const close = buffer.indexOf(">");
7671
+ var xmlElementLength = (buffer, start = 0) => {
7672
+ const close = buffer.indexOf(">", start);
7742
7673
  if (close === -1) {
7743
7674
  return 0;
7744
7675
  }
7745
7676
  if (buffer[close - 1] === "/") {
7746
- return close + 1;
7677
+ return close + 1 - start;
7747
7678
  }
7748
- if (buffer[1] === "/") {
7749
- return close + 1;
7679
+ if (buffer[start + 1] === "/") {
7680
+ return close + 1 - start;
7750
7681
  }
7751
- const nameMatch = buffer.match(OPENING_TAG_NAME);
7682
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7683
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7752
7684
  if (!nameMatch) {
7753
7685
  return 1;
7754
7686
  }
7755
7687
  const tagName = nameMatch[1];
7756
7688
  let depth = 0;
7757
7689
  const tagPattern = new RegExp(`<(/?)${escapeRegExpSource2(tagName)}(\\s[^>]*)?>`, "g");
7690
+ tagPattern.lastIndex = start;
7758
7691
  let match;
7759
7692
  while ((match = tagPattern.exec(buffer)) !== null) {
7760
7693
  const isSelfClosing = match[0].endsWith("/>");
7761
7694
  const isClosing = match[1] === "/";
7762
7695
  if (isSelfClosing) {
7763
7696
  if (depth === 0) {
7764
- return match.index + match[0].length;
7697
+ return match.index + match[0].length - start;
7765
7698
  }
7766
7699
  } else if (isClosing) {
7767
7700
  depth--;
7768
7701
  if (depth === 0) {
7769
- return match.index + match[0].length;
7702
+ return match.index + match[0].length - start;
7770
7703
  }
7771
7704
  } else {
7772
7705
  depth++;
@@ -7774,8 +7707,8 @@ var xmlElementLength = (buffer) => {
7774
7707
  }
7775
7708
  return 0;
7776
7709
  };
7777
- var linkLength = (buffer, offset) => {
7778
- const bracketClose = buffer.indexOf("]", offset + 1);
7710
+ var linkLength = (buffer, start, bracketAt) => {
7711
+ const bracketClose = buffer.indexOf("]", bracketAt + 1);
7779
7712
  if (bracketClose === -1) {
7780
7713
  return 0;
7781
7714
  }
@@ -7789,13 +7722,179 @@ var linkLength = (buffer, offset) => {
7789
7722
  if (parenClose === -1) {
7790
7723
  return 0;
7791
7724
  }
7792
- return parenClose + 1;
7725
+ return parenClose + 1 - start;
7726
+ };
7727
+
7728
+ // src/extensions/tags/xml-block-decoration.ts
7729
+ import { xmlLanguage as xmlLanguage2 } from "@codemirror/lang-xml";
7730
+ import { Decoration as Decoration16, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7731
+ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7732
+ const lineDecoration = lineClass ? Decoration16.line({
7733
+ class: lineClass
7734
+ }) : void 0;
7735
+ const contentDecoration = contentClass ? Decoration16.mark({
7736
+ class: contentClass
7737
+ }) : void 0;
7738
+ const hideDecoration = hideTags ? Decoration16.replace({}) : void 0;
7739
+ const buildDecorations5 = (view) => {
7740
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7741
+ if (!text.includes(`<${tag}`)) {
7742
+ return Decoration16.none;
7743
+ }
7744
+ const tree = xmlLanguage2.parser.parse(text);
7745
+ const ranges = [];
7746
+ tree.iterate({
7747
+ enter: (node) => {
7748
+ if (node.type.name !== "Element") {
7749
+ return;
7750
+ }
7751
+ const openTag = node.node.getChild("OpenTag");
7752
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7753
+ const tagNameNode = openTag?.getChild("TagName");
7754
+ if (!openTag || !tagNameNode) {
7755
+ return;
7756
+ }
7757
+ if (text.slice(tagNameNode.from, tagNameNode.to) !== tag) {
7758
+ return;
7759
+ }
7760
+ const contentFrom = openTag.to;
7761
+ const contentTo = closeTag?.from ?? node.node.to;
7762
+ if (hideDecoration) {
7763
+ ranges.push(hideDecoration.range(openTag.from, openTag.to));
7764
+ if (closeTag) {
7765
+ ranges.push(hideDecoration.range(closeTag.from, closeTag.to));
7766
+ }
7767
+ }
7768
+ if (contentDecoration && contentFrom < contentTo) {
7769
+ ranges.push(contentDecoration.range(contentFrom, contentTo));
7770
+ }
7771
+ if (lineDecoration && contentFrom <= view.state.doc.length) {
7772
+ let pos = contentFrom;
7773
+ while (pos <= contentTo && pos <= view.state.doc.length) {
7774
+ const line = view.state.doc.lineAt(pos);
7775
+ ranges.push(lineDecoration.range(line.from));
7776
+ if (line.to >= contentTo) {
7777
+ break;
7778
+ }
7779
+ pos = line.to + 1;
7780
+ }
7781
+ }
7782
+ }
7783
+ });
7784
+ return Decoration16.set(ranges, true);
7785
+ };
7786
+ return ViewPlugin22.fromClass(class {
7787
+ decorations;
7788
+ constructor(view) {
7789
+ this.decorations = buildDecorations5(view);
7790
+ }
7791
+ update(update2) {
7792
+ if (update2.docChanged) {
7793
+ this.decorations = buildDecorations5(update2.view);
7794
+ }
7795
+ }
7796
+ }, {
7797
+ decorations: (instance) => instance.decorations
7798
+ });
7799
+ };
7800
+
7801
+ // src/extensions/tags/xml-formatting.ts
7802
+ import { xmlLanguage as xmlLanguage3 } from "@codemirror/lang-xml";
7803
+ import { Decoration as Decoration17, EditorView as EditorView31, ViewPlugin as ViewPlugin23 } from "@codemirror/view";
7804
+ var XML_TAG_NODES = /* @__PURE__ */ new Set([
7805
+ "OpenTag",
7806
+ "CloseTag",
7807
+ "SelfClosingTag",
7808
+ "MismatchedCloseTag"
7809
+ ]);
7810
+ var xmlElementMark = Decoration17.mark({
7811
+ class: "cm-xml-element"
7812
+ });
7813
+ var xmlTagMark = Decoration17.mark({
7814
+ class: "cm-xml-tag"
7815
+ });
7816
+ var xmlContentMark = Decoration17.mark({
7817
+ class: "cm-xml-content"
7818
+ });
7819
+ var xmlFormatting = ({ skip } = {}) => {
7820
+ const skipSet = skip && skip.length > 0 ? new Set(skip) : void 0;
7821
+ const buildDecorations5 = (view) => {
7822
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7823
+ if (!text.includes("<")) {
7824
+ return Decoration17.none;
7825
+ }
7826
+ const tagNameAt = (node) => text.slice(node.from, node.to);
7827
+ const tree = xmlLanguage3.parser.parse(text);
7828
+ const ranges = [];
7829
+ tree.iterate({
7830
+ enter: (node) => {
7831
+ const name = node.type.name;
7832
+ if (name === "SelfClosingTag" && node.from < node.to) {
7833
+ if (skipSet) {
7834
+ const tagNameNode = node.node.getChild("TagName");
7835
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7836
+ return false;
7837
+ }
7838
+ }
7839
+ ranges.push(xmlElementMark.range(node.from, node.to));
7840
+ ranges.push(xmlTagMark.range(node.from, node.to));
7841
+ return;
7842
+ }
7843
+ if (XML_TAG_NODES.has(name) && node.from < node.to) {
7844
+ ranges.push(xmlTagMark.range(node.from, node.to));
7845
+ return;
7846
+ }
7847
+ if (name === "Element" && node.from < node.to) {
7848
+ const openTag = node.node.getChild("OpenTag");
7849
+ if (openTag && skipSet) {
7850
+ const tagNameNode = openTag.getChild("TagName");
7851
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7852
+ return false;
7853
+ }
7854
+ }
7855
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7856
+ ranges.push(xmlElementMark.range(node.from, node.to));
7857
+ if (openTag && closeTag && openTag.to < closeTag.from) {
7858
+ ranges.push(xmlContentMark.range(openTag.to, closeTag.from));
7859
+ }
7860
+ }
7861
+ }
7862
+ });
7863
+ return Decoration17.set(ranges, true);
7864
+ };
7865
+ return [
7866
+ ViewPlugin23.fromClass(class {
7867
+ decorations;
7868
+ constructor(view) {
7869
+ this.decorations = buildDecorations5(view);
7870
+ }
7871
+ update(update2) {
7872
+ if (update2.docChanged) {
7873
+ this.decorations = buildDecorations5(update2.view);
7874
+ }
7875
+ }
7876
+ }, {
7877
+ decorations: (instance) => instance.decorations
7878
+ }),
7879
+ EditorView31.baseTheme({
7880
+ ".cm-xml-element": {
7881
+ backgroundColor: "var(--color-current-surface)",
7882
+ borderRadius: "0.25rem",
7883
+ padding: "0.25rem"
7884
+ },
7885
+ ".cm-xml-tag": {
7886
+ color: "var(--color-blue-500)",
7887
+ fontFamily: "var(--font-mono)"
7888
+ },
7889
+ ".cm-xml-content": {}
7890
+ })
7891
+ ];
7793
7892
  };
7794
7893
 
7795
7894
  // src/extensions/tags/xml-tags.ts
7796
7895
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7797
7896
  import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7798
- import { Decoration as Decoration16, EditorView as EditorView31, ViewPlugin as ViewPlugin22, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7897
+ import { Decoration as Decoration18, EditorView as EditorView32, ViewPlugin as ViewPlugin24, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
7799
7898
  import { invariant as invariant7 } from "@dxos/invariant";
7800
7899
  import { log as log11 } from "@dxos/log";
7801
7900
  import { Domino as Domino4 } from "@dxos/ui";
@@ -7804,15 +7903,7 @@ import { Domino as Domino4 } from "@dxos/ui";
7804
7903
  import { invariant as invariant6 } from "@dxos/invariant";
7805
7904
  var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7806
7905
  var nodeToJson = (state, node) => {
7807
- invariant6(node.type.name === "Element", "Node is not an Element", {
7808
- F: __dxlog_file16,
7809
- L: 18,
7810
- S: void 0,
7811
- A: [
7812
- "node.type.name === 'Element'",
7813
- "'Node is not an Element'"
7814
- ]
7815
- });
7906
+ 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'"] });
7816
7907
  const openTag = node.node.getChild("OpenTag") || node.node.getChild("SelfClosingTag");
7817
7908
  if (openTag) {
7818
7909
  const tagName = openTag.getChild("TagName");
@@ -7844,13 +7935,23 @@ var nodeToJson = (state, node) => {
7844
7935
  if (node.type.name === "Element" && openTag.type.name !== "SelfClosingTag") {
7845
7936
  const children = [];
7846
7937
  let child = node.node.firstChild;
7938
+ const appendText = (raw) => {
7939
+ if (raw.length === 0) {
7940
+ return;
7941
+ }
7942
+ const last = children[children.length - 1];
7943
+ if (typeof last === "string") {
7944
+ children[children.length - 1] = last + raw;
7945
+ } else {
7946
+ children.push(raw);
7947
+ }
7948
+ };
7847
7949
  while (child) {
7848
7950
  if (child.type.name !== "OpenTag" && child.type.name !== "CloseTag") {
7849
7951
  if (child.type.name === "Text") {
7850
- const text = state.doc.sliceString(child.from, child.to).trim();
7851
- if (text) {
7852
- children.push(text);
7853
- }
7952
+ appendText(state.doc.sliceString(child.from, child.to));
7953
+ } else if (child.type.name === "EntityReference" || child.type.name === "CharacterReference") {
7954
+ appendText(decodeXmlEntity(state.doc.sliceString(child.from, child.to)));
7854
7955
  } else if (child.type.name === "Element") {
7855
7956
  const data = nodeToJson(state, child);
7856
7957
  if (data) {
@@ -7860,13 +7961,48 @@ var nodeToJson = (state, node) => {
7860
7961
  }
7861
7962
  child = child.nextSibling;
7862
7963
  }
7964
+ if (children.length > 0 && typeof children[0] === "string") {
7965
+ children[0] = children[0].trimStart();
7966
+ }
7863
7967
  if (children.length > 0) {
7864
- tag.children = children;
7968
+ const lastIndex = children.length - 1;
7969
+ const last = children[lastIndex];
7970
+ if (typeof last === "string") {
7971
+ children[lastIndex] = last.trimEnd();
7972
+ }
7973
+ }
7974
+ const trimmed = children.filter((value) => typeof value !== "string" || value.length > 0);
7975
+ if (trimmed.length > 0) {
7976
+ tag.children = trimmed;
7865
7977
  }
7866
7978
  }
7867
7979
  return tag;
7868
7980
  }
7869
7981
  };
7982
+ var XML_NAMED_ENTITIES = {
7983
+ "&lt;": "<",
7984
+ "&gt;": ">",
7985
+ "&amp;": "&",
7986
+ "&quot;": '"',
7987
+ "&apos;": "'"
7988
+ };
7989
+ var decodeXmlEntity = (raw) => {
7990
+ const named = XML_NAMED_ENTITIES[raw];
7991
+ if (named !== void 0) {
7992
+ return named;
7993
+ }
7994
+ const numeric = /^&#(x?)([0-9a-fA-F]+);$/.exec(raw);
7995
+ if (numeric) {
7996
+ const code = parseInt(numeric[2], numeric[1] ? 16 : 10);
7997
+ if (Number.isFinite(code)) {
7998
+ try {
7999
+ return String.fromCodePoint(code);
8000
+ } catch {
8001
+ }
8002
+ }
8003
+ }
8004
+ return raw;
8005
+ };
7870
8006
 
7871
8007
  // src/extensions/tags/xml-tags.ts
7872
8008
  var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
@@ -7904,12 +8040,7 @@ var widgetStateMapStateField = StateField14.define({
7904
8040
  log11("widget updated", {
7905
8041
  id,
7906
8042
  value
7907
- }, {
7908
- F: __dxlog_file17,
7909
- L: 184,
7910
- S: void 0,
7911
- C: (f, a) => f(...a)
7912
- });
8043
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 59, S: void 0 });
7913
8044
  const state = typeof value === "function" ? value(map[id]) : value;
7914
8045
  return {
7915
8046
  ...map,
@@ -7939,12 +8070,7 @@ var createWidgetMap = (setWidgets) => {
7939
8070
  log11("widget mounted", {
7940
8071
  id: state.id,
7941
8072
  tag: state.props._tag
7942
- }, {
7943
- F: __dxlog_file17,
7944
- L: 238,
7945
- S: void 0,
7946
- C: (f, a) => f(...a)
7947
- });
8073
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 101, S: void 0 });
7948
8074
  widgets.set(state.id, state);
7949
8075
  setWidgets?.([
7950
8076
  ...widgets.values()
@@ -7955,12 +8081,7 @@ var createWidgetMap = (setWidgets) => {
7955
8081
  log11("widget unmounted", {
7956
8082
  id,
7957
8083
  tag: state?.props._tag
7958
- }, {
7959
- F: __dxlog_file17,
7960
- L: 244,
7961
- S: void 0,
7962
- C: (f, a) => f(...a)
7963
- });
8084
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 112, S: void 0 });
7964
8085
  widgets.delete(id);
7965
8086
  setWidgets?.([
7966
8087
  ...widgets.values()
@@ -7969,7 +8090,7 @@ var createWidgetMap = (setWidgets) => {
7969
8090
  };
7970
8091
  return notifier;
7971
8092
  };
7972
- var keyHandlers = keymap13.of([
8093
+ var keyHandlers = keymap14.of([
7973
8094
  {
7974
8095
  key: "Mod-ArrowUp",
7975
8096
  run: (view) => {
@@ -7990,7 +8111,7 @@ var keyHandlers = keymap13.of([
7990
8111
  }
7991
8112
  ]);
7992
8113
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7993
- return EditorView31.updateListener.of((update2) => {
8114
+ return EditorView32.updateListener.of((update2) => {
7994
8115
  update2.transactions.forEach((transaction) => {
7995
8116
  for (const effect of transaction.effects) {
7996
8117
  if (effect.is(navigatePreviousEffect)) {
@@ -8075,7 +8196,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
8075
8196
  });
8076
8197
  });
8077
8198
  };
8078
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin22.fromClass(class {
8199
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
8079
8200
  update(update2) {
8080
8201
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
8081
8202
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -8120,7 +8241,7 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.def
8120
8241
  }
8121
8242
  return {
8122
8243
  from: 0,
8123
- decorations: Decoration16.none
8244
+ decorations: Decoration18.none
8124
8245
  };
8125
8246
  }
8126
8247
  }
@@ -8131,12 +8252,7 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.def
8131
8252
  log11("document reset", {
8132
8253
  from,
8133
8254
  to: state.doc.length
8134
- }, {
8135
- F: __dxlog_file17,
8136
- L: 406,
8137
- S: void 0,
8138
- C: (f, a) => f(...a)
8139
- });
8255
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 298, S: void 0 });
8140
8256
  return buildDecorations4(state, {
8141
8257
  from: 0,
8142
8258
  to: state.doc.length
@@ -8165,8 +8281,8 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.def
8165
8281
  };
8166
8282
  },
8167
8283
  provide: (field) => [
8168
- EditorView31.decorations.from(field, (v) => v.decorations),
8169
- EditorView31.atomicRanges.of((view) => view.state.field(field).decorations || Decoration16.none)
8284
+ EditorView32.decorations.from(field, (v) => v.decorations),
8285
+ EditorView32.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
8170
8286
  ]
8171
8287
  });
8172
8288
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -8177,7 +8293,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8177
8293
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
8178
8294
  return {
8179
8295
  from: range.from,
8180
- decorations: Decoration16.none
8296
+ decorations: Decoration18.none
8181
8297
  };
8182
8298
  }
8183
8299
  let last = range.from;
@@ -8213,7 +8329,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8213
8329
  };
8214
8330
  const widget = factory ? factory(props) ?? void 0 : Component ? new PlaceholderWidget2(widgetId, Component, props, notifier) : void 0;
8215
8331
  if (widget) {
8216
- builder.add(nodeRange.from, nodeRange.to, Decoration16.replace({
8332
+ builder.add(nodeRange.from, nodeRange.to, Decoration18.replace({
8217
8333
  widget,
8218
8334
  block,
8219
8335
  atomic: true,
@@ -8225,12 +8341,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8225
8341
  }
8226
8342
  }
8227
8343
  } catch (err) {
8228
- log11.catch(err, void 0, {
8229
- F: __dxlog_file17,
8230
- L: 515,
8231
- S: void 0,
8232
- C: (f, a) => f(...a)
8233
- });
8344
+ log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 401, S: void 0 });
8234
8345
  }
8235
8346
  return false;
8236
8347
  }
@@ -8277,7 +8388,7 @@ var buildDecorations4 = (state, range, registry, notifier) => {
8277
8388
  };
8278
8389
  const widget = def.factory ? def.factory(mergedProps) ?? void 0 : def.Component ? new PlaceholderWidget2(widgetId, def.Component, mergedProps, notifier, true) : void 0;
8279
8390
  if (widget) {
8280
- builder.add(absoluteFrom, range.to, Decoration16.replace({
8391
+ builder.add(absoluteFrom, range.to, Decoration18.replace({
8281
8392
  widget,
8282
8393
  block: def.block,
8283
8394
  atomic: true,
@@ -8309,15 +8420,7 @@ var PlaceholderWidget2 = class extends WidgetType10 {
8309
8420
  #view;
8310
8421
  constructor(id, Component, props, notifier, streaming) {
8311
8422
  super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier, this.streaming = streaming;
8312
- invariant7(id, void 0, {
8313
- F: __dxlog_file17,
8314
- L: 618,
8315
- S: this,
8316
- A: [
8317
- "id",
8318
- ""
8319
- ]
8320
- });
8423
+ invariant7(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 488, S: this, A: ["id", ""] });
8321
8424
  }
8322
8425
  get root() {
8323
8426
  return this.#root;
@@ -8364,71 +8467,10 @@ var PlaceholderWidget2 = class extends WidgetType10 {
8364
8467
  this.#view = void 0;
8365
8468
  }
8366
8469
  };
8367
-
8368
- // src/extensions/typewriter.ts
8369
- import { keymap as keymap14 } from "@codemirror/view";
8370
- var defaultItems = [
8371
- "hello world!",
8372
- "this is a test.",
8373
- "this is [DXOS](https://dxos.org)"
8374
- ];
8375
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
8376
- let t;
8377
- let idx = 0;
8378
- return [
8379
- keymap14.of([
8380
- {
8381
- // Reset.
8382
- key: "alt-meta-'",
8383
- run: () => {
8384
- clearTimeout(t);
8385
- idx = 0;
8386
- return true;
8387
- }
8388
- },
8389
- {
8390
- // Next prompt.
8391
- // TODO(burdon): Press 1-9 to select prompt?
8392
- key: "Shift-Meta-'",
8393
- run: (view) => {
8394
- clearTimeout(t);
8395
- const text = items[idx++];
8396
- if (idx === items?.length) {
8397
- idx = 0;
8398
- }
8399
- let i = 0;
8400
- const insert = (d = 0) => {
8401
- t = setTimeout(() => {
8402
- const pos = view.state.selection.main.head;
8403
- view.dispatch({
8404
- changes: {
8405
- from: pos,
8406
- insert: text[i++]
8407
- },
8408
- selection: {
8409
- anchor: pos + 1
8410
- }
8411
- });
8412
- if (i < text.length) {
8413
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
8414
- }
8415
- }, d);
8416
- };
8417
- insert();
8418
- return true;
8419
- }
8420
- }
8421
- ])
8422
- ];
8423
- };
8424
8470
  export {
8425
8471
  Cursor,
8426
- EditorInputMode,
8427
- EditorInputModes,
8428
8472
  EditorState4 as EditorState,
8429
- EditorView32 as EditorView,
8430
- EditorViewMode,
8431
- EditorViewModes,
8473
+ EditorView33 as EditorView,
8432
8474
  Inline,
8433
8475
  InputModeExtensions,
8434
8476
  List,
@@ -8557,6 +8599,7 @@ export {
8557
8599
  setSelection,
8558
8600
  setStyle,
8559
8601
  singleValueFacet,
8602
+ snippets2 as snippets,
8560
8603
  staticCompletion,
8561
8604
  submit,
8562
8605
  tabbable,
@@ -8576,10 +8619,12 @@ export {
8576
8619
  treeFacet,
8577
8620
  typeahead,
8578
8621
  typewriter,
8579
- wire,
8580
- wireBypass,
8622
+ typewriterBypass,
8623
+ typewriterDrainingEffect,
8581
8624
  wrapWithCatch,
8625
+ xmlBlockDecoration,
8582
8626
  xmlElementLength,
8627
+ xmlFormatting,
8583
8628
  xmlTagContextEffect,
8584
8629
  xmlTagResetEffect,
8585
8630
  xmlTagUpdateEffect,