@dxos/ui-editor 0.8.4-main.8360d9e660 → 0.8.4-main.8baae0fced

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 (165) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +1858 -1051
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/types/index.mjs +26 -6
  7. package/dist/lib/browser/types/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/index.mjs +1857 -1049
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/lib/node-esm/types/index.mjs +27 -6
  12. package/dist/lib/node-esm/types/index.mjs.map +4 -4
  13. package/dist/types/src/defaults.d.ts +4 -10
  14. package/dist/types/src/defaults.d.ts.map +1 -1
  15. package/dist/types/src/extensions/annotations.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 +5 -2
  19. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -1
  20. package/dist/types/src/extensions/autocomplete/typeahead.d.ts.map +1 -1
  21. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  23. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  25. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  27. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +1 -1
  28. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  29. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  30. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  31. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  32. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  33. package/dist/types/src/extensions/factories.d.ts +4 -3
  34. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  35. package/dist/types/src/extensions/factories.test.d.ts +2 -0
  36. package/dist/types/src/extensions/factories.test.d.ts.map +1 -0
  37. package/dist/types/src/extensions/focus.d.ts +1 -1
  38. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  39. package/dist/types/src/extensions/index.d.ts +2 -3
  40. package/dist/types/src/extensions/index.d.ts.map +1 -1
  41. package/dist/types/src/extensions/json.d.ts.map +1 -1
  42. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  43. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -0
  45. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  47. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  49. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  50. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  51. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  52. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  53. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  54. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  55. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  56. package/dist/types/src/extensions/outliner/menu.d.ts.map +1 -1
  57. package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
  58. package/dist/types/src/extensions/outliner/selection.d.ts.map +1 -1
  59. package/dist/types/src/extensions/outliner/tree.d.ts.map +1 -1
  60. package/dist/types/src/extensions/preview/preview.d.ts +2 -0
  61. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  62. package/dist/types/src/extensions/replacer.d.ts.map +1 -1
  63. package/dist/types/src/extensions/scrolling/auto-scroll.d.ts +18 -0
  64. package/dist/types/src/extensions/scrolling/auto-scroll.d.ts.map +1 -0
  65. package/dist/types/src/extensions/scrolling/crawler.d.ts +75 -0
  66. package/dist/types/src/extensions/scrolling/crawler.d.ts.map +1 -0
  67. package/dist/types/src/extensions/scrolling/index.d.ts +5 -0
  68. package/dist/types/src/extensions/scrolling/index.d.ts.map +1 -0
  69. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts +3 -0
  70. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts.map +1 -0
  71. package/dist/types/src/extensions/scrolling/scroller.d.ts +16 -0
  72. package/dist/types/src/extensions/scrolling/scroller.d.ts.map +1 -0
  73. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  74. package/dist/types/src/extensions/snippets.d.ts +10 -0
  75. package/dist/types/src/extensions/snippets.d.ts.map +1 -0
  76. package/dist/types/src/extensions/submit.d.ts.map +1 -1
  77. package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
  78. package/dist/types/src/extensions/tags/fader.d.ts +12 -0
  79. package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
  80. package/dist/types/src/extensions/tags/index.d.ts +4 -1
  81. package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
  82. package/dist/types/src/extensions/tags/typewriter.d.ts +43 -0
  83. package/dist/types/src/extensions/tags/typewriter.d.ts.map +1 -0
  84. package/dist/types/src/extensions/tags/typewriter.test.d.ts +2 -0
  85. package/dist/types/src/extensions/tags/typewriter.test.d.ts.map +1 -0
  86. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts +31 -0
  87. package/dist/types/src/extensions/tags/xml-block-decoration.d.ts.map +1 -0
  88. package/dist/types/src/extensions/tags/xml-formatting.d.ts +24 -0
  89. package/dist/types/src/extensions/tags/xml-formatting.d.ts.map +1 -0
  90. package/dist/types/src/extensions/tags/xml-tags.d.ts +28 -8
  91. package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
  92. package/dist/types/src/extensions/tags/xml-util.d.ts.map +1 -1
  93. package/dist/types/src/index.d.ts +0 -1
  94. package/dist/types/src/index.d.ts.map +1 -1
  95. package/dist/types/src/styles/theme.d.ts +2 -2
  96. package/dist/types/src/styles/theme.d.ts.map +1 -1
  97. package/dist/types/src/types/types.d.ts +4 -4
  98. package/dist/types/src/types/types.d.ts.map +1 -1
  99. package/dist/types/src/util/cursor.d.ts.map +1 -1
  100. package/dist/types/src/util/debug.d.ts.map +1 -1
  101. package/dist/types/src/util/decorations.d.ts.map +1 -1
  102. package/dist/types/src/util/dom.d.ts.map +1 -1
  103. package/dist/types/src/util/facet.d.ts.map +1 -1
  104. package/dist/types/src/util/util.d.ts.map +1 -1
  105. package/dist/types/tsconfig.tsbuildinfo +1 -1
  106. package/package.json +35 -38
  107. package/src/defaults.ts +33 -17
  108. package/src/extensions/autocomplete/placeholder.ts +37 -18
  109. package/src/extensions/automerge/automerge.test.tsx +35 -9
  110. package/src/extensions/automerge/automerge.ts +2 -5
  111. package/src/extensions/comments.ts +0 -1
  112. package/src/extensions/factories.test.ts +88 -0
  113. package/src/extensions/factories.ts +26 -8
  114. package/src/extensions/folding.ts +3 -20
  115. package/src/extensions/index.ts +2 -3
  116. package/src/extensions/markdown/action.ts +0 -1
  117. package/src/extensions/markdown/bundle.ts +23 -9
  118. package/src/extensions/markdown/decorate.ts +11 -9
  119. package/src/extensions/markdown/highlight.ts +4 -10
  120. package/src/extensions/markdown/parser.test.ts +0 -1
  121. package/src/extensions/markdown/styles.ts +37 -4
  122. package/src/extensions/markdown/table.ts +24 -2
  123. package/src/extensions/outliner/outliner.test.ts +0 -1
  124. package/src/extensions/outliner/outliner.ts +1 -2
  125. package/src/extensions/outliner/tree.test.ts +0 -1
  126. package/src/extensions/preview/preview.ts +54 -7
  127. package/src/extensions/scrolling/auto-scroll.ts +244 -0
  128. package/src/extensions/{scroller.ts → scrolling/crawler.ts} +66 -36
  129. package/src/extensions/scrolling/index.ts +8 -0
  130. package/src/extensions/scrolling/scroll-past-end.ts +32 -0
  131. package/src/extensions/scrolling/scroller.ts +27 -0
  132. package/src/extensions/snippets.ts +67 -0
  133. package/src/extensions/tags/extended-markdown.test.ts +120 -2
  134. package/src/extensions/tags/extended-markdown.ts +80 -1
  135. package/src/extensions/tags/fader.ts +195 -0
  136. package/src/extensions/tags/index.ts +4 -1
  137. package/src/extensions/tags/testing/text.md +36 -0
  138. package/src/extensions/tags/testing/text.txt +35 -0
  139. package/src/extensions/tags/typewriter.test.ts +65 -0
  140. package/src/extensions/tags/typewriter.ts +594 -0
  141. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  142. package/src/extensions/tags/xml-formatting.ts +125 -0
  143. package/src/extensions/tags/xml-tags.ts +183 -35
  144. package/src/extensions/tags/xml-util.test.ts +199 -24
  145. package/src/extensions/tags/xml-util.ts +62 -5
  146. package/src/index.ts +0 -1
  147. package/src/styles/theme.ts +46 -31
  148. package/src/types/types.ts +10 -2
  149. package/src/typings.d.ts +8 -0
  150. package/src/util/cursor.ts +0 -1
  151. package/dist/lib/browser/chunk-HL3YF6WC.mjs +0 -22
  152. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +0 -7
  153. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs +0 -24
  154. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +0 -7
  155. package/dist/types/src/extensions/auto-scroll.d.ts +0 -6
  156. package/dist/types/src/extensions/auto-scroll.d.ts.map +0 -1
  157. package/dist/types/src/extensions/scroller.d.ts +0 -66
  158. package/dist/types/src/extensions/scroller.d.ts.map +0 -1
  159. package/dist/types/src/extensions/tags/streamer.d.ts +0 -12
  160. package/dist/types/src/extensions/tags/streamer.d.ts.map +0 -1
  161. package/dist/types/src/extensions/typewriter.d.ts +0 -10
  162. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  163. package/src/extensions/auto-scroll.ts +0 -126
  164. package/src/extensions/tags/streamer.ts +0 -243
  165. package/src/extensions/typewriter.ts +0 -68
@@ -1,26 +1,42 @@
1
- import {
2
- EditorInputMode,
3
- EditorInputModes,
4
- EditorViewMode,
5
- EditorViewModes
6
- } from "./chunk-HL3YF6WC.mjs";
7
-
8
1
  // src/index.ts
9
- import { EditorState as EditorState3 } from "@codemirror/state";
10
- import { EditorView as EditorView30, keymap as keymap15 } from "@codemirror/view";
2
+ import { EditorState as EditorState4 } from "@codemirror/state";
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
 
14
7
  // src/defaults.ts
15
8
  import { mx } from "@dxos/ui-theme";
16
- var editorWidth = "!mx-auto w-full max-w-[min(50rem,100%-4rem)]";
17
- var editorSlots = {
9
+ var editorClassNames = (role) => mx("dx-attention-surface p-0.5 data-[toolbar=disabled]:pt-2 dx-focus-ring-inset", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-h-24" : "dx-container overflow-hidden");
10
+ var documentSlots = {
11
+ content: {
12
+ /**
13
+ * CodeMirror content width.
14
+ * 40rem = 640px. Corresponds to initial plank width (Google docs, Stashpad, etc.)
15
+ * 50rem = 800px. Maximum content width for solo mode.
16
+ * NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).
17
+ */
18
+ className: mx(
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",
24
+ // Wider margin for web (vs. mobile).
25
+ "pointer-fine:max-w-[min(50rem,100%-4rem)] pointer-coarse:max-w-[min(50rem,100%-2rem)]",
26
+ "mx-auto! w-full"
27
+ )
28
+ }
29
+ };
30
+ var compactSlots = {
31
+ content: {
32
+ className: "mx-2!"
33
+ }
34
+ };
35
+ var mobileSlots = {
18
36
  content: {
19
- className: editorWidth
37
+ className: "mx-2!"
20
38
  }
21
39
  };
22
- var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
23
- var stackItemContentEditorClassNames = (role) => mx("dx-attention-surface p-0.5 dx-focus-ring-inset data-[toolbar=disabled]:pt-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-h-24" : "dx-container overflow-hidden");
24
40
 
25
41
  // src/extensions/annotations.ts
26
42
  import { RangeSetBuilder } from "@codemirror/state";
@@ -204,7 +220,7 @@ var singleValueFacet = (defaultValue) => Facet.define({
204
220
  var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
205
221
  var defaultCursorConverter = {
206
222
  toCursor: (position) => position.toString(),
207
- fromCursor: (cursor2) => parseInt(cursor2)
223
+ fromCursor: (cursor) => parseInt(cursor)
208
224
  };
209
225
  var Cursor = class _Cursor {
210
226
  static converter = singleValueFacet(defaultCursorConverter);
@@ -217,9 +233,9 @@ var Cursor = class _Cursor {
217
233
  to
218
234
  ].join(":");
219
235
  };
220
- static getRangeFromCursor = (state, cursor2) => {
236
+ static getRangeFromCursor = (state, cursor) => {
221
237
  const cursorConverter2 = state.facet(_Cursor.converter);
222
- const parts = cursor2.split(":");
238
+ const parts = cursor.split(":");
223
239
  const from = cursorConverter2.fromCursor(parts[0]);
224
240
  const to = cursorConverter2.fromCursor(parts[1]);
225
241
  return from !== void 0 && to !== void 0 ? {
@@ -255,12 +271,7 @@ var wrapWithCatch = (fn, label) => {
255
271
  } catch (err) {
256
272
  log.catch(err, {
257
273
  label
258
- }, {
259
- F: __dxlog_file,
260
- L: 20,
261
- S: void 0,
262
- C: (f, a) => f(...a)
263
- });
274
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 13, S: void 0 });
264
275
  }
265
276
  };
266
277
  };
@@ -286,12 +297,7 @@ var logChanges = (trs) => {
286
297
  if (changes.length) {
287
298
  log("changes", {
288
299
  changes
289
- }, {
290
- F: __dxlog_file,
291
- L: 54,
292
- S: void 0,
293
- C: (f, a) => f(...a)
294
- });
300
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 44, S: void 0 });
295
301
  }
296
302
  };
297
303
 
@@ -357,30 +363,37 @@ var insertAtLineStart = (view, from, insert) => {
357
363
  };
358
364
 
359
365
  // src/extensions/autocomplete/placeholder.ts
360
- var placeholder = ({ content, delay = 3e3 }) => {
366
+ var placeholder = ({ content, delay = 3e3, focusOnly = false }) => {
361
367
  const plugin = ViewPlugin3.fromClass(class {
362
368
  _timeout;
363
369
  _decorations = Decoration3.none;
364
370
  update(update2) {
371
+ if (!update2.docChanged && !update2.selectionSet && !update2.focusChanged) {
372
+ return;
373
+ }
365
374
  if (this._timeout) {
366
375
  window.clearTimeout(this._timeout);
367
376
  this._timeout = void 0;
368
377
  }
378
+ this._decorations = Decoration3.none;
379
+ if (focusOnly && !update2.view.hasFocus) {
380
+ return;
381
+ }
369
382
  const activeLine = update2.view.state.doc.lineAt(update2.view.state.selection.main.head);
370
- const isEmpty = activeLine.text.trim() === "";
371
- if (isEmpty) {
372
- const lineStart = activeLine.from;
373
- this._timeout = setTimeout(() => {
374
- this._decorations = Decoration3.set([
375
- Decoration3.widget({
376
- widget: new PlaceholderWidget(content),
377
- side: 1
378
- }).range(lineStart)
379
- ]);
380
- update2.view.update([]);
381
- }, delay);
383
+ if (activeLine.text.trim() !== "") {
384
+ return;
382
385
  }
383
- 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);
384
397
  }
385
398
  destroy() {
386
399
  if (this._timeout) {
@@ -498,249 +511,10 @@ var typeahead = ({ onComplete } = {}) => {
498
511
  ];
499
512
  };
500
513
 
501
- // src/extensions/auto-scroll.ts
502
- import { EditorView as EditorView5, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
503
- import { addEventListener, combine, throttle } from "@dxos/async";
504
- import { Domino } from "@dxos/ui";
505
-
506
- // src/extensions/scroller.ts
507
- import { StateEffect } from "@codemirror/state";
508
- import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
509
- import { log as log2 } from "@dxos/log";
510
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scroller.ts";
511
- var scrollerLineEffect = StateEffect.define();
512
- var scrollerCrawlEffect = StateEffect.define();
513
- var scrollToLine = (view, options) => {
514
- view.dispatch({
515
- effects: scrollerLineEffect.of(options)
516
- });
517
- };
518
- var scroller = ({ overScroll = 0 } = {}) => {
519
- const scrollPlugin = ViewPlugin5.fromClass(class ScrollerPlugin {
520
- view;
521
- crawler;
522
- constructor(view) {
523
- this.view = view;
524
- this.crawler = createCrawler(this.view);
525
- }
526
- // No-op.
527
- destroy() {
528
- this.crawler.cancel();
529
- }
530
- cancel() {
531
- this.crawler.cancel();
532
- }
533
- crawl(start = false) {
534
- if (start) {
535
- this.crawler.scroll();
536
- } else {
537
- this.crawler.cancel();
538
- }
539
- }
540
- scroll({ line, offset = 0, position, behavior = "instant" }) {
541
- const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
542
- const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
543
- const doc = this.view.state.doc;
544
- let targetScrollTop = scrollHeight - clientHeight + offset;
545
- if (line >= 0 && line <= doc.lines - 1) {
546
- const lineStart = doc.line(line + 1).from;
547
- const coords = this.view.coordsAtPos(lineStart);
548
- if (coords) {
549
- const currentScrollTop = scrollTop;
550
- const maxScrollTop = scrollHeight - clientHeight;
551
- if (position === "end") {
552
- targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
553
- } else {
554
- targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
555
- }
556
- targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
557
- }
558
- }
559
- requestAnimationFrame(() => {
560
- this.view.scrollDOM.scrollTo({
561
- top: targetScrollTop
562
- });
563
- });
564
- }
565
- });
566
- return [
567
- scrollPlugin,
568
- // Listen for effect.s
569
- EditorView4.updateListener.of((update2) => {
570
- update2.transactions.forEach((transaction) => {
571
- try {
572
- const plugin = update2.view.plugin(scrollPlugin);
573
- if (plugin) {
574
- for (const effect of transaction.effects) {
575
- if (effect.is(scrollerCrawlEffect)) {
576
- plugin.crawl(effect.value);
577
- } else if (effect.is(scrollerLineEffect)) {
578
- plugin.scroll(effect.value);
579
- }
580
- }
581
- }
582
- } catch (err) {
583
- log2.catch(err, void 0, {
584
- F: __dxlog_file2,
585
- L: 146,
586
- S: void 0,
587
- C: (f, a) => f(...a)
588
- });
589
- }
590
- });
591
- }),
592
- // Styles.
593
- EditorView4.theme({
594
- ".cm-content": {
595
- paddingBottom: `${overScroll}px`
596
- },
597
- ".cm-scroller": {
598
- overflowAnchor: "none",
599
- paddingBottom: "0"
600
- },
601
- ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
602
- display: "none"
603
- },
604
- ".cm-scroller::-webkit-scrollbar-thumb": {
605
- background: "transparent",
606
- transition: "background 0.15s"
607
- },
608
- "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
609
- background: "var(--color-scrollbar-thumb)"
610
- },
611
- ".cm-scroll-button": {
612
- position: "absolute",
613
- bottom: "0.5rem",
614
- right: "1rem"
615
- }
616
- })
617
- ];
618
- };
619
- function createCrawler(view, k = 0.3, maxStep = 2, targetDelta = 0.5) {
620
- const el = view.scrollDOM;
621
- let currentTop = 0;
622
- let rafId = null;
623
- function frame() {
624
- const targetTop = el.scrollHeight - el.clientHeight;
625
- const delta = targetTop - currentTop;
626
- const absDelta = Math.abs(delta);
627
- if (absDelta < targetDelta) {
628
- el.scrollTop = targetTop;
629
- currentTop = targetTop;
630
- rafId = null;
631
- return;
632
- }
633
- const step = Math.sign(delta) * Math.min(absDelta, Math.max(1, Math.min(absDelta * k, maxStep)));
634
- currentTop += step;
635
- el.scrollTop = currentTop;
636
- rafId = requestAnimationFrame(frame);
637
- }
638
- return {
639
- scroll: () => {
640
- if (rafId === null) {
641
- currentTop = el.scrollTop;
642
- rafId = requestAnimationFrame(frame);
643
- }
644
- },
645
- cancel: () => {
646
- if (rafId !== null) {
647
- cancelAnimationFrame(rafId);
648
- rafId = null;
649
- }
650
- }
651
- };
652
- }
653
-
654
- // src/extensions/auto-scroll.ts
655
- var autoScroll = (_ = {}) => {
656
- let buttonContainer;
657
- let isPinned = true;
658
- const setPinned = (pinned) => {
659
- buttonContainer?.classList.toggle("opacity-0", pinned);
660
- isPinned = pinned;
661
- };
662
- return [
663
- // Update listener for logging when scrolling is needed.
664
- EditorView5.updateListener.of(({ view, heightChanged, state }) => {
665
- if (heightChanged) {
666
- if (isPinned) {
667
- const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
668
- const delta = scrollHeight - scrollTop - clientHeight;
669
- if (delta > 0 && scrollTop > 0) {
670
- setPinned(true);
671
- view.dispatch({
672
- effects: scrollerCrawlEffect.of(true)
673
- });
674
- } else if (delta < 0) {
675
- setPinned(false);
676
- }
677
- } else {
678
- if (state.doc.length === 0) {
679
- setPinned(true);
680
- }
681
- }
682
- }
683
- }),
684
- // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
685
- ViewPlugin6.fromClass(class {
686
- cleanup;
687
- constructor(view) {
688
- this.cleanup = createUserScrollDetector(view.scrollDOM, throttle(() => {
689
- requestAnimationFrame(() => {
690
- const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
691
- const delta = scrollHeight - scrollTop - clientHeight;
692
- const pinned = delta === 0;
693
- setPinned(pinned);
694
- if (!pinned) {
695
- view.dispatch({
696
- effects: scrollerCrawlEffect.of(false)
697
- });
698
- }
699
- });
700
- }, 500));
701
- }
702
- destroy() {
703
- this.cleanup();
704
- }
705
- }),
706
- // Scroll button.
707
- ViewPlugin6.fromClass(class {
708
- constructor(view) {
709
- const icon = Domino.of("dx-icon").attributes({
710
- icon: "ph--arrow-down--regular"
711
- });
712
- const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
713
- "data-density": "fine"
714
- }).children(icon).on("click", () => {
715
- setPinned(true);
716
- view.dispatch({
717
- effects: scrollerLineEffect.of({
718
- line: -1,
719
- position: "end",
720
- behavior: "smooth"
721
- })
722
- });
723
- });
724
- buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").children(button).root;
725
- view.scrollDOM.parentElement.appendChild(buttonContainer);
726
- }
727
- })
728
- ];
729
- };
730
- function createUserScrollDetector(element, onUserScroll) {
731
- return combine(addEventListener(element, "wheel", () => onUserScroll(), {
732
- passive: true
733
- }), addEventListener(element, "pointerdown", (event) => {
734
- if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
735
- onUserScroll();
736
- }
737
- }));
738
- }
739
-
740
514
  // src/extensions/automerge/automerge.ts
741
515
  import { next as A3 } from "@automerge/automerge";
742
516
  import { StateField, Transaction as Transaction2 } from "@codemirror/state";
743
- import { EditorView as EditorView6, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
517
+ import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
744
518
  import { DocAccessor } from "@dxos/echo-db";
745
519
 
746
520
  // src/extensions/state.ts
@@ -749,42 +523,32 @@ var initialSync = Transaction.userEvent.of("initial.sync");
749
523
 
750
524
  // src/extensions/automerge/cursor.ts
751
525
  import { fromCursor, toCursor } from "@dxos/echo-db";
752
- import { log as log3 } from "@dxos/log";
753
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
526
+ import { log as log2 } from "@dxos/log";
527
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
754
528
  var cursorConverter = (accessor) => ({
755
529
  toCursor: (pos, assoc) => {
756
530
  try {
757
531
  return toCursor(accessor, pos, assoc);
758
532
  } catch (err) {
759
- log3.catch(err, void 0, {
760
- F: __dxlog_file3,
761
- L: 15,
762
- S: void 0,
763
- C: (f, a) => f(...a)
764
- });
533
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 11, S: void 0 });
765
534
  return "";
766
535
  }
767
536
  },
768
- fromCursor: (cursor2) => {
537
+ fromCursor: (cursor) => {
769
538
  try {
770
- return fromCursor(accessor, cursor2);
539
+ return fromCursor(accessor, cursor);
771
540
  } catch (err) {
772
- log3.catch(err, void 0, {
773
- F: __dxlog_file3,
774
- L: 24,
775
- S: void 0,
776
- C: (f, a) => f(...a)
777
- });
541
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 19, S: void 0 });
778
542
  return 0;
779
543
  }
780
544
  }
781
545
  });
782
546
 
783
547
  // src/extensions/automerge/defs.ts
784
- import { Annotation, StateEffect as StateEffect2 } from "@codemirror/state";
548
+ import { Annotation, StateEffect } from "@codemirror/state";
785
549
  var getPath = (state, field) => state.field(field).path;
786
550
  var getLastHeads = (state, field) => state.field(field).lastHeads;
787
- var updateHeadsEffect = StateEffect2.define({});
551
+ var updateHeadsEffect = StateEffect.define({});
788
552
  var updateHeads = (newHeads) => updateHeadsEffect.of({
789
553
  newHeads
790
554
  });
@@ -795,7 +559,7 @@ var isReconcile = (tr) => {
795
559
 
796
560
  // src/extensions/automerge/sync.ts
797
561
  import { next as A2 } from "@automerge/automerge";
798
- import { log as log4 } from "@dxos/log";
562
+ import { log as log3 } from "@dxos/log";
799
563
 
800
564
  // src/extensions/automerge/update-automerge.ts
801
565
  import { next as A } from "@automerge/automerge";
@@ -936,7 +700,7 @@ var charPath = (textPath, candidatePath) => {
936
700
  };
937
701
 
938
702
  // src/extensions/automerge/sync.ts
939
- var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/sync.ts";
703
+ var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/sync.ts";
940
704
  var Syncer = class {
941
705
  _handle;
942
706
  _state;
@@ -959,12 +723,7 @@ var Syncer = class {
959
723
  this._pending = false;
960
724
  }
961
725
  onEditorChange(view) {
962
- log4("onEditorChange", void 0, {
963
- F: __dxlog_file4,
964
- L: 45,
965
- S: this,
966
- C: (f, a) => f(...a)
967
- });
726
+ log3("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 35, S: this });
968
727
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
969
728
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
970
729
  if (newHeads) {
@@ -975,12 +734,7 @@ var Syncer = class {
975
734
  }
976
735
  }
977
736
  onAutomergeChange(view) {
978
- log4("onAutomergeChange", void 0, {
979
- F: __dxlog_file4,
980
- L: 60,
981
- S: this,
982
- C: (f, a) => f(...a)
983
- });
737
+ log3("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 47, S: this });
984
738
  const oldHeads = getLastHeads(view.state, this._state);
985
739
  const newHeads = A2.getHeads(this._handle.doc());
986
740
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1033,7 +787,7 @@ var automerge = (accessor) => {
1033
787
  // Track heads.
1034
788
  syncState,
1035
789
  // Reconcile external updates.
1036
- ViewPlugin7.fromClass(class {
790
+ ViewPlugin5.fromClass(class {
1037
791
  _view;
1038
792
  constructor(_view) {
1039
793
  this._view = _view;
@@ -1042,14 +796,16 @@ var automerge = (accessor) => {
1042
796
  const value = DocAccessor.getValue(accessor);
1043
797
  const current = this._view.state.doc.toString();
1044
798
  if (value !== current) {
1045
- console.warn("ENABLING INITIAL SYNC -- THIS MAY BE A REGRESSION");
1046
799
  this._view.dispatch({
1047
800
  changes: {
1048
801
  from: 0,
1049
802
  to: this._view.state.doc.length,
1050
803
  insert: value
1051
804
  },
1052
- annotations: initialSync
805
+ annotations: [
806
+ initialSync,
807
+ reconcileAnnotation.of(true)
808
+ ]
1053
809
  });
1054
810
  }
1055
811
  });
@@ -1062,7 +818,7 @@ var automerge = (accessor) => {
1062
818
  };
1063
819
  }),
1064
820
  // Reconcile local updates.
1065
- EditorView6.updateListener.of(({ view, changes, transactions }) => {
821
+ EditorView4.updateListener.of(({ view, changes, transactions }) => {
1066
822
  if (!changes.empty) {
1067
823
  const isInitialSync = transactions.some((tr) => tr.annotation(Transaction2.userEvent) === initialSync.value);
1068
824
  if (!isInitialSync) {
@@ -1075,10 +831,10 @@ var automerge = (accessor) => {
1075
831
 
1076
832
  // src/extensions/awareness/awareness.ts
1077
833
  import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1078
- import { Decoration as Decoration5, EditorView as EditorView7, ViewPlugin as ViewPlugin8, WidgetType as WidgetType3 } from "@codemirror/view";
834
+ import { Decoration as Decoration5, EditorView as EditorView5, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
1079
835
  import { Event } from "@dxos/async";
1080
836
  import { Context } from "@dxos/context";
1081
- var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness.ts";
837
+ var __dxlog_file4 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness.ts";
1082
838
  var dummyProvider = {
1083
839
  remoteStateChange: new Event(),
1084
840
  open: () => {
@@ -1094,17 +850,14 @@ var RemoteSelectionChangedAnnotation = Annotation2.define();
1094
850
  var awareness = (provider = dummyProvider) => {
1095
851
  return [
1096
852
  awarenessProvider.of(provider),
1097
- ViewPlugin8.fromClass(RemoteSelectionsDecorator, {
853
+ ViewPlugin6.fromClass(RemoteSelectionsDecorator, {
1098
854
  decorations: (value) => value.decorations
1099
855
  }),
1100
856
  styles
1101
857
  ];
1102
858
  };
1103
859
  var RemoteSelectionsDecorator = class {
1104
- _ctx = new Context(void 0, {
1105
- F: __dxlog_file5,
1106
- L: 80
1107
- });
860
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 33 });
1108
861
  _cursorConverter;
1109
862
  _provider;
1110
863
  _lastAnchor;
@@ -1253,7 +1006,7 @@ var RemoteCaretWidget = class extends WidgetType3 {
1253
1006
  return true;
1254
1007
  }
1255
1008
  };
1256
- var styles = EditorView7.theme({
1009
+ var styles = EditorView5.theme({
1257
1010
  ".cm-collab-selection": {},
1258
1011
  ".cm-collab-selectionLine": {
1259
1012
  padding: 0,
@@ -1315,8 +1068,8 @@ var styles = EditorView7.theme({
1315
1068
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
1316
1069
  import { Context as Context2 } from "@dxos/context";
1317
1070
  import { invariant } from "@dxos/invariant";
1318
- import { log as log5 } from "@dxos/log";
1319
- var __dxlog_file6 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness-provider.ts";
1071
+ import { log as log4 } from "@dxos/log";
1072
+ var __dxlog_file5 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/awareness/awareness-provider.ts";
1320
1073
  var DEBOUNCE_INTERVAL = 100;
1321
1074
  var SpaceAwarenessProvider = class {
1322
1075
  _remoteStates = /* @__PURE__ */ new Map();
@@ -1335,10 +1088,7 @@ var SpaceAwarenessProvider = class {
1335
1088
  this._info = info;
1336
1089
  }
1337
1090
  open() {
1338
- this._ctx = new Context2(void 0, {
1339
- F: __dxlog_file6,
1340
- L: 57
1341
- });
1091
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 28 });
1342
1092
  this._postTask = new DeferredTask(this._ctx, async () => {
1343
1093
  if (this._localState) {
1344
1094
  await this._messenger.postMessage(this._channel, {
@@ -1363,14 +1113,9 @@ var SpaceAwarenessProvider = class {
1363
1113
  void this._messenger.postMessage(this._channel, {
1364
1114
  kind: "query"
1365
1115
  }).catch((err) => {
1366
- log5.debug("failed to query awareness", {
1116
+ log4.debug("failed to query awareness", {
1367
1117
  err
1368
- }, {
1369
- F: __dxlog_file6,
1370
- L: 91,
1371
- S: this,
1372
- C: (f, a) => f(...a)
1373
- });
1118
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 57, S: this });
1374
1119
  });
1375
1120
  }
1376
1121
  close() {
@@ -1382,15 +1127,7 @@ var SpaceAwarenessProvider = class {
1382
1127
  return Array.from(this._remoteStates.values());
1383
1128
  }
1384
1129
  update(position) {
1385
- invariant(this._postTask, void 0, {
1386
- F: __dxlog_file6,
1387
- L: 106,
1388
- S: this,
1389
- A: [
1390
- "this._postTask",
1391
- ""
1392
- ]
1393
- });
1130
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 71, S: this, A: ["this._postTask", ""] });
1394
1131
  this._localState = {
1395
1132
  peerId: this._peerId,
1396
1133
  position,
@@ -1399,38 +1136,22 @@ var SpaceAwarenessProvider = class {
1399
1136
  this._postTask.schedule();
1400
1137
  }
1401
1138
  _handleQueryMessage() {
1402
- invariant(this._postTask, void 0, {
1403
- F: __dxlog_file6,
1404
- L: 117,
1405
- S: this,
1406
- A: [
1407
- "this._postTask",
1408
- ""
1409
- ]
1410
- });
1139
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 80, S: this, A: ["this._postTask", ""] });
1411
1140
  this._postTask.schedule();
1412
1141
  }
1413
1142
  _handlePostMessage(message) {
1414
- invariant(message.kind === "post", void 0, {
1415
- F: __dxlog_file6,
1416
- L: 122,
1417
- S: this,
1418
- A: [
1419
- "message.kind === 'post'",
1420
- ""
1421
- ]
1422
- });
1143
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1423
1144
  this._remoteStates.set(message.state.peerId, message.state);
1424
1145
  this.remoteStateChange.emit();
1425
1146
  }
1426
1147
  };
1427
1148
 
1428
1149
  // src/extensions/blast.ts
1429
- import { EditorView as EditorView8, keymap as keymap3 } from "@codemirror/view";
1150
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1430
1151
  import defaultsDeep from "lodash.defaultsdeep";
1431
- import { throttle as throttle2 } from "@dxos/async";
1152
+ import { throttle } from "@dxos/async";
1432
1153
  import { invariant as invariant2 } from "@dxos/invariant";
1433
- var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/blast.ts";
1154
+ var __dxlog_file6 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/blast.ts";
1434
1155
  var defaultOptions = {
1435
1156
  effect: 2,
1436
1157
  maxParticles: 200,
@@ -1475,7 +1196,7 @@ var blast = (options = defaultOptions) => {
1475
1196
  };
1476
1197
  return [
1477
1198
  // Cursor moved.
1478
- EditorView8.updateListener.of((update2) => {
1199
+ EditorView6.updateListener.of((update2) => {
1479
1200
  if (blaster?.node !== update2.view.scrollDOM) {
1480
1201
  if (blaster) {
1481
1202
  blaster.destroy();
@@ -1548,15 +1269,7 @@ var Blaster = class {
1548
1269
  return this._node;
1549
1270
  }
1550
1271
  initialize() {
1551
- invariant2(!this._canvas && !this._ctx, void 0, {
1552
- F: __dxlog_file7,
1553
- L: 142,
1554
- S: this,
1555
- A: [
1556
- "!this._canvas && !this._ctx",
1557
- ""
1558
- ]
1559
- });
1272
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1560
1273
  this._canvas = document.createElement("canvas");
1561
1274
  this._canvas.id = "code-blast-canvas";
1562
1275
  this._canvas.style.position = "absolute";
@@ -1585,15 +1298,7 @@ var Blaster = class {
1585
1298
  }
1586
1299
  }
1587
1300
  start() {
1588
- invariant2(this._canvas && this._ctx, void 0, {
1589
- F: __dxlog_file7,
1590
- L: 181,
1591
- S: this,
1592
- A: [
1593
- "this._canvas && this._ctx",
1594
- ""
1595
- ]
1596
- });
1301
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1597
1302
  this._running = true;
1598
1303
  this.loop();
1599
1304
  }
@@ -1620,11 +1325,11 @@ var Blaster = class {
1620
1325
  this.drawParticles();
1621
1326
  requestAnimationFrame(this.loop.bind(this));
1622
1327
  }
1623
- shake = throttle2(({ time }) => {
1328
+ shake = throttle(({ time }) => {
1624
1329
  this._shakeTime = this._shakeTimeMax || time;
1625
1330
  this._shakeTimeMax = time;
1626
1331
  }, 100);
1627
- spawn = throttle2(({ element, point }) => {
1332
+ spawn = throttle(({ element, point }) => {
1628
1333
  const color = getRGBComponents(element, this._options.color);
1629
1334
  const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
1630
1335
  const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
@@ -1733,9 +1438,9 @@ var random = (min, max) => {
1733
1438
 
1734
1439
  // src/extensions/blocks.ts
1735
1440
  import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1736
- import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin9 } from "@codemirror/view";
1441
+ import { Decoration as Decoration6, EditorView as EditorView7, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
1737
1442
  import { mx as mx2 } from "@dxos/ui-theme";
1738
- var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1443
+ var paragraphBlockPlugin = ViewPlugin7.fromClass(class {
1739
1444
  decorations;
1740
1445
  constructor(view) {
1741
1446
  this.decorations = this.build(view);
@@ -1794,7 +1499,7 @@ var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1794
1499
  });
1795
1500
  var blocks = () => [
1796
1501
  paragraphBlockPlugin,
1797
- EditorView9.baseTheme({
1502
+ EditorView7.baseTheme({
1798
1503
  ".cm-line.block-line": {
1799
1504
  paddingLeft: "0.75rem",
1800
1505
  paddingRight: "0.75rem",
@@ -1828,13 +1533,13 @@ var blocks = () => [
1828
1533
  ];
1829
1534
 
1830
1535
  // src/extensions/bookmarks.ts
1831
- import { Prec as Prec3, StateEffect as StateEffect3, StateField as StateField2 } from "@codemirror/state";
1536
+ import { Prec as Prec3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1832
1537
  import { keymap as keymap4 } from "@codemirror/view";
1833
- import { log as log6 } from "@dxos/log";
1834
- var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1835
- var addBookmark = StateEffect3.define();
1836
- var removeBookmark = StateEffect3.define();
1837
- var clearBookmarks = StateEffect3.define();
1538
+ import { log as log5 } from "@dxos/log";
1539
+ var __dxlog_file7 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1540
+ var addBookmark = StateEffect2.define();
1541
+ var removeBookmark = StateEffect2.define();
1542
+ var clearBookmarks = StateEffect2.define();
1838
1543
  var bookmarks = () => {
1839
1544
  return [
1840
1545
  bookmarksField,
@@ -1843,12 +1548,7 @@ var bookmarks = () => {
1843
1548
  key: "Mod-ArrowUp",
1844
1549
  run: (view) => {
1845
1550
  const bookmarks2 = view.state.field(bookmarksField);
1846
- log6("up", bookmarks2, {
1847
- F: __dxlog_file8,
1848
- L: 29,
1849
- S: void 0,
1850
- C: (f, a) => f(...a)
1851
- });
1551
+ log5("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 18, S: void 0 });
1852
1552
  return true;
1853
1553
  }
1854
1554
  },
@@ -1856,12 +1556,7 @@ var bookmarks = () => {
1856
1556
  key: "Mod-ArrowDown",
1857
1557
  run: (view) => {
1858
1558
  const bookmarks2 = view.state.field(bookmarksField);
1859
- log6("down", bookmarks2, {
1860
- F: __dxlog_file8,
1861
- L: 37,
1862
- S: void 0,
1863
- C: (f, a) => f(...a)
1864
- });
1559
+ log5("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 26, S: void 0 });
1865
1560
  return true;
1866
1561
  }
1867
1562
  }
@@ -1898,27 +1593,27 @@ var bookmarksField = StateField2.define({
1898
1593
 
1899
1594
  // src/extensions/comments.ts
1900
1595
  import { invertedEffects } from "@codemirror/commands";
1901
- import { StateEffect as StateEffect4, StateField as StateField3 } from "@codemirror/state";
1902
- import { Decoration as Decoration7, EditorView as EditorView11, ViewPlugin as ViewPlugin10, hoverTooltip, keymap as keymap6 } from "@codemirror/view";
1596
+ import { StateEffect as StateEffect3, StateField as StateField3 } from "@codemirror/state";
1597
+ import { Decoration as Decoration7, EditorView as EditorView9, ViewPlugin as ViewPlugin8, hoverTooltip, keymap as keymap6 } from "@codemirror/view";
1903
1598
  import sortBy from "lodash.sortby";
1904
1599
  import { debounce as debounce2 } from "@dxos/async";
1905
- import { log as log7 } from "@dxos/log";
1600
+ import { log as log6 } from "@dxos/log";
1906
1601
  import { isNonNullable } from "@dxos/util";
1907
1602
 
1908
1603
  // src/extensions/selection.ts
1909
1604
  import { Transaction as Transaction3 } from "@codemirror/state";
1910
- import { EditorView as EditorView10, keymap as keymap5 } from "@codemirror/view";
1605
+ import { EditorView as EditorView8, keymap as keymap5 } from "@codemirror/view";
1911
1606
  import { debounce } from "@dxos/async";
1912
1607
  import { invariant as invariant3 } from "@dxos/invariant";
1913
1608
  import { isTruthy } from "@dxos/util";
1914
- var __dxlog_file9 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/selection.ts";
1609
+ var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/selection.ts";
1915
1610
  var documentId = singleValueFacet();
1916
1611
  var stateRestoreAnnotation = "org.dxos.cm.state-restore";
1917
1612
  var createEditorStateTransaction = ({ scrollTo, selection }) => {
1918
1613
  return {
1919
1614
  selection,
1920
1615
  scrollIntoView: !scrollTo,
1921
- effects: scrollTo ? EditorView10.scrollIntoView(scrollTo, {
1616
+ effects: scrollTo ? EditorView8.scrollIntoView(scrollTo, {
1922
1617
  yMargin: 96
1923
1618
  }) : void 0,
1924
1619
  annotations: Transaction3.userEvent.of(stateRestoreAnnotation)
@@ -1926,28 +1621,12 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1926
1621
  };
1927
1622
  var createEditorStateStore = (keyPrefix) => ({
1928
1623
  getState: (id) => {
1929
- invariant3(id, void 0, {
1930
- F: __dxlog_file9,
1931
- L: 47,
1932
- S: void 0,
1933
- A: [
1934
- "id",
1935
- ""
1936
- ]
1937
- });
1624
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0, A: ["id", ""] });
1938
1625
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
1939
1626
  return state ? JSON.parse(state) : void 0;
1940
1627
  },
1941
1628
  setState: (id, state) => {
1942
- invariant3(id, void 0, {
1943
- F: __dxlog_file9,
1944
- L: 53,
1945
- S: void 0,
1946
- A: [
1947
- "id",
1948
- ""
1949
- ]
1950
- });
1629
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 31, S: void 0, A: ["id", ""] });
1951
1630
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
1952
1631
  }
1953
1632
  });
@@ -1960,7 +1639,7 @@ var selectionState = ({ getState, setState } = {}) => {
1960
1639
  // setStateDebounced(id, {});
1961
1640
  // },
1962
1641
  // }),
1963
- EditorView10.updateListener.of(({ view, transactions }) => {
1642
+ EditorView8.updateListener.of(({ view, transactions }) => {
1964
1643
  const id = view.state.facet(documentId);
1965
1644
  if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
1966
1645
  return;
@@ -1999,10 +1678,10 @@ var selectionState = ({ getState, setState } = {}) => {
1999
1678
  };
2000
1679
 
2001
1680
  // src/extensions/comments.ts
2002
- var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
2003
- var setComments = StateEffect4.define();
2004
- var setSelection = StateEffect4.define();
2005
- var setCommentState = StateEffect4.define();
1681
+ var __dxlog_file9 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
1682
+ var setComments = StateEffect3.define();
1683
+ var setSelection = StateEffect3.define();
1684
+ var setCommentState = StateEffect3.define();
2006
1685
  var commentsState = StateField3.define({
2007
1686
  create: (state) => ({
2008
1687
  id: state.facet(documentId),
@@ -2041,7 +1720,7 @@ var commentsState = StateField3.define({
2041
1720
  return value;
2042
1721
  }
2043
1722
  });
2044
- var styles2 = EditorView11.theme({
1723
+ var styles2 = EditorView9.theme({
2045
1724
  ".cm-comment, .cm-comment-current": {
2046
1725
  padding: "3px 0",
2047
1726
  color: "var(--color-cm-comment-text)",
@@ -2062,19 +1741,14 @@ var createCommentMark = (id, isCurrent) => Decoration7.mark({
2062
1741
  "data-comment-id": id
2063
1742
  }
2064
1743
  });
2065
- var commentsDecorations = EditorView11.decorations.compute([
1744
+ var commentsDecorations = EditorView9.decorations.compute([
2066
1745
  commentsState
2067
1746
  ], (state) => {
2068
1747
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
2069
1748
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2070
1749
  const range = comment.range;
2071
1750
  if (!range) {
2072
- log7.warn("Invalid range:", range, {
2073
- F: __dxlog_file10,
2074
- L: 140,
2075
- S: void 0,
2076
- C: (f, a) => f(...a)
2077
- });
1751
+ log6.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 93, S: void 0 });
2078
1752
  return void 0;
2079
1753
  } else if (range.from === range.to) {
2080
1754
  return void 0;
@@ -2084,8 +1758,8 @@ var commentsDecorations = EditorView11.decorations.compute([
2084
1758
  }).filter(isNonNullable);
2085
1759
  return Decoration7.set(decorations2);
2086
1760
  });
2087
- var commentClickedEffect = StateEffect4.define();
2088
- var handleCommentClick = EditorView11.domEventHandlers({
1761
+ var commentClickedEffect = StateEffect3.define();
1762
+ var handleCommentClick = EditorView9.domEventHandlers({
2089
1763
  click: (event, view) => {
2090
1764
  let target = event.target;
2091
1765
  const editorRoot = view.dom;
@@ -2124,7 +1798,7 @@ var trackPastedComments = (onUpdate) => {
2124
1798
  }
2125
1799
  };
2126
1800
  return [
2127
- EditorView11.domEventHandlers({
1801
+ EditorView9.domEventHandlers({
2128
1802
  cut: handleTrack,
2129
1803
  copy: handleTrack
2130
1804
  }),
@@ -2146,7 +1820,7 @@ var trackPastedComments = (onUpdate) => {
2146
1820
  return effects;
2147
1821
  }),
2148
1822
  // Handle paste or the undo of comment deletion.
2149
- EditorView11.updateListener.of((update2) => {
1823
+ EditorView9.updateListener.of((update2) => {
2150
1824
  const restore = [];
2151
1825
  for (let i = 0; i < update2.transactions.length; i++) {
2152
1826
  const tr = update2.transactions[i];
@@ -2187,10 +1861,10 @@ var trackPastedComments = (onUpdate) => {
2187
1861
  const { comments: comments2 } = update2.startState.field(commentsState);
2188
1862
  const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
2189
1863
  if (!exists) {
2190
- const cursor2 = Cursor.getCursorFromRange(update2.state, comment);
1864
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
2191
1865
  onUpdate({
2192
1866
  id: comment.id,
2193
- cursor: cursor2
1867
+ cursor
2194
1868
  });
2195
1869
  }
2196
1870
  }
@@ -2202,7 +1876,7 @@ var mapTrackedComment = (comment, changes) => ({
2202
1876
  from: changes.mapPos(comment.from, 1),
2203
1877
  to: changes.mapPos(comment.to, 1)
2204
1878
  });
2205
- var restoreCommentEffect = StateEffect4.define({
1879
+ var restoreCommentEffect = StateEffect3.define({
2206
1880
  map: mapTrackedComment
2207
1881
  });
2208
1882
  var createComment = (view) => {
@@ -2219,13 +1893,13 @@ var createComment = (view) => {
2219
1893
  }
2220
1894
  });
2221
1895
  }
2222
- const cursor2 = Cursor.getCursorFromRange(view.state, {
1896
+ const cursor = Cursor.getCursorFromRange(view.state, {
2223
1897
  from,
2224
1898
  to
2225
1899
  });
2226
- if (cursor2) {
1900
+ if (cursor) {
2227
1901
  options.onCreate?.({
2228
- cursor: cursor2,
1902
+ cursor,
2229
1903
  from,
2230
1904
  location: view.coordsAtPos(from)
2231
1905
  });
@@ -2288,7 +1962,7 @@ var comments = (options = {}) => {
2288
1962
  //
2289
1963
  // Track deleted ranges and update ranges for decorations.
2290
1964
  //
2291
- EditorView11.updateListener.of(({ view, state, changes }) => {
1965
+ EditorView9.updateListener.of(({ view, state, changes }) => {
2292
1966
  let mod = false;
2293
1967
  const { comments: comments2, ...value } = state.field(commentsState);
2294
1968
  changes.iterChanges((from, to, from2, to2) => {
@@ -2320,7 +1994,7 @@ var comments = (options = {}) => {
2320
1994
  //
2321
1995
  // Track selection/proximity.
2322
1996
  //
2323
- EditorView11.updateListener.of(({ view, state }) => {
1997
+ EditorView9.updateListener.of(({ view, state }) => {
2324
1998
  let min = Infinity;
2325
1999
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
2326
2000
  const { head } = state.selection.main;
@@ -2374,7 +2048,7 @@ var scrollThreadIntoView = (view, id, center = true) => {
2374
2048
  anchor: range.from
2375
2049
  } : void 0,
2376
2050
  effects: [
2377
- needsScroll ? EditorView11.scrollIntoView(range.from, center ? {
2051
+ needsScroll ? EditorView9.scrollIntoView(range.from, center ? {
2378
2052
  y: "center"
2379
2053
  } : void 0) : [],
2380
2054
  needsSelectionUpdate ? setSelection.of({
@@ -2405,7 +2079,7 @@ var ExternalCommentSync = class {
2405
2079
  this.unsubscribe();
2406
2080
  };
2407
2081
  };
2408
- var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin10.fromClass(class {
2082
+ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin8.fromClass(class {
2409
2083
  constructor(view) {
2410
2084
  return new ExternalCommentSync(view, id, subscribe, getComments);
2411
2085
  }
@@ -2425,12 +2099,12 @@ var debugNodeLogger = (log12 = console.log) => {
2425
2099
  };
2426
2100
 
2427
2101
  // src/extensions/dnd.ts
2428
- import { EditorView as EditorView12, dropCursor } from "@codemirror/view";
2102
+ import { EditorView as EditorView10, dropCursor } from "@codemirror/view";
2429
2103
  var dropFile = (options = {}) => {
2430
2104
  return [
2431
2105
  styles3,
2432
2106
  dropCursor(),
2433
- EditorView12.domEventHandlers({
2107
+ EditorView10.domEventHandlers({
2434
2108
  drop: (event, view) => {
2435
2109
  event.preventDefault();
2436
2110
  const files = event.dataTransfer?.files;
@@ -2449,7 +2123,7 @@ var dropFile = (options = {}) => {
2449
2123
  })
2450
2124
  ];
2451
2125
  };
2452
- var styles3 = EditorView12.theme({
2126
+ var styles3 = EditorView10.theme({
2453
2127
  ".cm-dropCursor": {
2454
2128
  borderLeft: "2px solid var(--color-accent-text)",
2455
2129
  color: "var(--color-accent-text)",
@@ -2466,40 +2140,40 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap }
2466
2140
  import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
2467
2141
  import { searchKeymap } from "@codemirror/search";
2468
2142
  import { EditorState } from "@codemirror/state";
2469
- import { EditorView as EditorView15, ViewPlugin as ViewPlugin11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2, scrollPastEnd } from "@codemirror/view";
2143
+ import { EditorView as EditorView16, ViewPlugin as ViewPlugin12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2 } from "@codemirror/view";
2470
2144
  import { vscodeDarkStyle, vscodeLightStyle } from "@uiw/codemirror-theme-vscode";
2471
2145
  import defaultsDeep2 from "lodash.defaultsdeep";
2472
2146
  import { generateName } from "@dxos/display-name";
2473
2147
  import { log as log8 } from "@dxos/log";
2474
- import { hexToHue, isTruthy as isTruthy2 } from "@dxos/util";
2148
+ import { hexToHue, isTruthy as isTruthy3 } from "@dxos/util";
2475
2149
 
2476
2150
  // src/styles/theme.ts
2477
- import { EditorView as EditorView13 } from "@codemirror/view";
2151
+ import { EditorView as EditorView11 } from "@codemirror/view";
2478
2152
  import { mx as mx3 } from "@dxos/ui-theme";
2479
2153
  var headings = {
2480
2154
  1: {
2481
- className: "text-4xl",
2482
- fontSize: "var(--text-4xl)",
2483
- lineHeight: "var(--text-4xl--line-height)"
2484
- },
2485
- 2: {
2486
2155
  className: "text-3xl",
2487
2156
  fontSize: "var(--text-3xl)",
2488
- lineHeight: "var(--text-3xl--line-height)"
2157
+ lineHeight: "var(--text-4xl--line-height)"
2489
2158
  },
2490
- 3: {
2159
+ 2: {
2491
2160
  className: "text-2xl",
2492
2161
  fontSize: "var(--text-2xl)",
2493
- lineHeight: "var(--text-2xl--line-height)"
2162
+ lineHeight: "var(--text-3xl--line-height)"
2494
2163
  },
2495
- 4: {
2164
+ 3: {
2496
2165
  className: "text-xl",
2497
2166
  fontSize: "var(--text-xl)",
2498
- lineHeight: "var(--text-xl--line-height)"
2167
+ lineHeight: "var(--text-2xl--line-height)"
2499
2168
  },
2500
- 5: {
2169
+ 4: {
2501
2170
  className: "text-lg",
2502
2171
  fontSize: "var(--text-lg)",
2172
+ lineHeight: "var(--text-xl--line-height)"
2173
+ },
2174
+ 5: {
2175
+ className: "text-base",
2176
+ fontSize: "var(--text-base)",
2503
2177
  lineHeight: "var(--text-lg--line-height)"
2504
2178
  },
2505
2179
  6: {
@@ -2508,21 +2182,21 @@ var headings = {
2508
2182
  lineHeight: "var(--text-base--line-height)"
2509
2183
  }
2510
2184
  };
2185
+ var fontBody = '"Inter Variable", ui-sans-serif, system-ui, sans-serif';
2186
+ var fontMono = '"JetBrains Mono Variable", ui-monospace, "Cascadia Code", "Source Code Pro", monospace';
2511
2187
  var markdownTheme = {
2512
- code: "font-mono no-underline! text-cm-code",
2513
- codeMark: "font-mono text-cm-code-mark",
2514
- mark: "opacity-50",
2188
+ code: "font-mono! cm-code-inline",
2189
+ codeMark: "font-mono! cm-code-mark",
2190
+ mark: "font-mono!",
2515
2191
  heading: (level) => ({
2516
- className: mx3(headings[level].className, "font-light text-cm-heading"),
2192
+ className: mx3(headings[level].className, "font-light text-(--color-cm-heading-number)"),
2517
2193
  color: "var(--color-cm-heading) !important",
2518
2194
  lineHeight: headings[level].lineHeight,
2519
2195
  fontSize: headings[level].fontSize,
2520
2196
  fontWeight: "100 !important"
2521
2197
  })
2522
2198
  };
2523
- var fontBody = "Inter Variable, ui-sans-serif, system-ui, sans-serif";
2524
- var fontMono = "JetBrains Mono Variable, ui-monospace, Cascadia Code, Source Code Pro, monospace";
2525
- var baseTheme = EditorView13.baseTheme({
2199
+ var baseTheme = EditorView11.baseTheme({
2526
2200
  /**
2527
2201
  * Outer frame.
2528
2202
  */
@@ -2534,12 +2208,21 @@ var baseTheme = EditorView13.baseTheme({
2534
2208
  * Scroller
2535
2209
  */
2536
2210
  ".cm-scroller": {
2537
- overflowAnchor: "none"
2211
+ // Browser scroll-anchoring: see comment in `scrolling/crawler.ts`. `auto` lets the browser pin a
2212
+ // stable element near the viewport top so widget resizes (e.g. tool-block TogglePanel
2213
+ // open/close) don't jump the user's view.
2214
+ overflowAnchor: "auto"
2538
2215
  },
2539
2216
  ".cm-scroller::-webkit-scrollbar": {
2540
- width: "8px"
2217
+ width: "var(--scrollbar-size,8px)",
2218
+ height: "var(--scrollbar-size,8px)"
2219
+ },
2220
+ ".cm-scroller::-webkit-scrollbar-corner": {
2221
+ background: "transparent"
2222
+ },
2223
+ ".cm-scroller::-webkit-scrollbar-track": {
2224
+ background: "transparent"
2541
2225
  },
2542
- ".cm-scroller::-webkit-scrollbar-track": {},
2543
2226
  ".cm-scroller::-webkit-scrollbar-thumb": {
2544
2227
  background: "transparent",
2545
2228
  transition: "background 0.15s"
@@ -2553,7 +2236,6 @@ var baseTheme = EditorView13.baseTheme({
2553
2236
  */
2554
2237
  ".cm-content": {
2555
2238
  padding: "unset",
2556
- lineHeight: "24px",
2557
2239
  color: "unset"
2558
2240
  },
2559
2241
  /**
@@ -2584,9 +2266,16 @@ var baseTheme = EditorView13.baseTheme({
2584
2266
  * Line.
2585
2267
  */
2586
2268
  ".cm-line": {
2587
- lineHeight: "24px",
2269
+ lineHeight: 1.5,
2588
2270
  paddingInline: 0
2589
2271
  },
2272
+ /**
2273
+ * Force all inline children to inherit line-height to prevent monospace font metrics
2274
+ * (JetBrains Mono ascent/descent) from inflating the line box beyond 24px.
2275
+ */
2276
+ ".cm-line *": {
2277
+ lineHeight: "inherit"
2278
+ },
2590
2279
  ".cm-activeLine": {
2591
2280
  background: "var(--color-cm-active-line)"
2592
2281
  },
@@ -2630,7 +2319,8 @@ var baseTheme = EditorView13.baseTheme({
2630
2319
  textDecorationThickness: "1px",
2631
2320
  textDecorationColor: "var(--color-separator)",
2632
2321
  textUnderlineOffset: "2px",
2633
- borderRadius: ".125rem"
2322
+ borderRadius: ".125rem",
2323
+ cursor: "pointer"
2634
2324
  },
2635
2325
  ".cm-link > span": {
2636
2326
  color: "var(--color-accent-text)"
@@ -2668,12 +2358,12 @@ var baseTheme = EditorView13.baseTheme({
2668
2358
  padding: "4px"
2669
2359
  },
2670
2360
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2671
- background: "var(--color-active-surface)",
2672
- color: "var(--color-base-surface-text)"
2361
+ background: "var(--color-current-surface)",
2362
+ color: "var(--color-base-foreground)"
2673
2363
  },
2674
2364
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2675
2365
  paddingLeft: "4px !important",
2676
- color: "var(--color-base-surface-text)"
2366
+ color: "var(--color-base-foreground)"
2677
2367
  },
2678
2368
  /**
2679
2369
  * Completion info.
@@ -2692,7 +2382,7 @@ var baseTheme = EditorView13.baseTheme({
2692
2382
  padding: "0 4px"
2693
2383
  },
2694
2384
  ".cm-completionMatchedText": {
2695
- color: "var(--color-base-surface-text)",
2385
+ color: "var(--color-base-foreground)",
2696
2386
  textDecoration: "none !important"
2697
2387
  },
2698
2388
  /**
@@ -2727,7 +2417,7 @@ var baseTheme = EditorView13.baseTheme({
2727
2417
  backgroundColor: "var(--color-input-surface)"
2728
2418
  },
2729
2419
  ".cm-panel input:focus, .cm-panel button:focus": {
2730
- outline: "1px solid var(--color-neutral-focus-indicator)"
2420
+ outline: "1px solid var(--color-focus-ring-subtle)"
2731
2421
  },
2732
2422
  ".cm-panel label": {
2733
2423
  display: "inline-flex",
@@ -2740,7 +2430,7 @@ var baseTheme = EditorView13.baseTheme({
2740
2430
  height: "8px",
2741
2431
  marginRight: "6px !important",
2742
2432
  padding: "2px !important",
2743
- color: "var(--color-neutral-focus-indicator)"
2433
+ color: "var(--color-focus-ring-subtle)"
2744
2434
  },
2745
2435
  ".cm-panel button": {
2746
2436
  "&:hover": {
@@ -2756,18 +2446,17 @@ var baseTheme = EditorView13.baseTheme({
2756
2446
  borderTop: "1px solid var(--color-separator)"
2757
2447
  }
2758
2448
  });
2759
- var editorGutter = EditorView13.theme({
2449
+ var editorGutter = EditorView11.theme({
2760
2450
  ".cm-gutters": {
2761
2451
  // NOTE: Non-transparent background required to cover content if scrolling horizontally.
2762
2452
  background: "var(--color-base-surface) !important",
2763
2453
  paddingRight: "1rem"
2764
2454
  }
2765
2455
  });
2766
- var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2767
- // Set metrics on the scroller (this is often what CM uses for layout).
2456
+ var createFontTheme = ({ monospace } = {}) => EditorView11.theme({
2457
+ // Main content.
2768
2458
  ".cm-scroller": {
2769
- fontFamily: monospace ? fontMono : fontBody,
2770
- fontSize: "16px"
2459
+ fontFamily: monospace ? fontMono : fontBody
2771
2460
  },
2772
2461
  // Maintain defaults for UI components.
2773
2462
  ".cm-content, .cm-gutters, .cm-panel": {
@@ -2777,9 +2466,9 @@ var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2777
2466
  });
2778
2467
 
2779
2468
  // src/extensions/focus.ts
2780
- import { StateEffect as StateEffect5, StateField as StateField5 } from "@codemirror/state";
2781
- import { EditorView as EditorView14 } from "@codemirror/view";
2782
- var focusEffect = StateEffect5.define();
2469
+ import { StateEffect as StateEffect4, StateField as StateField5 } from "@codemirror/state";
2470
+ import { EditorView as EditorView12 } from "@codemirror/view";
2471
+ var focusEffect = StateEffect4.define();
2783
2472
  var focusField = StateField5.define({
2784
2473
  create: () => false,
2785
2474
  update: (value, tr) => {
@@ -2793,7 +2482,7 @@ var focusField = StateField5.define({
2793
2482
  });
2794
2483
  var focus = [
2795
2484
  focusField,
2796
- EditorView14.domEventHandlers({
2485
+ EditorView12.domEventHandlers({
2797
2486
  focus: (_event, view) => {
2798
2487
  requestAnimationFrame(() => view.dispatch({
2799
2488
  effects: focusEffect.of(true)
@@ -2807,9 +2496,388 @@ var focus = [
2807
2496
  })
2808
2497
  ];
2809
2498
 
2499
+ // src/extensions/scrolling/auto-scroll.ts
2500
+ import { StateEffect as StateEffect6 } from "@codemirror/state";
2501
+ import { EditorView as EditorView14, ViewPlugin as ViewPlugin10 } from "@codemirror/view";
2502
+ import { addEventListener, combine, throttle as throttle2 } from "@dxos/async";
2503
+ import { Domino } from "@dxos/ui";
2504
+ import { getSize } from "@dxos/ui-theme";
2505
+
2506
+ // src/extensions/scrolling/crawler.ts
2507
+ import { StateEffect as StateEffect5 } from "@codemirror/state";
2508
+ import { EditorView as EditorView13, ViewPlugin as ViewPlugin9 } from "@codemirror/view";
2509
+ import { log as log7 } from "@dxos/log";
2510
+ var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scrolling/crawler.ts";
2511
+ var crawlerLineEffect = StateEffect5.define();
2512
+ var crawlerActiveEffect = StateEffect5.define();
2513
+ var scrollToLine = (view, options) => {
2514
+ view.dispatch({
2515
+ effects: crawlerLineEffect.of(options)
2516
+ });
2517
+ };
2518
+ var crawler = ({ overScroll = 0 } = {}) => {
2519
+ const crawlerPlugin = ViewPlugin9.fromClass(class CrawlerPlugin {
2520
+ view;
2521
+ crawler;
2522
+ constructor(view) {
2523
+ this.view = view;
2524
+ this.crawler = createCrawler(this.view);
2525
+ }
2526
+ // No-op.
2527
+ destroy() {
2528
+ this.crawler.cancel();
2529
+ }
2530
+ cancel() {
2531
+ this.crawler.cancel();
2532
+ }
2533
+ crawl(start = false) {
2534
+ if (start) {
2535
+ this.crawler.scroll();
2536
+ } else {
2537
+ this.crawler.cancel();
2538
+ }
2539
+ }
2540
+ scroll({ line, offset = 0, position, behavior = "instant" }) {
2541
+ const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
2542
+ const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
2543
+ const doc = this.view.state.doc;
2544
+ let targetScrollTop = scrollHeight - clientHeight + offset;
2545
+ if (line >= 0 && line <= doc.lines - 1) {
2546
+ const lineStart = doc.line(line + 1).from;
2547
+ const coords = this.view.coordsAtPos(lineStart);
2548
+ if (coords) {
2549
+ const currentScrollTop = scrollTop;
2550
+ const maxScrollTop = scrollHeight - clientHeight;
2551
+ if (position === "end") {
2552
+ targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
2553
+ } else {
2554
+ targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
2555
+ }
2556
+ targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
2557
+ }
2558
+ }
2559
+ requestAnimationFrame(() => {
2560
+ this.view.scrollDOM.scrollTo({
2561
+ top: targetScrollTop
2562
+ });
2563
+ });
2564
+ }
2565
+ });
2566
+ return [
2567
+ crawlerPlugin,
2568
+ // Listen for effect.
2569
+ EditorView13.updateListener.of((update2) => {
2570
+ update2.transactions.forEach((transaction) => {
2571
+ try {
2572
+ const plugin = update2.view.plugin(crawlerPlugin);
2573
+ if (plugin) {
2574
+ for (const effect of transaction.effects) {
2575
+ if (effect.is(crawlerActiveEffect)) {
2576
+ plugin.crawl(effect.value);
2577
+ } else if (effect.is(crawlerLineEffect)) {
2578
+ plugin.scroll(effect.value);
2579
+ }
2580
+ }
2581
+ }
2582
+ } catch (err) {
2583
+ log7.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 98, S: void 0 });
2584
+ }
2585
+ });
2586
+ }),
2587
+ // Styles.
2588
+ EditorView13.theme({
2589
+ ".cm-scroller": {
2590
+ overflowY: "scroll",
2591
+ // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks
2592
+ // expanding their TogglePanel), the browser picks a stable element near the viewport
2593
+ // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning
2594
+ // logic still has the final word when pinned (forces scrollTop to scrollHeight).
2595
+ overflowAnchor: "auto"
2596
+ },
2597
+ ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
2598
+ display: "none"
2599
+ },
2600
+ ".cm-scroller::-webkit-scrollbar-thumb": {
2601
+ background: "transparent",
2602
+ transition: "background 0.15s"
2603
+ },
2604
+ "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
2605
+ background: "var(--color-scrollbar-thumb)"
2606
+ },
2607
+ // Spacer below the last text line. Implemented as a real block pseudo-element
2608
+ // (rather than `padding-bottom` on `.cm-content`) so it materializes in the
2609
+ // scroller's `scrollHeight` regardless of how `padding` is reset by the base
2610
+ // theme or downstream classes — this is what gives auto-scroll its head-room
2611
+ // so the last line stays `overScroll` px above the viewport bottom.
2612
+ ".cm-content::after": {
2613
+ content: '""',
2614
+ display: "block",
2615
+ height: `${overScroll}px`
2616
+ },
2617
+ ".cm-scroll-button": {
2618
+ position: "absolute",
2619
+ bottom: "0.5rem",
2620
+ right: "1rem"
2621
+ }
2622
+ })
2623
+ ];
2624
+ };
2625
+ function createCrawler(view, omega = 5, snapThreshold = 5, snapVelocity = 50) {
2626
+ const el = view.scrollDOM;
2627
+ let currentTop = 0;
2628
+ let velocity = 0;
2629
+ let rafId = null;
2630
+ let lastTime = 0;
2631
+ function frame(now) {
2632
+ const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1e3);
2633
+ lastTime = now;
2634
+ const targetTop = el.scrollHeight - el.clientHeight;
2635
+ const delta = targetTop - currentTop;
2636
+ if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {
2637
+ el.scrollTop = targetTop;
2638
+ currentTop = targetTop;
2639
+ velocity = 0;
2640
+ rafId = null;
2641
+ lastTime = 0;
2642
+ return;
2643
+ }
2644
+ const accel = omega * omega * delta - 2 * omega * velocity;
2645
+ velocity += accel * dt;
2646
+ currentTop += velocity * dt;
2647
+ el.scrollTop = currentTop;
2648
+ rafId = requestAnimationFrame(frame);
2649
+ }
2650
+ return {
2651
+ scroll: () => {
2652
+ if (rafId === null) {
2653
+ currentTop = el.scrollTop;
2654
+ lastTime = 0;
2655
+ rafId = requestAnimationFrame(frame);
2656
+ }
2657
+ },
2658
+ cancel: () => {
2659
+ if (rafId !== null) {
2660
+ cancelAnimationFrame(rafId);
2661
+ velocity = 0;
2662
+ lastTime = 0;
2663
+ rafId = null;
2664
+ }
2665
+ }
2666
+ };
2667
+ }
2668
+
2669
+ // src/extensions/scrolling/auto-scroll.ts
2670
+ var autoScrollEffect = StateEffect6.define();
2671
+ var autoScroll = ({ scrollOnResize = true } = {}) => {
2672
+ let buttonContainer;
2673
+ let isPinned = true;
2674
+ let jumpPending = false;
2675
+ let enabled = true;
2676
+ let firstUpdate = true;
2677
+ const setPinned = (pinned) => {
2678
+ buttonContainer?.classList.toggle("opacity-0", pinned);
2679
+ isPinned = pinned;
2680
+ };
2681
+ return [
2682
+ // Update listener for scrolling when content changes.
2683
+ EditorView14.updateListener.of((update2) => {
2684
+ const { view, heightChanged, state, startState } = update2;
2685
+ for (const tr of update2.transactions) {
2686
+ for (const effect of tr.effects) {
2687
+ if (effect.is(autoScrollEffect)) {
2688
+ enabled = effect.value;
2689
+ if (enabled) {
2690
+ setPinned(true);
2691
+ view.dispatch({
2692
+ effects: crawlerActiveEffect.of(true)
2693
+ });
2694
+ } else {
2695
+ view.dispatch({
2696
+ effects: crawlerActiveEffect.of(false)
2697
+ });
2698
+ }
2699
+ }
2700
+ }
2701
+ }
2702
+ if (!enabled) {
2703
+ return;
2704
+ }
2705
+ if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
2706
+ firstUpdate = false;
2707
+ jumpPending = true;
2708
+ requestAnimationFrame(() => {
2709
+ view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
2710
+ jumpPending = false;
2711
+ });
2712
+ return;
2713
+ }
2714
+ firstUpdate = false;
2715
+ if (jumpPending) {
2716
+ return;
2717
+ }
2718
+ if (heightChanged) {
2719
+ if (isPinned) {
2720
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
2721
+ const delta = scrollHeight - scrollTop - clientHeight;
2722
+ if (delta > 0) {
2723
+ setPinned(true);
2724
+ view.dispatch({
2725
+ effects: crawlerActiveEffect.of(true)
2726
+ });
2727
+ } else if (delta < -1) {
2728
+ setPinned(false);
2729
+ }
2730
+ } else {
2731
+ if (state.doc.length === 0) {
2732
+ setPinned(true);
2733
+ }
2734
+ }
2735
+ }
2736
+ }),
2737
+ // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,
2738
+ // window resize). Doc-driven height changes are handled by the updateListener above; this
2739
+ // observer covers the case where the viewport changes while the doc length is unchanged.
2740
+ scrollOnResize ? ViewPlugin10.fromClass(class {
2741
+ observer;
2742
+ firstObservation = true;
2743
+ destroyed = false;
2744
+ constructor(view) {
2745
+ const onResize = throttle2(() => {
2746
+ if (this.destroyed || !enabled) {
2747
+ return;
2748
+ }
2749
+ setPinned(true);
2750
+ requestAnimationFrame(() => {
2751
+ if (this.destroyed) {
2752
+ return;
2753
+ }
2754
+ view.scrollDOM.scrollTo({
2755
+ top: view.scrollDOM.scrollHeight,
2756
+ behavior: "instant"
2757
+ });
2758
+ view.dispatch({
2759
+ effects: crawlerActiveEffect.of(false)
2760
+ });
2761
+ });
2762
+ }, 50);
2763
+ this.observer = new ResizeObserver(() => {
2764
+ if (this.firstObservation) {
2765
+ this.firstObservation = false;
2766
+ return;
2767
+ }
2768
+ onResize();
2769
+ });
2770
+ this.observer.observe(view.scrollDOM);
2771
+ }
2772
+ destroy() {
2773
+ this.destroyed = true;
2774
+ this.observer.disconnect();
2775
+ }
2776
+ }) : [],
2777
+ // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
2778
+ ViewPlugin10.fromClass(class {
2779
+ cleanup;
2780
+ constructor(view) {
2781
+ const onUserScroll = throttle2(() => {
2782
+ requestAnimationFrame(() => {
2783
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
2784
+ const delta = scrollHeight - scrollTop - clientHeight;
2785
+ const pinned = Math.abs(delta) <= 1;
2786
+ setPinned(pinned);
2787
+ if (!pinned) {
2788
+ view.dispatch({
2789
+ effects: crawlerActiveEffect.of(false)
2790
+ });
2791
+ }
2792
+ });
2793
+ }, 500);
2794
+ this.cleanup = createUserScrollDetector(view.scrollDOM, () => {
2795
+ if (isPinned) {
2796
+ setPinned(false);
2797
+ view.dispatch({
2798
+ effects: crawlerActiveEffect.of(false)
2799
+ });
2800
+ }
2801
+ onUserScroll();
2802
+ });
2803
+ }
2804
+ destroy() {
2805
+ this.cleanup();
2806
+ }
2807
+ }),
2808
+ // Scroll button.
2809
+ ViewPlugin10.fromClass(class {
2810
+ constructor(view) {
2811
+ const icon = Domino.of("dx-icon").classNames(getSize(4)).attributes({
2812
+ icon: "ph--arrow-down--regular"
2813
+ });
2814
+ const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
2815
+ "data-density": "fine"
2816
+ }).append(icon).on("click", () => {
2817
+ setPinned(true);
2818
+ view.dispatch({
2819
+ effects: crawlerLineEffect.of({
2820
+ line: -1,
2821
+ position: "end",
2822
+ behavior: "smooth"
2823
+ })
2824
+ });
2825
+ });
2826
+ buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").append(button).root;
2827
+ view.scrollDOM.parentElement.appendChild(buttonContainer);
2828
+ }
2829
+ })
2830
+ ];
2831
+ };
2832
+ function createUserScrollDetector(element, onUserScroll) {
2833
+ return combine(addEventListener(element, "wheel", () => onUserScroll(), {
2834
+ passive: true
2835
+ }), addEventListener(element, "pointerdown", (event) => {
2836
+ if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
2837
+ onUserScroll();
2838
+ }
2839
+ }));
2840
+ }
2841
+
2842
+ // src/extensions/scrolling/scroll-past-end.ts
2843
+ import { EditorView as EditorView15, ViewPlugin as ViewPlugin11 } from "@codemirror/view";
2844
+ var scrollPastEndPlugin = ViewPlugin11.fromClass(class {
2845
+ _height = 1e3;
2846
+ _attrs = {
2847
+ style: `padding-bottom: ${this._height}px`
2848
+ };
2849
+ update({ view }) {
2850
+ const lastLineBlock = view.lineBlockAt(view.state.doc.length);
2851
+ const height = view.dom.clientHeight - lastLineBlock.height - view.documentPadding.top - 0.5;
2852
+ if (height >= 0 && height !== this._height) {
2853
+ this._height = height;
2854
+ this._attrs = {
2855
+ style: `padding-bottom: ${height}px`
2856
+ };
2857
+ }
2858
+ }
2859
+ });
2860
+ var scrollPastEnd = () => [
2861
+ scrollPastEndPlugin,
2862
+ EditorView15.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?._attrs ?? null)
2863
+ ];
2864
+
2865
+ // src/extensions/scrolling/scroller.ts
2866
+ import { isTruthy as isTruthy2 } from "@dxos/util";
2867
+ var scroller = ({ overScroll, scrollOnResize, autoScroll: autoScroll2 = true } = {}) => {
2868
+ return [
2869
+ crawler({
2870
+ overScroll
2871
+ }),
2872
+ autoScroll2 && autoScroll({
2873
+ scrollOnResize
2874
+ })
2875
+ ].filter(isTruthy2);
2876
+ };
2877
+
2810
2878
  // src/extensions/factories.ts
2811
2879
  var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2812
- var tabbable = EditorView15.contentAttributes.of({
2880
+ var tabbable = EditorView16.contentAttributes.of({
2813
2881
  tabindex: "0"
2814
2882
  });
2815
2883
  var filterChars = (chars) => {
@@ -2862,13 +2930,8 @@ var createBasicExtensions = (propsProp) => {
2862
2930
  const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2863
2931
  return [
2864
2932
  // NOTE: Doesn't catch errors in keymap functions.
2865
- EditorView15.exceptionSink.of((err) => {
2866
- log8.catch(err, void 0, {
2867
- F: __dxlog_file11,
2868
- L: 131,
2869
- S: void 0,
2870
- C: (f, a) => f(...a)
2871
- });
2933
+ EditorView16.exceptionSink.of((err) => {
2934
+ log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
2872
2935
  }),
2873
2936
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2874
2937
  props.bracketMatching && bracketMatching(),
@@ -2877,7 +2940,7 @@ var createBasicExtensions = (propsProp) => {
2877
2940
  props.drawSelection && drawSelection({
2878
2941
  cursorBlinkRate: 1200
2879
2942
  }),
2880
- props.editable !== void 0 && EditorView15.editable.of(props.editable),
2943
+ props.editable !== void 0 && EditorView16.editable.of(props.editable),
2881
2944
  props.focus && focus,
2882
2945
  props.highlightActiveLine && highlightActiveLine(),
2883
2946
  props.history && history(),
@@ -2885,9 +2948,16 @@ var createBasicExtensions = (propsProp) => {
2885
2948
  lineNumbers(),
2886
2949
  editorGutter
2887
2950
  ],
2888
- props.lineWrapping && EditorView15.lineWrapping,
2951
+ props.lineWrapping && EditorView16.lineWrapping,
2889
2952
  props.placeholder && placeholder2(props.placeholder),
2890
2953
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2954
+ // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
2955
+ // transactions. Some extensions (e.g. `@codemirror/lang-markdown`'s Enter handler that
2956
+ // continues a list) dispatch programmatic edits regardless. Drop user-initiated edits
2957
+ // (`input` / `delete` keymap dispatches plus `undo` / `redo` from the history extension)
2958
+ // but pass programmatic dispatches — streaming `MarkdownStream` and similar consumers
2959
+ // depend on being able to populate the doc themselves.
2960
+ props.readOnly && EditorState.transactionFilter.of((tr) => tr.docChanged && (tr.isUserEvent("input") || tr.isUserEvent("delete") || tr.isUserEvent("undo") || tr.isUserEvent("redo")) ? [] : tr),
2891
2961
  props.scrollPastEnd && scrollPastEnd(),
2892
2962
  props.tabbable && tabbable,
2893
2963
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -2912,8 +2982,8 @@ var createBasicExtensions = (propsProp) => {
2912
2982
  preventDefault: true,
2913
2983
  run: () => true
2914
2984
  }
2915
- ].filter(isTruthy2))
2916
- ].filter(isTruthy2);
2985
+ ].filter(isTruthy3))
2986
+ ].filter(isTruthy3);
2917
2987
  };
2918
2988
  var grow = {
2919
2989
  editor: {
@@ -2930,27 +3000,32 @@ var defaultStyles = {
2930
3000
  dark: vscodeDarkStyle,
2931
3001
  light: vscodeLightStyle
2932
3002
  };
2933
- var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp } = {}) => {
3003
+ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp, themeMode } = {}) => {
2934
3004
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
2935
3005
  return [
2936
3006
  baseTheme,
2937
- EditorView15.darkTheme.of(themeMode === "dark"),
3007
+ EditorView16.darkTheme.of(themeMode === "dark"),
2938
3008
  createFontTheme({
2939
3009
  monospace
2940
3010
  }),
2941
3011
  syntaxHighlightingProp && syntaxHighlighting(HighlightStyle.define(themeMode === "dark" ? defaultStyles.dark : defaultStyles.light)),
2942
- slots.editor?.className && EditorView15.editorAttributes.of({
3012
+ slots.editor?.className && EditorView16.editorAttributes.of({
2943
3013
  class: slots.editor.className
2944
3014
  }),
2945
- slots.content?.className && EditorView15.contentAttributes.of({
3015
+ slots.content?.className && EditorView16.contentAttributes.of({
2946
3016
  class: slots.content.className
2947
3017
  }),
2948
- slots.scroll?.className && ViewPlugin11.fromClass(class {
3018
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
2949
3019
  constructor(view) {
2950
- view.scrollDOM.classList.add(...slots.scroll.className.split(/\s+/));
3020
+ if (slots.scroller?.className) {
3021
+ view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
3022
+ }
3023
+ if (scrollbarThin) {
3024
+ view.scrollDOM.style.setProperty("--scrollbar-size", "4px");
3025
+ }
2951
3026
  }
2952
3027
  })
2953
- ].filter(isTruthy2);
3028
+ ].filter(isTruthy3);
2954
3029
  };
2955
3030
  var createDataExtensions = ({ id, text, messenger, identity }) => {
2956
3031
  const extensions = [];
@@ -2976,7 +3051,7 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2976
3051
 
2977
3052
  // src/extensions/folding.ts
2978
3053
  import { codeFolding, foldGutter } from "@codemirror/language";
2979
- import { EditorView as EditorView16 } from "@codemirror/view";
3054
+ import { EditorView as EditorView17 } from "@codemirror/view";
2980
3055
  import { Domino as Domino2, mx as mx4 } from "@dxos/ui";
2981
3056
  var folding = () => {
2982
3057
  return [
@@ -2984,13 +3059,14 @@ var folding = () => {
2984
3059
  placeholderDOM: () => Domino2.of("span").root
2985
3060
  }),
2986
3061
  foldGutter({
3062
+ // NOTE: We can't animate since the element is remounted on state change.
2987
3063
  markerDOM: (open) => {
2988
- return Domino2.of("div").classNames("flex h-full justify-center items-center").children(Domino2.of("svg", Domino2.SVG).classNames(mx4("w-4 h-4 cursor-pointer", open && "rotate-90")).children(Domino2.of("use", Domino2.SVG).attributes({
3064
+ return Domino2.of("div").classNames("flex h-full justify-center items-center").append(Domino2.of("svg", Domino2.SVG).classNames(mx4("w-4 h-4 cursor-pointer", open && "rotate-90")).append(Domino2.of("use", Domino2.SVG).attributes({
2989
3065
  href: Domino2.icon("ph--caret-right--regular")
2990
3066
  }))).root;
2991
3067
  }
2992
3068
  }),
2993
- EditorView16.theme({
3069
+ EditorView17.theme({
2994
3070
  ".cm-foldGutter": {
2995
3071
  opacity: 0.3,
2996
3072
  transition: "opacity 0.3s",
@@ -3004,7 +3080,7 @@ var folding = () => {
3004
3080
  };
3005
3081
 
3006
3082
  // src/extensions/hashtag.ts
3007
- import { Decoration as Decoration8, EditorView as EditorView17, MatchDecorator, ViewPlugin as ViewPlugin12, WidgetType as WidgetType4 } from "@codemirror/view";
3083
+ import { Decoration as Decoration8, EditorView as EditorView18, MatchDecorator, ViewPlugin as ViewPlugin13, WidgetType as WidgetType4 } from "@codemirror/view";
3008
3084
  import { getHashStyles, mx as mx5 } from "@dxos/ui-theme";
3009
3085
  var TagWidget = class extends WidgetType4 {
3010
3086
  _text;
@@ -3025,7 +3101,7 @@ var tagMatcher = new MatchDecorator({
3025
3101
  })
3026
3102
  });
3027
3103
  var hashtag = () => [
3028
- ViewPlugin12.fromClass(class {
3104
+ ViewPlugin13.fromClass(class {
3029
3105
  tags;
3030
3106
  constructor(view) {
3031
3107
  this.tags = tagMatcher.createDeco(view);
@@ -3035,11 +3111,11 @@ var hashtag = () => [
3035
3111
  }
3036
3112
  }, {
3037
3113
  decorations: (instance) => instance.tags,
3038
- provide: (plugin) => EditorView17.atomicRanges.of((view) => {
3114
+ provide: (plugin) => EditorView18.atomicRanges.of((view) => {
3039
3115
  return view.plugin(plugin)?.tags || Decoration8.none;
3040
3116
  })
3041
3117
  }),
3042
- EditorView17.theme({
3118
+ EditorView18.theme({
3043
3119
  ".cm-tag": {
3044
3120
  borderRadius: "4px",
3045
3121
  marginRight: "6px",
@@ -3094,18 +3170,18 @@ var schemaLinter = (validate) => (view) => {
3094
3170
  };
3095
3171
 
3096
3172
  // src/extensions/listener.ts
3097
- import { EditorView as EditorView18 } from "@codemirror/view";
3173
+ import { EditorView as EditorView19 } from "@codemirror/view";
3098
3174
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
3099
3175
  var listener = ({ onFocus, onChange }) => {
3100
3176
  return [
3101
- onFocus && EditorView18.focusChangeEffect.of((state, focusing) => {
3177
+ onFocus && EditorView19.focusChangeEffect.of((state, focusing) => {
3102
3178
  onFocus({
3103
3179
  id: state.facet(documentId),
3104
3180
  focusing
3105
3181
  });
3106
3182
  return null;
3107
3183
  }),
3108
- onChange && EditorView18.updateListener.of(({ state, docChanged }) => {
3184
+ onChange && EditorView19.updateListener.of(({ state, docChanged }) => {
3109
3185
  if (docChanged) {
3110
3186
  onChange({
3111
3187
  id: state.facet(documentId),
@@ -3120,7 +3196,7 @@ var listener = ({ onFocus, onChange }) => {
3120
3196
  import { snippet } from "@codemirror/autocomplete";
3121
3197
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3122
3198
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
3123
- import { EditorView as EditorView19, keymap as keymap8 } from "@codemirror/view";
3199
+ import { EditorView as EditorView20, keymap as keymap8 } from "@codemirror/view";
3124
3200
  import { debounceAndThrottle } from "@dxos/async";
3125
3201
  var formattingEquals = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
3126
3202
  var Inline = /* @__PURE__ */ (function(Inline2) {
@@ -4209,7 +4285,7 @@ var getFormatting = (state) => {
4209
4285
  };
4210
4286
  };
4211
4287
  var formattingListener = (onStateChange, delay = 100) => {
4212
- return EditorView19.updateListener.of(debounceAndThrottle((update2) => {
4288
+ return EditorView20.updateListener.of(debounceAndThrottle((update2) => {
4213
4289
  if (update2.docChanged || update2.selectionSet) {
4214
4290
  onStateChange(getFormatting(update2.state));
4215
4291
  }
@@ -4270,22 +4346,16 @@ import { completionKeymap } from "@codemirror/autocomplete";
4270
4346
  import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
4271
4347
  import { jsonLanguage } from "@codemirror/lang-json";
4272
4348
  import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
4273
- import { xml } from "@codemirror/lang-xml";
4274
- import { LanguageDescription, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4349
+ import { foldNodeProp, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4275
4350
  import { languages } from "@codemirror/language-data";
4276
4351
  import { keymap as keymap9 } from "@codemirror/view";
4277
- import { isTruthy as isTruthy3 } from "@dxos/util";
4352
+ import { isTruthy as isTruthy4 } from "@dxos/util";
4278
4353
 
4279
4354
  // src/extensions/markdown/highlight.ts
4280
4355
  import { markdownLanguage } from "@codemirror/lang-markdown";
4281
4356
  import { HighlightStyle as HighlightStyle2 } from "@codemirror/language";
4282
4357
  import { Tag, styleTags, tags } from "@lezer/highlight";
4283
4358
  import { Table } from "@lezer/markdown";
4284
- var styles4 = {
4285
- code: "font-mono no-underline! text-cm-code",
4286
- codeMark: "font-mono text-cm-code-mark",
4287
- mark: "opacity-50"
4288
- };
4289
4359
  var markdownTags = {
4290
4360
  Blockquote: Tag.define(),
4291
4361
  CodeMark: Tag.define(),
@@ -4367,7 +4437,7 @@ var markdownHighlightStyle = (_options = {}) => {
4367
4437
  markdownTags.LinkReference,
4368
4438
  markdownTags.ListMark
4369
4439
  ],
4370
- class: styles4.mark
4440
+ class: markdownTheme.mark
4371
4441
  },
4372
4442
  // Markdown marks.
4373
4443
  {
@@ -4378,7 +4448,7 @@ var markdownHighlightStyle = (_options = {}) => {
4378
4448
  markdownTags.QuoteMark,
4379
4449
  markdownTags.EmphasisMark
4380
4450
  ],
4381
- class: styles4.mark
4451
+ class: markdownTheme.mark
4382
4452
  },
4383
4453
  // E.g., code block language (after ```).
4384
4454
  {
@@ -4387,7 +4457,7 @@ var markdownHighlightStyle = (_options = {}) => {
4387
4457
  tags.function(tags.variableName),
4388
4458
  tags.labelName
4389
4459
  ],
4390
- class: styles4.codeMark
4460
+ class: markdownTheme.codeMark
4391
4461
  },
4392
4462
  // Fonts.
4393
4463
  {
@@ -4453,7 +4523,7 @@ var markdownHighlightStyle = (_options = {}) => {
4453
4523
  markdownTags.CodeText,
4454
4524
  markdownTags.InlineCode
4455
4525
  ],
4456
- class: styles4.code
4526
+ class: markdownTheme.code
4457
4527
  },
4458
4528
  {
4459
4529
  tag: [
@@ -4483,15 +4553,23 @@ var createMarkdownExtensions = (options = {}) => {
4483
4553
  // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
4484
4554
  base: markdownLanguage2,
4485
4555
  // Languages for syntax highlighting fenced code blocks.
4556
+ // Caller-supplied languages are checked first so they can override defaults.
4486
4557
  defaultCodeLanguage: jsonLanguage,
4487
- codeLanguages: languages,
4558
+ codeLanguages: [
4559
+ ...options.codeLanguages ?? [],
4560
+ ...languages
4561
+ ],
4488
4562
  // Don't complete HTML tags.
4489
4563
  completeHTMLTags: false,
4490
4564
  // Parser extensions.
4491
4565
  extensions: [
4492
4566
  // GFM provided by default.
4493
4567
  markdownTagsExtensions,
4494
- ...options.extensions ?? defaultExtensions()
4568
+ ...options.extensions ?? defaultExtensions(),
4569
+ // Disable folding for fenced code blocks by overriding foldNodeProp.
4570
+ // Note: returning null from foldService does not prevent syntaxFolding fallback,
4571
+ // so we must override the node prop directly on the FencedCode node type.
4572
+ noFencedCodeFolding
4495
4573
  ]
4496
4574
  }),
4497
4575
  // Custom styles.
@@ -4503,21 +4581,16 @@ var createMarkdownExtensions = (options = {}) => {
4503
4581
  ...defaultKeymap2,
4504
4582
  // TODO(burdon): Remove?
4505
4583
  ...completionKeymap
4506
- ].filter(isTruthy3))
4584
+ ].filter(isTruthy4))
4507
4585
  ];
4508
4586
  };
4509
- var xmlLanguageDesc = LanguageDescription.of({
4510
- name: "xml",
4511
- alias: [
4512
- "html",
4513
- "xhtml"
4514
- ],
4515
- extensions: [
4516
- "xml",
4517
- "xhtml"
4518
- ],
4519
- load: async () => xml()
4520
- });
4587
+ var noFencedCodeFolding = {
4588
+ props: [
4589
+ foldNodeProp.add({
4590
+ FencedCode: () => null
4591
+ })
4592
+ ]
4593
+ };
4521
4594
  var defaultExtensions = () => [
4522
4595
  noSetExtHeading,
4523
4596
  noHtml
@@ -4537,19 +4610,19 @@ var debugTree = (cb) => StateField6.define({
4537
4610
  update: (value, tr) => cb(convertTreeToJson(tr.state))
4538
4611
  });
4539
4612
  var convertTreeToJson = (state) => {
4540
- const treeToJson = (cursor2) => {
4613
+ const treeToJson = (cursor) => {
4541
4614
  const node = {
4542
- type: cursor2.type.name,
4543
- from: cursor2.from,
4544
- to: cursor2.to,
4545
- text: state.doc.slice(cursor2.from, cursor2.to).toString(),
4615
+ type: cursor.type.name,
4616
+ from: cursor.from,
4617
+ to: cursor.to,
4618
+ text: state.doc.slice(cursor.from, cursor.to).toString(),
4546
4619
  children: []
4547
4620
  };
4548
- if (cursor2.firstChild()) {
4621
+ if (cursor.firstChild()) {
4549
4622
  do {
4550
- node.children.push(treeToJson(cursor2));
4551
- } while (cursor2.nextSibling());
4552
- cursor2.parent();
4623
+ node.children.push(treeToJson(cursor));
4624
+ } while (cursor.nextSibling());
4625
+ cursor.parent();
4553
4626
  }
4554
4627
  return node;
4555
4628
  };
@@ -4558,17 +4631,16 @@ var convertTreeToJson = (state) => {
4558
4631
 
4559
4632
  // src/extensions/markdown/decorate.ts
4560
4633
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4561
- import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect6 } from "@codemirror/state";
4562
- import { Decoration as Decoration11, EditorView as EditorView23, ViewPlugin as ViewPlugin14, WidgetType as WidgetType7 } from "@codemirror/view";
4634
+ import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect7 } from "@codemirror/state";
4635
+ import { Decoration as Decoration11, EditorView as EditorView24, ViewPlugin as ViewPlugin15, WidgetType as WidgetType7 } from "@codemirror/view";
4563
4636
  import { invariant as invariant4 } from "@dxos/invariant";
4564
- import { mx as mx6 } from "@dxos/ui-theme";
4565
4637
 
4566
4638
  // src/extensions/markdown/changes.ts
4567
4639
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4568
4640
  import { Transaction as Transaction4 } from "@codemirror/state";
4569
- import { ViewPlugin as ViewPlugin13 } from "@codemirror/view";
4641
+ import { ViewPlugin as ViewPlugin14 } from "@codemirror/view";
4570
4642
  var adjustChanges = () => {
4571
- return ViewPlugin13.fromClass(class {
4643
+ return ViewPlugin14.fromClass(class {
4572
4644
  update(update2) {
4573
4645
  const tree = syntaxTree4(update2.state);
4574
4646
  const adjustments = [];
@@ -4710,7 +4782,7 @@ var getValidUrl = (str) => {
4710
4782
  // src/extensions/markdown/image.ts
4711
4783
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4712
4784
  import { StateField as StateField7 } from "@codemirror/state";
4713
- import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType5 } from "@codemirror/view";
4785
+ import { Decoration as Decoration9, EditorView as EditorView21, WidgetType as WidgetType5 } from "@codemirror/view";
4714
4786
  var image = (_options = {}) => {
4715
4787
  return [
4716
4788
  StateField7.define({
@@ -4721,10 +4793,10 @@ var image = (_options = {}) => {
4721
4793
  if (!tr.docChanged && !tr.selection) {
4722
4794
  return value;
4723
4795
  }
4724
- const cursor2 = tr.state.selection.main.head;
4796
+ const cursor = tr.state.selection.main.head;
4725
4797
  const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
4726
- let from = Math.min(cursor2, oldCursor);
4727
- let to = Math.max(cursor2, oldCursor);
4798
+ let from = Math.min(cursor, oldCursor);
4799
+ let to = Math.max(cursor, oldCursor);
4728
4800
  tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
4729
4801
  from = Math.min(from, fromB);
4730
4802
  to = Math.max(to, toB);
@@ -4738,19 +4810,19 @@ var image = (_options = {}) => {
4738
4810
  add: buildDecorations(tr.state, from, to)
4739
4811
  });
4740
4812
  },
4741
- provide: (field) => EditorView20.decorations.from(field)
4813
+ provide: (field) => EditorView21.decorations.from(field)
4742
4814
  })
4743
4815
  ];
4744
4816
  };
4745
4817
  var buildDecorations = (state, from, to) => {
4746
4818
  const decorations2 = [];
4747
- const cursor2 = state.selection.main.head;
4819
+ const cursor = state.selection.main.head;
4748
4820
  syntaxTree5(state).iterate({
4749
4821
  enter: (node) => {
4750
4822
  if (node.name === "Image") {
4751
4823
  const urlNode = node.node.getChild("URL");
4752
4824
  if (urlNode) {
4753
- const hide2 = state.readOnly || cursor2 < node.from || cursor2 > node.to || !state.field(focusField);
4825
+ const hide2 = state.readOnly || cursor < node.from || cursor > node.to || !state.field(focusField);
4754
4826
  const url = state.sliceDoc(urlNode.from, urlNode.to);
4755
4827
  if (url.match(/^https?:\/\//) === null && url.match(/^file?:\/\//) === null) {
4756
4828
  return;
@@ -4798,10 +4870,10 @@ var ImageWidget = class extends WidgetType5 {
4798
4870
  };
4799
4871
 
4800
4872
  // src/extensions/markdown/styles.ts
4801
- import { EditorView as EditorView21 } from "@codemirror/view";
4873
+ import { EditorView as EditorView22 } from "@codemirror/view";
4802
4874
  var bulletListIndentationWidth = 24;
4803
4875
  var orderedListIndentationWidth = 36;
4804
- var formattingStyles = EditorView21.theme({
4876
+ var formattingStyles = EditorView22.theme({
4805
4877
  /**
4806
4878
  * Horizontal rule.
4807
4879
  */
@@ -4836,13 +4908,38 @@ var formattingStyles = EditorView21.theme({
4836
4908
  background: "var(--color-cm-codeblock)",
4837
4909
  borderLeft: "2px solid var(--color-cm-separator)",
4838
4910
  paddingLeft: "1rem",
4839
- margin: "0"
4911
+ margin: 0
4840
4912
  },
4841
4913
  /**
4842
4914
  * Code and codeblocks.
4843
4915
  */
4916
+ "& code": {
4917
+ fontFamily: fontMono,
4918
+ color: "var(--color-cm-code)",
4919
+ whiteSpace: "nowrap"
4920
+ },
4844
4921
  "& .cm-code": {
4845
- fontFamily: fontMono
4922
+ fontFamily: fontMono,
4923
+ color: "var(--color-cm-code)"
4924
+ },
4925
+ // Inline code spans (triggered by backticks) use `cm-code-inline` + `font-mono`.
4926
+ // Different monospace font metrics can slightly overflow the fixed CodeMirror line box,
4927
+ // so constrain them to the target 24px height.
4928
+ "& .cm-code-inline": {
4929
+ fontFamily: fontMono,
4930
+ height: "24px",
4931
+ // display: 'inline-flex',
4932
+ alignItems: "center",
4933
+ overflow: "hidden",
4934
+ whiteSpace: "nowrap",
4935
+ color: "var(--color-cm-code-inline)"
4936
+ },
4937
+ "& .cm-code-mark": {
4938
+ fontFamily: fontMono,
4939
+ height: "24px",
4940
+ display: "inline-flex",
4941
+ alignItems: "center",
4942
+ overflow: "hidden"
4846
4943
  },
4847
4944
  "& .cm-codeblock-line": {
4848
4945
  background: "var(--color-cm-codeblock)",
@@ -4874,16 +4971,24 @@ var formattingStyles = EditorView21.theme({
4874
4971
  */
4875
4972
  ".cm-table *": {
4876
4973
  fontFamily: fontMono,
4974
+ lineHeight: 1.5,
4877
4975
  textDecoration: "none !important"
4878
4976
  },
4879
4977
  ".cm-table-head": {
4880
4978
  padding: "2px 16px 2px 0px",
4979
+ overflowWrap: "break-word",
4980
+ whiteSpace: "pre-wrap",
4981
+ wordBreak: "keep-all",
4881
4982
  textAlign: "left",
4882
- borderBottom: "1px solid var(--color-cm-separator)",
4883
- color: "var(--color-subdued)"
4983
+ color: "var(--color-subdued)",
4984
+ borderBottom: "1px solid var(--color-cm-separator)"
4884
4985
  },
4885
4986
  ".cm-table-cell": {
4886
- padding: "2px 16px 2px 0px"
4987
+ padding: "2px 16px 2px 0px",
4988
+ overflowWrap: "break-word",
4989
+ whiteSpace: "pre-wrap",
4990
+ wordBreak: "keep-all",
4991
+ verticalAlign: "top"
4887
4992
  },
4888
4993
  /**
4889
4994
  * Image.
@@ -4899,12 +5004,12 @@ var formattingStyles = EditorView21.theme({
4899
5004
  },
4900
5005
  ".cm-image-with-loader": {
4901
5006
  display: "block",
4902
- opacity: "0",
5007
+ opacity: 0,
4903
5008
  transitionDuration: "350ms",
4904
5009
  transitionProperty: "opacity"
4905
5010
  },
4906
5011
  ".cm-image-with-loader.cm-loaded-image": {
4907
- opacity: "1"
5012
+ opacity: 1
4908
5013
  },
4909
5014
  ".cm-image-wrapper": {
4910
5015
  "grid-template-columns": "1fr",
@@ -4923,17 +5028,17 @@ var formattingStyles = EditorView21.theme({
4923
5028
  // src/extensions/markdown/table.ts
4924
5029
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4925
5030
  import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4926
- import { Decoration as Decoration10, EditorView as EditorView22, WidgetType as WidgetType6 } from "@codemirror/view";
5031
+ import { Decoration as Decoration10, EditorView as EditorView23, WidgetType as WidgetType6 } from "@codemirror/view";
4927
5032
  var table = (options = {}) => {
4928
5033
  return StateField8.define({
4929
5034
  create: (state) => update(state, options),
4930
5035
  update: (_, tr) => update(tr.state, options),
4931
- provide: (field) => EditorView22.decorations.from(field)
5036
+ provide: (field) => EditorView23.decorations.from(field)
4932
5037
  });
4933
5038
  };
4934
5039
  var update = (state, _options) => {
4935
5040
  const builder = new RangeSetBuilder4();
4936
- const cursor2 = state.selection.main.head;
5041
+ const cursor = state.selection.main.head;
4937
5042
  const tables = [];
4938
5043
  const getTable = () => tables[tables.length - 1];
4939
5044
  const getRow = () => {
@@ -4971,7 +5076,7 @@ var update = (state, _options) => {
4971
5076
  }
4972
5077
  });
4973
5078
  tables.forEach((table2) => {
4974
- const replace = state.readOnly || cursor2 < table2.from || cursor2 > table2.to;
5079
+ const replace = state.readOnly || cursor < table2.from || cursor > table2.to;
4975
5080
  if (replace) {
4976
5081
  builder.add(table2.from, table2.to, Decoration10.replace({
4977
5082
  block: true,
@@ -4985,6 +5090,26 @@ var update = (state, _options) => {
4985
5090
  });
4986
5091
  return builder.finish();
4987
5092
  };
5093
+ var renderCellContent = (el, text) => {
5094
+ const parts = text.split(/(`[^`\n]+`|\*\*[^*\n]+\*\*|__[^_\n]+__|\*[^*\n]+\*|_[^_\n]+_)/);
5095
+ for (const part of parts) {
5096
+ if (part.length > 2 && part.startsWith("`") && part.endsWith("`")) {
5097
+ const code = document.createElement("code");
5098
+ code.textContent = part.slice(1, -1);
5099
+ el.appendChild(code);
5100
+ } else if (part.startsWith("**") && part.endsWith("**") || part.startsWith("__") && part.endsWith("__")) {
5101
+ const strong = document.createElement("strong");
5102
+ strong.textContent = part.slice(2, -2);
5103
+ el.appendChild(strong);
5104
+ } else if (part.startsWith("*") && part.endsWith("*") || part.startsWith("_") && part.endsWith("_")) {
5105
+ const em = document.createElement("em");
5106
+ em.textContent = part.slice(1, -1);
5107
+ el.appendChild(em);
5108
+ } else {
5109
+ el.appendChild(document.createTextNode(part));
5110
+ }
5111
+ }
5112
+ };
4988
5113
  var TableWidget = class extends WidgetType6 {
4989
5114
  _table;
4990
5115
  constructor(_table) {
@@ -5004,7 +5129,7 @@ var TableWidget = class extends WidgetType6 {
5004
5129
  this._table.header?.forEach((cell) => {
5005
5130
  const th = document.createElement("th");
5006
5131
  th.setAttribute("class", "cm-table-head");
5007
- tr.appendChild(th).textContent = cell;
5132
+ renderCellContent(tr.appendChild(th), cell);
5008
5133
  });
5009
5134
  const body = table2.appendChild(document.createElement("tbody"));
5010
5135
  this._table.rows?.forEach((row) => {
@@ -5012,7 +5137,7 @@ var TableWidget = class extends WidgetType6 {
5012
5137
  row.forEach((cell) => {
5013
5138
  const td = document.createElement("td");
5014
5139
  td.setAttribute("class", "cm-table-cell");
5015
- tr2.appendChild(td).textContent = cell;
5140
+ renderCellContent(tr2.appendChild(td), cell);
5016
5141
  });
5017
5142
  });
5018
5143
  return div;
@@ -5119,10 +5244,10 @@ var fencedCodeLine = Decoration11.line({
5119
5244
  class: "cm-code cm-codeblock-line"
5120
5245
  });
5121
5246
  var fencedCodeLineFirst = Decoration11.line({
5122
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-start")
5247
+ class: "cm-code cm-codeblock-line cm-codeblock-start"
5123
5248
  });
5124
5249
  var fencedCodeLineLast = Decoration11.line({
5125
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-end")
5250
+ class: "cm-code cm-codeblock-line cm-codeblock-end"
5126
5251
  });
5127
5252
  var commentBlockLine = fencedCodeLine;
5128
5253
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -5154,15 +5279,7 @@ var buildDecorations2 = (view, options, focus2) => {
5154
5279
  const { state } = view;
5155
5280
  const headerLevels = [];
5156
5281
  const getHeaderLevels = (node, level) => {
5157
- invariant4(level > 0, void 0, {
5158
- F: __dxlog_file12,
5159
- L: 179,
5160
- S: void 0,
5161
- A: [
5162
- "level > 0",
5163
- ""
5164
- ]
5165
- });
5282
+ invariant4(level > 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 160, S: void 0, A: ["level > 0", ""] });
5166
5283
  if (level > headerLevels.length) {
5167
5284
  const len = headerLevels.length;
5168
5285
  headerLevels.length = level;
@@ -5193,15 +5310,7 @@ var buildDecorations2 = (view, options, focus2) => {
5193
5310
  listLevels.pop();
5194
5311
  };
5195
5312
  const getCurrentListLevel = () => {
5196
- invariant4(listLevels.length, void 0, {
5197
- F: __dxlog_file12,
5198
- L: 201,
5199
- S: void 0,
5200
- A: [
5201
- "listLevels.length",
5202
- ""
5203
- ]
5204
- });
5313
+ invariant4(listLevels.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 192, S: void 0, A: ["listLevels.length", ""] });
5205
5314
  return listLevels[listLevels.length - 1];
5206
5315
  };
5207
5316
  const enterNode = (node) => {
@@ -5239,7 +5348,7 @@ var buildDecorations2 = (view, options, focus2) => {
5239
5348
  deco: hide
5240
5349
  });
5241
5350
  } else {
5242
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5351
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + "). ";
5243
5352
  if (num.length) {
5244
5353
  atomicDecoRanges.push({
5245
5354
  from: mark.from,
@@ -5422,11 +5531,11 @@ var buildDecorations2 = (view, options, focus2) => {
5422
5531
  }
5423
5532
  decoRanges.push({
5424
5533
  from: marks[0].to,
5425
- to: marks[1].from,
5534
+ to: !editing && options.renderLinkButton ? node.to : marks[1].from,
5426
5535
  deco: Decoration11.mark({
5427
5536
  tagName: "a",
5428
5537
  attributes: {
5429
- class: "cm-link",
5538
+ class: options.renderLinkButton ? "cm-link cm-link-with-button" : "cm-link",
5430
5539
  href: url,
5431
5540
  rel: "noreferrer",
5432
5541
  target: "_blank"
@@ -5504,18 +5613,21 @@ var buildDecorations2 = (view, options, focus2) => {
5504
5613
  deco.add(from, to, d);
5505
5614
  }
5506
5615
  const atomicDeco = new RangeSetBuilder5();
5507
- for (const { from, to, deco: d } of atomicDecoRanges) {
5508
- atomicDeco.add(from, to, d);
5616
+ for (const { from, to, deco: deco2 } of atomicDecoRanges) {
5617
+ if (from < to && state.doc.lineAt(from).number !== state.doc.lineAt(to).number) {
5618
+ continue;
5619
+ }
5620
+ atomicDeco.add(from, to, deco2);
5509
5621
  }
5510
5622
  return {
5511
5623
  deco: deco.finish(),
5512
5624
  atomicDeco: atomicDeco.finish()
5513
5625
  };
5514
5626
  };
5515
- var forceUpdate = StateEffect6.define();
5627
+ var forceUpdate = StateEffect7.define();
5516
5628
  var decorateMarkdown = (options = {}) => {
5517
5629
  return [
5518
- ViewPlugin14.fromClass(class {
5630
+ ViewPlugin15.fromClass(class {
5519
5631
  deco;
5520
5632
  atomicDeco;
5521
5633
  pendingUpdate;
@@ -5550,9 +5662,9 @@ var decorateMarkdown = (options = {}) => {
5550
5662
  }
5551
5663
  }, {
5552
5664
  provide: (plugin) => [
5553
- Prec4.low(EditorView23.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5554
- EditorView23.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5555
- EditorView23.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5665
+ Prec4.low(EditorView24.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5666
+ EditorView24.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5667
+ EditorView24.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5556
5668
  ]
5557
5669
  }),
5558
5670
  image(),
@@ -5620,12 +5732,7 @@ var mention = ({ debug, onSearch }) => {
5620
5732
  (context) => {
5621
5733
  log9.info("completion context", {
5622
5734
  context
5623
- }, {
5624
- F: __dxlog_file13,
5625
- L: 27,
5626
- S: void 0,
5627
- C: (f, a) => f(...a)
5628
- });
5735
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 18, S: void 0 });
5629
5736
  const match = context.matchBefore(/@(\w+)?/);
5630
5737
  if (!match || match.from === match.to && !context.explicit) {
5631
5738
  return null;
@@ -5642,8 +5749,8 @@ var mention = ({ debug, onSearch }) => {
5642
5749
  };
5643
5750
 
5644
5751
  // src/extensions/modal.ts
5645
- import { StateEffect as StateEffect7, StateField as StateField9 } from "@codemirror/state";
5646
- var modalStateEffect = StateEffect7.define();
5752
+ import { StateEffect as StateEffect8, StateField as StateField9 } from "@codemirror/state";
5753
+ var modalStateEffect = StateEffect8.define();
5647
5754
  var modalStateField = StateField9.define({
5648
5755
  create: () => false,
5649
5756
  update: (value, tr) => {
@@ -5858,15 +5965,7 @@ var outlinerTree = (_options = {}) => {
5858
5965
  break;
5859
5966
  }
5860
5967
  case "BulletList": {
5861
- invariant5(current, void 0, {
5862
- F: __dxlog_file14,
5863
- L: 219,
5864
- S: void 0,
5865
- A: [
5866
- "current",
5867
- ""
5868
- ]
5869
- });
5968
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 169, S: void 0, A: ["current", ""] });
5870
5969
  parent = current;
5871
5970
  if (current) {
5872
5971
  current.lineRange.to = current.node.from;
@@ -5875,15 +5974,7 @@ var outlinerTree = (_options = {}) => {
5875
5974
  break;
5876
5975
  }
5877
5976
  case "ListItem": {
5878
- invariant5(parent, void 0, {
5879
- F: __dxlog_file14,
5880
- L: 228,
5881
- S: void 0,
5882
- A: [
5883
- "parent",
5884
- ""
5885
- ]
5886
- });
5977
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 179, S: void 0, A: ["parent", ""] });
5887
5978
  const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
5888
5979
  const docRange = {
5889
5980
  from: state.doc.lineAt(node.from).from,
@@ -5917,42 +6008,18 @@ var outlinerTree = (_options = {}) => {
5917
6008
  break;
5918
6009
  }
5919
6010
  case "ListMark": {
5920
- invariant5(current, void 0, {
5921
- F: __dxlog_file14,
5922
- L: 272,
5923
- S: void 0,
5924
- A: [
5925
- "current",
5926
- ""
5927
- ]
5928
- });
6011
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 219, S: void 0, A: ["current", ""] });
5929
6012
  current.type = "bullet";
5930
6013
  current.contentRange.from = node.from + "- ".length;
5931
6014
  break;
5932
6015
  }
5933
6016
  case "Task": {
5934
- invariant5(current, void 0, {
5935
- F: __dxlog_file14,
5936
- L: 278,
5937
- S: void 0,
5938
- A: [
5939
- "current",
5940
- ""
5941
- ]
5942
- });
6017
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 226, S: void 0, A: ["current", ""] });
5943
6018
  current.type = "task";
5944
6019
  break;
5945
6020
  }
5946
6021
  case "TaskMarker": {
5947
- invariant5(current, void 0, {
5948
- F: __dxlog_file14,
5949
- L: 283,
5950
- S: void 0,
5951
- A: [
5952
- "current",
5953
- ""
5954
- ]
5955
- });
6022
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 232, S: void 0, A: ["current", ""] });
5956
6023
  current.contentRange.from = node.from + "[ ] ".length;
5957
6024
  break;
5958
6025
  }
@@ -5960,29 +6027,13 @@ var outlinerTree = (_options = {}) => {
5960
6027
  },
5961
6028
  leave: (node) => {
5962
6029
  if (node.name === "BulletList") {
5963
- invariant5(parent, void 0, {
5964
- F: __dxlog_file14,
5965
- L: 291,
5966
- S: void 0,
5967
- A: [
5968
- "parent",
5969
- ""
5970
- ]
5971
- });
6030
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 240, S: void 0, A: ["parent", ""] });
5972
6031
  prevSiblings[level--] = void 0;
5973
6032
  parent = parent.parent;
5974
6033
  }
5975
6034
  }
5976
6035
  });
5977
- invariant5(tree, void 0, {
5978
- F: __dxlog_file14,
5979
- L: 298,
5980
- S: void 0,
5981
- A: [
5982
- "tree",
5983
- ""
5984
- ]
5985
- });
6036
+ invariant5(tree, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 246, S: void 0, A: ["tree", ""] });
5986
6037
  return tree;
5987
6038
  };
5988
6039
  return [
@@ -6267,17 +6318,17 @@ var commands = () => keymap11.of([
6267
6318
 
6268
6319
  // src/extensions/outliner/outliner.ts
6269
6320
  import { Prec as Prec5 } from "@codemirror/state";
6270
- import { Decoration as Decoration12, EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6271
- import { mx as mx7 } from "@dxos/ui-theme";
6321
+ import { Decoration as Decoration12, EditorView as EditorView26, ViewPlugin as ViewPlugin18 } from "@codemirror/view";
6322
+ import { mx as mx6 } from "@dxos/ui-theme";
6272
6323
 
6273
6324
  // src/extensions/outliner/editor.ts
6274
6325
  import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
6275
- import { ViewPlugin as ViewPlugin15 } from "@codemirror/view";
6326
+ import { ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6276
6327
  import { log as log10 } from "@dxos/log";
6277
6328
  var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6278
6329
  var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
6279
6330
  var initialize = () => {
6280
- return ViewPlugin15.fromClass(class {
6331
+ return ViewPlugin16.fromClass(class {
6281
6332
  constructor(view) {
6282
6333
  const first = view.state.doc.lineAt(0);
6283
6334
  const text = view.state.sliceDoc(first.from, first.to);
@@ -6424,35 +6475,20 @@ var editor = () => [
6424
6475
  text: insert.toString(),
6425
6476
  length: insert.length
6426
6477
  }
6427
- }, {
6428
- F: __dxlog_file15,
6429
- L: 164,
6430
- S: void 0,
6431
- C: (f, a) => f(...a)
6432
- });
6478
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 174, S: void 0 });
6433
6479
  }
6434
6480
  });
6435
6481
  if (changes.length > 0) {
6436
6482
  log10("modified,", {
6437
6483
  changes
6438
- }, {
6439
- F: __dxlog_file15,
6440
- L: 175,
6441
- S: void 0,
6442
- C: (f, a) => f(...a)
6443
- });
6484
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 196, S: void 0 });
6444
6485
  return [
6445
6486
  {
6446
6487
  changes
6447
6488
  }
6448
6489
  ];
6449
6490
  } else if (cancel) {
6450
- log10("cancel", void 0, {
6451
- F: __dxlog_file15,
6452
- L: 178,
6453
- S: void 0,
6454
- C: (f, a) => f(...a)
6455
- });
6491
+ log10("cancel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 205, S: void 0 });
6456
6492
  return [];
6457
6493
  }
6458
6494
  return tr;
@@ -6460,10 +6496,10 @@ var editor = () => [
6460
6496
  ];
6461
6497
 
6462
6498
  // src/extensions/outliner/menu.ts
6463
- import { EditorView as EditorView24, ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6499
+ import { EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6464
6500
  import { addEventListener as addEventListener2 } from "@dxos/async";
6465
6501
  var menu = (options = {}) => [
6466
- ViewPlugin16.fromClass(class {
6502
+ ViewPlugin17.fromClass(class {
6467
6503
  view;
6468
6504
  tag;
6469
6505
  rafId;
@@ -6525,7 +6561,7 @@ var menu = (options = {}) => [
6525
6561
  this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
6526
6562
  }
6527
6563
  }),
6528
- EditorView24.theme({
6564
+ EditorView25.theme({
6529
6565
  ".cm-popover-trigger": {
6530
6566
  position: "fixed",
6531
6567
  padding: "0",
@@ -6561,12 +6597,12 @@ var outliner = (_options = {}) => [
6561
6597
  listPaddingLeft: 8
6562
6598
  }),
6563
6599
  // Researve space for menu.
6564
- EditorView25.contentAttributes.of({
6600
+ EditorView26.contentAttributes.of({
6565
6601
  class: "w-full !mr-[3rem]"
6566
6602
  })
6567
6603
  ];
6568
6604
  var decorations = () => [
6569
- ViewPlugin17.fromClass(class {
6605
+ ViewPlugin18.fromClass(class {
6570
6606
  decorations = Decoration12.none;
6571
6607
  constructor(view) {
6572
6608
  this.updateDecorations(view.state, view);
@@ -6591,7 +6627,7 @@ var decorations = () => [
6591
6627
  const lineTo = doc.lineAt(item.contentRange.to);
6592
6628
  const isSelected = selection.includes(item.index) || item === current;
6593
6629
  decorations2.push(Decoration12.line({
6594
- class: mx7("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6630
+ class: mx6("cm-list-item", lineFrom.number === line.number && "cm-list-item-start", lineTo.number === line.number && "cm-list-item-end", isSelected && (hasFocus ? "cm-list-item-focused" : "cm-list-item-selected"))
6595
6631
  }).range(line.from, line.from));
6596
6632
  }
6597
6633
  }
@@ -6601,7 +6637,7 @@ var decorations = () => [
6601
6637
  decorations: (v) => v.decorations
6602
6638
  }),
6603
6639
  // Theme.
6604
- EditorView25.theme(Object.assign({
6640
+ EditorView26.theme(Object.assign({
6605
6641
  ".cm-list-item": {
6606
6642
  borderLeftWidth: "1px",
6607
6643
  borderRightWidth: "1px",
@@ -6626,7 +6662,7 @@ var decorations = () => [
6626
6662
  marginBottom: "2px"
6627
6663
  },
6628
6664
  ".cm-list-item-focused": {
6629
- borderColor: "var(--color-neutral-focus-indicator)"
6665
+ borderColor: "var(--color-focus-ring-subtle)"
6630
6666
  },
6631
6667
  "&:focus-within .cm-list-item-selected": {
6632
6668
  borderColor: "var(--color-separator)"
@@ -6636,28 +6672,57 @@ var decorations = () => [
6636
6672
 
6637
6673
  // src/extensions/preview/preview.ts
6638
6674
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
6639
- import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField11 } from "@codemirror/state";
6640
- import { Decoration as Decoration13, EditorView as EditorView26, WidgetType as WidgetType8 } from "@codemirror/view";
6675
+ import { RangeSetBuilder as RangeSetBuilder6, StateEffect as StateEffect9, StateField as StateField11 } from "@codemirror/state";
6676
+ import { Decoration as Decoration13, EditorView as EditorView27, ViewPlugin as ViewPlugin19, WidgetType as WidgetType8 } from "@codemirror/view";
6677
+ import { DXN, Entity } from "@dxos/echo";
6678
+ var labelResolvedEffect = StateEffect9.define();
6641
6679
  var preview = (options = {}) => {
6680
+ const viewRef = {
6681
+ current: void 0
6682
+ };
6642
6683
  return [
6643
6684
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
6644
6685
  // "Block decorations may not be specified via plugins".
6645
6686
  StateField11.define({
6646
- create: (state) => buildDecorations3(state, options),
6687
+ create: (state) => buildDecorations3(state, options, viewRef),
6647
6688
  update: (decorations2, tr) => {
6648
- if (tr.docChanged) {
6649
- return buildDecorations3(tr.state, options);
6689
+ if (tr.docChanged || tr.effects.some((effect) => effect.is(labelResolvedEffect))) {
6690
+ return buildDecorations3(tr.state, options, viewRef);
6650
6691
  }
6651
6692
  return decorations2.map(tr.changes);
6652
6693
  },
6653
6694
  provide: (field) => [
6654
- EditorView26.decorations.from(field),
6655
- EditorView26.atomicRanges.of((view) => view.state.field(field))
6695
+ EditorView27.decorations.from(field),
6696
+ EditorView27.atomicRanges.of((view) => view.state.field(field))
6656
6697
  ]
6698
+ }),
6699
+ ViewPlugin19.define((view) => {
6700
+ viewRef.current = view;
6701
+ return {
6702
+ destroy() {
6703
+ viewRef.current = void 0;
6704
+ }
6705
+ };
6657
6706
  })
6658
6707
  ];
6659
6708
  };
6660
- var buildDecorations3 = (state, options) => {
6709
+ var resolveLabel = (db, dxnStr, viewRef) => {
6710
+ const dxn = DXN.tryParse(dxnStr);
6711
+ if (!dxn) {
6712
+ return;
6713
+ }
6714
+ const ref = db.makeRef(dxn);
6715
+ const target = ref.target;
6716
+ if (target) {
6717
+ return Entity.getLabel(target);
6718
+ }
6719
+ void ref.tryLoad().then(() => {
6720
+ viewRef.current?.dispatch({
6721
+ effects: labelResolvedEffect.of(void 0)
6722
+ });
6723
+ });
6724
+ };
6725
+ var buildDecorations3 = (state, options, viewRef) => {
6661
6726
  const builder = new RangeSetBuilder6();
6662
6727
  syntaxTree10(state).iterate({
6663
6728
  enter: (node) => {
@@ -6669,8 +6734,13 @@ var buildDecorations3 = (state, options) => {
6669
6734
  case "Link": {
6670
6735
  const link = getLinkRef(state, node.node);
6671
6736
  if (link) {
6737
+ const resolved = options.db ? resolveLabel(options.db, link.dxn, viewRef) : void 0;
6738
+ const displayLink = resolved ? {
6739
+ ...link,
6740
+ label: resolved
6741
+ } : link;
6672
6742
  builder.add(node.from, node.to, Decoration13.replace({
6673
- widget: new PreviewInlineWidget(options, link),
6743
+ widget: new PreviewInlineWidget(options, displayLink),
6674
6744
  side: 1
6675
6745
  }));
6676
6746
  }
@@ -6762,7 +6832,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6762
6832
  };
6763
6833
 
6764
6834
  // src/extensions/replacer.ts
6765
- import { EditorView as EditorView27 } from "@codemirror/view";
6835
+ import { EditorView as EditorView28 } from "@codemirror/view";
6766
6836
  var defaultReplacements = [
6767
6837
  {
6768
6838
  input: "--",
@@ -6825,7 +6895,7 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6825
6895
  const sortedReplacements = [
6826
6896
  ...replacements
6827
6897
  ].sort((a, b) => b.input.length - a.input.length);
6828
- return EditorView27.inputHandler.of((view, from, to, insert) => {
6898
+ return EditorView28.inputHandler.of((view, from, to, insert) => {
6829
6899
  if (insert.length !== 1) {
6830
6900
  return false;
6831
6901
  }
@@ -6859,12 +6929,69 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6859
6929
  });
6860
6930
  };
6861
6931
 
6932
+ // src/extensions/snippets.ts
6933
+ import { keymap as keymap12 } from "@codemirror/view";
6934
+ var defaultItems = [
6935
+ "hello world!",
6936
+ "this is a test.",
6937
+ "this is [DXOS](https://dxos.org)"
6938
+ ];
6939
+ var snippets2 = ({ delay = 75, items = defaultItems } = {}) => {
6940
+ let timer;
6941
+ let index = 0;
6942
+ return [
6943
+ keymap12.of([
6944
+ {
6945
+ // Reset.
6946
+ key: "alt-meta-'",
6947
+ run: () => {
6948
+ clearTimeout(timer);
6949
+ index = 0;
6950
+ return true;
6951
+ }
6952
+ },
6953
+ {
6954
+ // Next snippet.
6955
+ // TODO(burdon): Press 1-9 to select snippet?
6956
+ key: "Shift-Meta-'",
6957
+ run: (view) => {
6958
+ clearTimeout(timer);
6959
+ const text = items[index++];
6960
+ if (index === items?.length) {
6961
+ index = 0;
6962
+ }
6963
+ let offset = 0;
6964
+ const insert = (delayMs = 0) => {
6965
+ timer = setTimeout(() => {
6966
+ const pos = view.state.selection.main.head;
6967
+ view.dispatch({
6968
+ changes: {
6969
+ from: pos,
6970
+ insert: text[offset++]
6971
+ },
6972
+ selection: {
6973
+ anchor: pos + 1
6974
+ }
6975
+ });
6976
+ if (offset < text.length) {
6977
+ insert(Math.random() * delay * (text[offset] === " " ? 2 : 1));
6978
+ }
6979
+ }, delayMs);
6980
+ };
6981
+ insert();
6982
+ return true;
6983
+ }
6984
+ }
6985
+ ])
6986
+ ];
6987
+ };
6988
+
6862
6989
  // src/extensions/submit.ts
6863
6990
  import { Prec as Prec6 } from "@codemirror/state";
6864
- import { keymap as keymap12 } from "@codemirror/view";
6991
+ import { keymap as keymap13 } from "@codemirror/view";
6865
6992
  var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6866
6993
  return [
6867
- Prec6.highest(keymap12.of([
6994
+ Prec6.highest(keymap13.of([
6868
6995
  {
6869
6996
  key: "Enter",
6870
6997
  preventDefault: true,
@@ -6909,6 +7036,7 @@ var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6909
7036
  // src/extensions/tags/extended-markdown.ts
6910
7037
  import { xmlLanguage } from "@codemirror/lang-xml";
6911
7038
  import { parseMixed } from "@lezer/common";
7039
+ var escapeRegExpSource = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6912
7040
  var extendedMarkdown = ({ registry } = {}) => {
6913
7041
  return [
6914
7042
  createMarkdownExtensions({
@@ -6920,13 +7048,65 @@ var extendedMarkdown = ({ registry } = {}) => {
6920
7048
  {
6921
7049
  name: "SetextHeading",
6922
7050
  parse: () => false
6923
- }
7051
+ },
7052
+ // Custom XML block parser that keeps registered tags as a single HTMLBlock
7053
+ // even when their content contains blank lines.
7054
+ ...xmlBlockParsers(registry)
6924
7055
  ]
6925
7056
  }
6926
7057
  ]
6927
7058
  })
6928
7059
  ];
6929
7060
  };
7061
+ var xmlBlockParsers = (registry) => {
7062
+ const customTags = Object.keys(registry ?? {});
7063
+ if (customTags.length === 0) {
7064
+ return [];
7065
+ }
7066
+ const tagPattern = customTags.map(escapeRegExpSource).join("|");
7067
+ const selfClosePattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/>\\s*$`);
7068
+ const openPattern = new RegExp(`^\\s*<(${tagPattern})(\\s[^>]*)?\\/?>`);
7069
+ return [
7070
+ {
7071
+ name: "XMLBlock",
7072
+ before: "HTMLBlock",
7073
+ parse: (cx, line) => {
7074
+ const match = openPattern.exec(line.text);
7075
+ if (!match) {
7076
+ return false;
7077
+ }
7078
+ if (selfClosePattern.test(line.text)) {
7079
+ const end2 = cx.lineStart + line.text.length;
7080
+ cx.addElement(cx.elt("HTMLBlock", cx.lineStart, end2));
7081
+ cx.nextLine();
7082
+ return true;
7083
+ }
7084
+ if (match[0].trimEnd().endsWith("/>")) {
7085
+ return false;
7086
+ }
7087
+ const tagName = match[1];
7088
+ const closeTag = `</${tagName}>`;
7089
+ const start = cx.lineStart;
7090
+ if (line.text.includes(closeTag)) {
7091
+ cx.addElement(cx.elt("HTMLBlock", start, start + line.text.length));
7092
+ cx.nextLine();
7093
+ return true;
7094
+ }
7095
+ let end = cx.lineStart + line.text.length;
7096
+ while (cx.nextLine()) {
7097
+ end = cx.lineStart + line.text.length;
7098
+ if (line.text.includes(closeTag)) {
7099
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7100
+ cx.nextLine();
7101
+ return true;
7102
+ }
7103
+ }
7104
+ cx.addElement(cx.elt("HTMLBlock", start, end));
7105
+ return true;
7106
+ }
7107
+ }
7108
+ ];
7109
+ };
6930
7110
  var mixedParser = (registry) => {
6931
7111
  const customTags = Object.keys(registry ?? {});
6932
7112
  const tagPattern = new RegExp(`<(${customTags.join("|")})`);
@@ -6953,226 +7133,800 @@ var mixedParser = (registry) => {
6953
7133
  parser: xmlLanguage.parser
6954
7134
  };
6955
7135
  }
6956
- return null;
7136
+ return null;
7137
+ }
7138
+ }
7139
+ return null;
7140
+ });
7141
+ };
7142
+
7143
+ // src/extensions/tags/fader.ts
7144
+ import { StateEffect as StateEffect10, StateField as StateField12 } from "@codemirror/state";
7145
+ import { Decoration as Decoration14, EditorView as EditorView29, ViewPlugin as ViewPlugin20 } from "@codemirror/view";
7146
+ var DEFAULT_REMOVAL_DELAY = 5e3;
7147
+ var DEFAULT_COALESCE_WINDOW = 100;
7148
+ var CLEANUP_INTERVAL = 1e3;
7149
+ var fader = (options = {}) => {
7150
+ const removalDelay = DEFAULT_REMOVAL_DELAY;
7151
+ const coalesceWindow = options.coalesce ?? DEFAULT_COALESCE_WINDOW;
7152
+ let lastCount = -1;
7153
+ const log12 = (expiries) => {
7154
+ if (expiries.length !== lastCount) {
7155
+ lastCount = expiries.length;
7156
+ }
7157
+ };
7158
+ const dequeue = StateEffect10.define();
7159
+ const fadeField = StateField12.define({
7160
+ create: () => ({
7161
+ decorations: Decoration14.none,
7162
+ expiries: [],
7163
+ batchStart: 0
7164
+ }),
7165
+ update: ({ decorations: decorations2, expiries, batchStart }, tr) => {
7166
+ for (const effect of tr.effects) {
7167
+ if (effect.is(dequeue)) {
7168
+ const now2 = effect.value;
7169
+ let removeCount = 0;
7170
+ while (removeCount < expiries.length && expiries[removeCount] <= now2) {
7171
+ removeCount++;
7172
+ }
7173
+ if (removeCount > 0) {
7174
+ expiries = expiries.slice(removeCount);
7175
+ let skipped = 0;
7176
+ decorations2 = decorations2.update({
7177
+ filter: () => {
7178
+ if (skipped < removeCount) {
7179
+ skipped++;
7180
+ return false;
7181
+ }
7182
+ return true;
7183
+ }
7184
+ });
7185
+ }
7186
+ }
7187
+ }
7188
+ if (!tr.docChanged) {
7189
+ log12(expiries);
7190
+ return {
7191
+ decorations: decorations2,
7192
+ expiries,
7193
+ batchStart
7194
+ };
7195
+ }
7196
+ let isReset = tr.state.doc.length === 0;
7197
+ if (!isReset && tr.startState.doc.length > 0) {
7198
+ tr.changes.iterChanges((fromA, toA) => {
7199
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7200
+ isReset = true;
7201
+ }
7202
+ });
7203
+ }
7204
+ if (isReset) {
7205
+ log12([]);
7206
+ return {
7207
+ decorations: Decoration14.none,
7208
+ expiries: [],
7209
+ batchStart: 0
7210
+ };
7211
+ }
7212
+ const now = Date.now();
7213
+ const add = [];
7214
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7215
+ if (toA === tr.startState.doc.length && inserted.length > 0) {
7216
+ add.push({
7217
+ from: fromB,
7218
+ to: toB
7219
+ });
7220
+ }
7221
+ });
7222
+ if (add.length > 0) {
7223
+ const canCoalesce = expiries.length > 0 && batchStart > 0 && now - batchStart < coalesceWindow;
7224
+ if (canCoalesce) {
7225
+ let lastFrom = -1;
7226
+ let lastTo = -1;
7227
+ decorations2.between(0, tr.state.doc.length, (from, to) => {
7228
+ lastFrom = from;
7229
+ lastTo = to;
7230
+ });
7231
+ if (lastFrom >= 0) {
7232
+ decorations2 = decorations2.update({
7233
+ filter: (from, to) => !(from === lastFrom && to === lastTo)
7234
+ });
7235
+ const mergedFrom = Math.min(lastFrom, add[0].from);
7236
+ const mergedTo = add[add.length - 1].to;
7237
+ decorations2 = decorations2.update({
7238
+ add: [
7239
+ Decoration14.mark({
7240
+ class: "cm-fader"
7241
+ }).range(mergedFrom, mergedTo)
7242
+ ]
7243
+ });
7244
+ expiries = [
7245
+ ...expiries.slice(0, -1),
7246
+ now + removalDelay
7247
+ ];
7248
+ }
7249
+ } else {
7250
+ batchStart = now;
7251
+ expiries = [
7252
+ ...expiries,
7253
+ now + removalDelay
7254
+ ];
7255
+ decorations2 = decorations2.update({
7256
+ add: add.map(({ from, to }) => Decoration14.mark({
7257
+ class: "cm-fader"
7258
+ }).range(from, to))
7259
+ });
7260
+ }
7261
+ }
7262
+ log12(expiries);
7263
+ return {
7264
+ decorations: decorations2,
7265
+ expiries,
7266
+ batchStart
7267
+ };
7268
+ },
7269
+ provide: (f) => EditorView29.decorations.from(f, (value) => value.decorations)
7270
+ });
7271
+ const cleanup = ViewPlugin20.fromClass(class {
7272
+ view;
7273
+ #timer;
7274
+ constructor(view) {
7275
+ this.view = view;
7276
+ this.#schedule();
7277
+ }
7278
+ update() {
7279
+ this.#schedule();
7280
+ }
7281
+ #schedule() {
7282
+ const { expiries } = this.view.state.field(fadeField);
7283
+ if (expiries.length === 0) {
7284
+ clearTimeout(this.#timer);
7285
+ this.#timer = void 0;
7286
+ return;
7287
+ }
7288
+ if (this.#timer !== void 0) {
7289
+ return;
7290
+ }
7291
+ const delay = Math.max(CLEANUP_INTERVAL, expiries[0] - Date.now());
7292
+ this.#timer = setTimeout(() => {
7293
+ this.#timer = void 0;
7294
+ this.view.dispatch({
7295
+ effects: dequeue.of(Date.now())
7296
+ });
7297
+ }, delay);
7298
+ }
7299
+ destroy() {
7300
+ clearTimeout(this.#timer);
7301
+ }
7302
+ });
7303
+ return [
7304
+ fadeField,
7305
+ cleanup,
7306
+ EditorView29.theme({
7307
+ ".cm-fader": {
7308
+ animation: "fader 1s ease-out forwards"
7309
+ },
7310
+ "@keyframes fader": {
7311
+ "0%": {
7312
+ textShadow: "0 0 16px rgba(100, 200, 255, 1), 0 0 32px rgba(100, 200, 255, 0.6)"
7313
+ },
7314
+ "100%": {}
7315
+ }
7316
+ })
7317
+ ];
7318
+ };
7319
+
7320
+ // src/extensions/tags/typewriter.ts
7321
+ import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7322
+ import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7323
+ import { Domino as Domino3 } from "@dxos/ui";
7324
+ var typewriterBypass = Annotation3.define();
7325
+ var typewriterDrainingEffect = StateEffect11.define();
7326
+ var CURSOR_LINGER = 3e3;
7327
+ var FRAME_BUDGET_MS = 4;
7328
+ var CHARS_PER_FRAME = 5;
7329
+ var FLUSH_THRESHOLD = 2e3;
7330
+ var COMPACT_HEAD_THRESHOLD = 4096;
7331
+ var typewriter = (options = {}) => {
7332
+ const streamingTags = options.streamingTags ?? /* @__PURE__ */ new Set();
7333
+ const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;
7334
+ const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;
7335
+ const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;
7336
+ const suppressAppend = StateEffect11.define();
7337
+ const insertChunk = StateEffect11.define();
7338
+ const bufferField = StateField13.define({
7339
+ create: () => ({
7340
+ text: "",
7341
+ head: 0,
7342
+ insertAt: 0
7343
+ }),
7344
+ update: (value, tr) => {
7345
+ let { text, head, insertAt } = value;
7346
+ for (const effect of tr.effects) {
7347
+ if (effect.is(suppressAppend)) {
7348
+ if (text.length === head) {
7349
+ insertAt = effect.value.from;
7350
+ }
7351
+ text += effect.value.text;
7352
+ }
7353
+ if (effect.is(insertChunk)) {
7354
+ head += effect.value.text.length;
7355
+ insertAt = effect.value.from + effect.value.text.length;
7356
+ if (head >= COMPACT_HEAD_THRESHOLD || head > 0 && head * 2 >= text.length) {
7357
+ text = text.slice(head);
7358
+ head = 0;
7359
+ }
7360
+ }
7361
+ }
7362
+ if (tr.docChanged) {
7363
+ let isReset = tr.state.doc.length === 0;
7364
+ if (!isReset && tr.startState.doc.length > 0) {
7365
+ tr.changes.iterChanges((fromA, toA) => {
7366
+ if (fromA === 0 && toA === tr.startState.doc.length) {
7367
+ isReset = true;
7368
+ }
7369
+ });
7370
+ }
7371
+ if (isReset) {
7372
+ return {
7373
+ text: "",
7374
+ head: 0,
7375
+ insertAt: 0
7376
+ };
7377
+ }
7378
+ if (!tr.effects.some((effect) => effect.is(insertChunk))) {
7379
+ insertAt = tr.changes.mapPos(Math.min(insertAt, tr.startState.doc.length));
7380
+ }
7381
+ }
7382
+ return {
7383
+ text,
7384
+ head,
7385
+ insertAt
7386
+ };
7387
+ }
7388
+ });
7389
+ const filter = EditorState3.transactionFilter.of((tr) => {
7390
+ if (!tr.docChanged) {
7391
+ return tr;
7392
+ }
7393
+ if (tr.annotation(typewriterBypass) || tr.effects.some((effect) => effect.is(insertChunk))) {
7394
+ return tr;
7395
+ }
7396
+ let appendedText = "";
7397
+ let appendFrom = -1;
7398
+ let isAppendOnly = true;
7399
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
7400
+ if (toA === tr.startState.doc.length && fromA === toA && inserted.length > 0) {
7401
+ appendedText += inserted.sliceString(0);
7402
+ if (appendFrom === -1) {
7403
+ appendFrom = fromA;
7404
+ }
7405
+ } else {
7406
+ isAppendOnly = false;
7407
+ }
7408
+ });
7409
+ if (!isAppendOnly || appendedText.length === 0) {
7410
+ return tr;
7411
+ }
7412
+ return {
7413
+ changes: ChangeSet2.empty(tr.startState.doc.length),
7414
+ effects: suppressAppend.of({
7415
+ from: appendFrom,
7416
+ text: appendedText
7417
+ })
7418
+ };
7419
+ });
7420
+ const drainPlugin = ViewPlugin21.fromClass(class {
7421
+ view;
7422
+ _raf;
7423
+ _activeStreamTag = null;
7424
+ constructor(view) {
7425
+ this.view = view;
7426
+ }
7427
+ update(update2) {
7428
+ const { text, head } = update2.state.field(bufferField);
7429
+ const pending = text.length - head;
7430
+ if (pending === 0) {
7431
+ this._activeStreamTag = null;
7432
+ }
7433
+ if (pending > 0 && this._raf === void 0) {
7434
+ this._start();
7435
+ }
7436
+ }
7437
+ _start() {
7438
+ queueMicrotask(() => {
7439
+ this.view.dispatch({
7440
+ effects: typewriterDrainingEffect.of(true),
7441
+ annotations: typewriterBypass.of(true)
7442
+ });
7443
+ });
7444
+ this._raf = requestAnimationFrame(this._tick);
7445
+ }
7446
+ _tick = () => {
7447
+ const { text, head, insertAt } = this.view.state.field(bufferField);
7448
+ const pending = text.length - head;
7449
+ if (pending === 0) {
7450
+ this.view.dispatch({
7451
+ effects: typewriterDrainingEffect.of(false),
7452
+ annotations: typewriterBypass.of(true)
7453
+ });
7454
+ this._raf = void 0;
7455
+ return;
7456
+ }
7457
+ if (pending > flushThreshold) {
7458
+ const chunk = text.slice(head);
7459
+ this._activeStreamTag = null;
7460
+ this.view.dispatch({
7461
+ changes: {
7462
+ from: insertAt,
7463
+ insert: chunk
7464
+ },
7465
+ effects: insertChunk.of({
7466
+ from: insertAt,
7467
+ text: chunk
7468
+ })
7469
+ });
7470
+ this._raf = requestAnimationFrame(this._tick);
7471
+ return;
7472
+ }
7473
+ const startTime = performance.now();
7474
+ let pos = head;
7475
+ let activeTag = this._activeStreamTag;
7476
+ let charsEmitted = 0;
7477
+ while (pos < text.length && performance.now() - startTime < frameBudgetMs) {
7478
+ const result = flushable(text, pos, streamingTags, activeTag);
7479
+ if (result.count === 0) {
7480
+ break;
7481
+ }
7482
+ if (charsEmitted > 0 && charsEmitted + result.count > charsPerFrame) {
7483
+ break;
7484
+ }
7485
+ if (result.enterTag) {
7486
+ activeTag = result.enterTag;
7487
+ }
7488
+ if (result.exitTag) {
7489
+ activeTag = null;
7490
+ }
7491
+ pos += result.count;
7492
+ charsEmitted += result.count;
7493
+ }
7494
+ const totalCount = pos - head;
7495
+ if (totalCount > 0) {
7496
+ const chunk = text.slice(head, head + totalCount);
7497
+ this._activeStreamTag = activeTag;
7498
+ this.view.dispatch({
7499
+ changes: {
7500
+ from: insertAt,
7501
+ insert: chunk
7502
+ },
7503
+ effects: insertChunk.of({
7504
+ from: insertAt,
7505
+ text: chunk
7506
+ })
7507
+ });
7508
+ }
7509
+ this._raf = requestAnimationFrame(this._tick);
7510
+ };
7511
+ destroy() {
7512
+ if (this._raf !== void 0) {
7513
+ cancelAnimationFrame(this._raf);
6957
7514
  }
6958
7515
  }
6959
- return null;
6960
7516
  });
6961
- };
6962
-
6963
- // src/extensions/tags/streamer.ts
6964
- import { StateEffect as StateEffect8, StateField as StateField12 } from "@codemirror/state";
6965
- import { Decoration as Decoration14, EditorView as EditorView28, ViewPlugin as ViewPlugin18, WidgetType as WidgetType9 } from "@codemirror/view";
6966
- import { Domino as Domino3 } from "@dxos/ui";
6967
- import { isTruthy as isTruthy4 } from "@dxos/util";
6968
- var BLINK_RATE = 2e3;
6969
- var streamer = (options = {}) => {
6970
7517
  return [
6971
- options.cursor && cursor(),
6972
- options.fadeIn && fadeIn(typeof options.fadeIn === "object" ? options.fadeIn : {})
6973
- ].filter(isTruthy4);
6974
- };
6975
- var cursor = () => {
6976
- const hideCursor = StateEffect8.define();
6977
- const showCursor = StateField12.define({
6978
- create: () => true,
7518
+ bufferField,
7519
+ filter,
7520
+ drainPlugin,
7521
+ options.cursor && typewriterCursor(bufferField)
7522
+ ].filter(Boolean);
7523
+ };
7524
+ var typewriterCursor = (bufferField) => {
7525
+ const hideCursor = StateEffect11.define();
7526
+ const visibilityField = StateField13.define({
7527
+ create: () => ({
7528
+ visible: false,
7529
+ insertAt: 0,
7530
+ lastNonWsAt: 0
7531
+ }),
6979
7532
  update: (value, tr) => {
7533
+ const { text, head, insertAt } = tr.state.field(bufferField);
7534
+ const pending = text.length - head;
7535
+ if (pending > 0) {
7536
+ let lastNonWsAt = tr.changes.mapPos(Math.min(value.lastNonWsAt, tr.startState.doc.length));
7537
+ if (tr.docChanged) {
7538
+ tr.changes.iterChanges((_fromA, _toA, _fromB, _toB, inserted) => {
7539
+ const chunk = inserted.sliceString(0);
7540
+ if (chunk.trim().length > 0) {
7541
+ lastNonWsAt = _fromB + chunk.length;
7542
+ }
7543
+ });
7544
+ }
7545
+ return {
7546
+ visible: true,
7547
+ insertAt,
7548
+ lastNonWsAt
7549
+ };
7550
+ }
6980
7551
  for (const effect of tr.effects) {
6981
7552
  if (effect.is(hideCursor)) {
6982
- return false;
7553
+ return {
7554
+ ...value,
7555
+ visible: false
7556
+ };
6983
7557
  }
6984
7558
  }
6985
- if (tr.docChanged) {
6986
- return true;
6987
- }
6988
7559
  return value;
6989
7560
  }
6990
7561
  });
6991
- const timerPlugin = ViewPlugin18.fromClass(class {
7562
+ const decorationField = StateField13.define({
7563
+ create: () => Decoration15.none,
7564
+ update: (_decorations, tr) => {
7565
+ const { visible, insertAt, lastNonWsAt } = tr.state.field(visibilityField);
7566
+ if (!visible) {
7567
+ return Decoration15.none;
7568
+ }
7569
+ const { text, head } = tr.state.field(bufferField);
7570
+ const cursorAt = text.length > head ? insertAt : lastNonWsAt;
7571
+ const pos = Math.min(cursorAt, tr.state.doc.length);
7572
+ return Decoration15.set([
7573
+ Decoration15.widget({
7574
+ widget: new CursorWidget(),
7575
+ side: 1
7576
+ }).range(pos)
7577
+ ]);
7578
+ },
7579
+ provide: (field) => EditorView30.decorations.from(field)
7580
+ });
7581
+ const timerPlugin = ViewPlugin21.fromClass(class {
6992
7582
  view;
6993
- timer;
7583
+ _timer;
6994
7584
  constructor(view) {
6995
7585
  this.view = view;
6996
7586
  }
6997
7587
  update(update2) {
6998
- if (update2.docChanged) {
6999
- clearTimeout(this.timer);
7000
- this.timer = setTimeout(() => {
7588
+ const { text, head } = update2.state.field(bufferField);
7589
+ const { visible } = update2.state.field(visibilityField);
7590
+ const pending = text.length - head;
7591
+ if (pending > 0) {
7592
+ clearTimeout(this._timer);
7593
+ this._timer = void 0;
7594
+ } else if (visible && this._timer === void 0) {
7595
+ this._timer = setTimeout(() => {
7001
7596
  this.view.dispatch({
7002
7597
  effects: hideCursor.of(null)
7003
7598
  });
7004
- }, BLINK_RATE);
7599
+ this._timer = void 0;
7600
+ }, CURSOR_LINGER);
7005
7601
  }
7006
7602
  }
7007
7603
  destroy() {
7008
- clearTimeout(this.timer);
7604
+ clearTimeout(this._timer);
7009
7605
  }
7010
7606
  });
7011
- const cursorDecoration = StateField12.define({
7012
- create: () => Decoration14.none,
7013
- update: (_decorations, tr) => {
7014
- const show = tr.state.field(showCursor);
7015
- if (!show) {
7016
- return Decoration14.none;
7017
- }
7018
- const endPos = tr.state.doc.length;
7019
- return Decoration14.set([
7020
- Decoration14.widget({
7021
- widget: new CursorWidget(),
7022
- side: 1
7023
- }).range(endPos)
7024
- ]);
7025
- },
7026
- provide: (f) => EditorView28.decorations.from(f)
7027
- });
7028
7607
  return [
7029
- showCursor,
7030
- timerPlugin,
7031
- cursorDecoration
7608
+ visibilityField,
7609
+ decorationField,
7610
+ timerPlugin
7032
7611
  ];
7033
7612
  };
7034
- var CursorWidget = class extends WidgetType9 {
7613
+ var CursorWidget = class _CursorWidget extends WidgetType9 {
7614
+ // All instances are interchangeable — let CM reuse the existing DOM across drips so
7615
+ // the blink animation isn't restarted on every transaction.
7616
+ eq(other) {
7617
+ return other instanceof _CursorWidget;
7618
+ }
7035
7619
  toDOM() {
7036
- const inner = Domino3.of("span").text("\u258F").style({
7037
- animation: "blink 2s infinite"
7620
+ const inner = Domino3.of("span").text("\u2217").style({
7621
+ animation: "blink 1s infinite",
7622
+ animationDelay: "250ms"
7038
7623
  });
7039
7624
  return Domino3.of("span").style({
7040
7625
  opacity: "0.8"
7041
- }).children(inner).root;
7626
+ }).append(inner).root;
7042
7627
  }
7043
7628
  };
7044
- var fadeIn = (options = {}) => {
7045
- const FADE_IN_DURATION = 1e3;
7046
- const DEFAULT_REMOVAL_DELAY = 3e3;
7047
- const removalDelay = options.removalDelay ?? DEFAULT_REMOVAL_DELAY;
7048
- const removeDecoration = StateEffect8.define();
7049
- const fadeField = StateField12.define({
7050
- create: () => Decoration14.none,
7051
- update: (decorations2, tr) => {
7052
- let next = decorations2;
7053
- for (const effect of tr.effects) {
7054
- if (effect.is(removeDecoration)) {
7055
- const target = effect.value;
7056
- next = next.update({
7057
- filter: (from, to) => !(from === target.from && to === target.to)
7058
- });
7059
- }
7060
- }
7061
- if (!tr.docChanged) {
7062
- return next;
7629
+ var OPENING_TAG_NAME = /^<([a-zA-Z][\w-]*)/;
7630
+ var TAG_NAME_PROBE = 64;
7631
+ var escapeRegExpSource2 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7632
+ var flushable = (buffer, start, streamingTags, activeStreamTag) => {
7633
+ if (start >= buffer.length) {
7634
+ return {
7635
+ count: 0
7636
+ };
7637
+ }
7638
+ if (activeStreamTag) {
7639
+ const closeTag = `</${activeStreamTag}>`;
7640
+ if (buffer.startsWith(closeTag, start)) {
7641
+ return {
7642
+ count: closeTag.length,
7643
+ exitTag: true
7644
+ };
7645
+ }
7646
+ if (buffer[start] === "<") {
7647
+ return {
7648
+ count: xmlElementLength(buffer, start)
7649
+ };
7650
+ }
7651
+ return {
7652
+ count: 1
7653
+ };
7654
+ }
7655
+ const ch = buffer[start];
7656
+ if (ch === "<") {
7657
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7658
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7659
+ if (nameMatch && streamingTags.has(nameMatch[1])) {
7660
+ const close = buffer.indexOf(">", start);
7661
+ if (close === -1) {
7662
+ return {
7663
+ count: 0
7664
+ };
7063
7665
  }
7064
- let isReset = tr.state.doc.length === 0;
7065
- if (!isReset) {
7066
- tr.changes.iterChanges((fromA, toA) => {
7067
- if (fromA === 0 && toA === tr.startState.doc.length) {
7068
- isReset = true;
7069
- }
7070
- });
7666
+ if (buffer[close - 1] === "/") {
7667
+ return {
7668
+ count: close + 1 - start
7669
+ };
7071
7670
  }
7072
- if (isReset) {
7073
- return Decoration14.none;
7671
+ return {
7672
+ count: close + 1 - start,
7673
+ enterTag: nameMatch[1]
7674
+ };
7675
+ }
7676
+ return {
7677
+ count: xmlElementLength(buffer, start)
7678
+ };
7679
+ }
7680
+ if (ch === "!" && buffer.length > start + 1 && buffer[start + 1] === "[") {
7681
+ return {
7682
+ count: linkLength(buffer, start, start + 1)
7683
+ };
7684
+ }
7685
+ if (ch === "[") {
7686
+ return {
7687
+ count: linkLength(buffer, start, start)
7688
+ };
7689
+ }
7690
+ return {
7691
+ count: 1
7692
+ };
7693
+ };
7694
+ var xmlElementLength = (buffer, start = 0) => {
7695
+ const close = buffer.indexOf(">", start);
7696
+ if (close === -1) {
7697
+ return 0;
7698
+ }
7699
+ if (buffer[close - 1] === "/") {
7700
+ return close + 1 - start;
7701
+ }
7702
+ if (buffer[start + 1] === "/") {
7703
+ return close + 1 - start;
7704
+ }
7705
+ const probe = buffer.slice(start, start + TAG_NAME_PROBE);
7706
+ const nameMatch = probe.match(OPENING_TAG_NAME);
7707
+ if (!nameMatch) {
7708
+ return 1;
7709
+ }
7710
+ const tagName = nameMatch[1];
7711
+ let depth = 0;
7712
+ const tagPattern = new RegExp(`<(/?)${escapeRegExpSource2(tagName)}(\\s[^>]*)?>`, "g");
7713
+ tagPattern.lastIndex = start;
7714
+ let match;
7715
+ while ((match = tagPattern.exec(buffer)) !== null) {
7716
+ const isSelfClosing = match[0].endsWith("/>");
7717
+ const isClosing = match[1] === "/";
7718
+ if (isSelfClosing) {
7719
+ if (depth === 0) {
7720
+ return match.index + match[0].length - start;
7721
+ }
7722
+ } else if (isClosing) {
7723
+ depth--;
7724
+ if (depth === 0) {
7725
+ return match.index + match[0].length - start;
7074
7726
  }
7075
- const add = [];
7076
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7077
- if (fromB === 0 && toB === inserted.length) {
7727
+ } else {
7728
+ depth++;
7729
+ }
7730
+ }
7731
+ return 0;
7732
+ };
7733
+ var linkLength = (buffer, start, bracketAt) => {
7734
+ const bracketClose = buffer.indexOf("]", bracketAt + 1);
7735
+ if (bracketClose === -1) {
7736
+ return 0;
7737
+ }
7738
+ if (bracketClose + 1 >= buffer.length) {
7739
+ return 0;
7740
+ }
7741
+ if (buffer[bracketClose + 1] !== "(") {
7742
+ return 1;
7743
+ }
7744
+ const parenClose = buffer.indexOf(")", bracketClose + 2);
7745
+ if (parenClose === -1) {
7746
+ return 0;
7747
+ }
7748
+ return parenClose + 1 - start;
7749
+ };
7750
+
7751
+ // src/extensions/tags/xml-block-decoration.ts
7752
+ import { xmlLanguage as xmlLanguage2 } from "@codemirror/lang-xml";
7753
+ import { Decoration as Decoration16, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7754
+ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7755
+ const lineDecoration = lineClass ? Decoration16.line({
7756
+ class: lineClass
7757
+ }) : void 0;
7758
+ const contentDecoration = contentClass ? Decoration16.mark({
7759
+ class: contentClass
7760
+ }) : void 0;
7761
+ const hideDecoration = hideTags ? Decoration16.replace({}) : void 0;
7762
+ const buildDecorations5 = (view) => {
7763
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7764
+ if (!text.includes(`<${tag}`)) {
7765
+ return Decoration16.none;
7766
+ }
7767
+ const tree = xmlLanguage2.parser.parse(text);
7768
+ const ranges = [];
7769
+ tree.iterate({
7770
+ enter: (node) => {
7771
+ if (node.type.name !== "Element") {
7078
7772
  return;
7079
7773
  }
7080
- if (toA === tr.startState.doc.length && inserted.length > 0) {
7081
- add.push(Decoration14.mark({
7082
- class: "cm-fade-in"
7083
- }).range(fromB, toB));
7774
+ const openTag = node.node.getChild("OpenTag");
7775
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7776
+ const tagNameNode = openTag?.getChild("TagName");
7777
+ if (!openTag || !tagNameNode) {
7778
+ return;
7084
7779
  }
7085
- });
7086
- return next.update({
7087
- add
7088
- });
7089
- },
7090
- provide: (f) => EditorView28.decorations.from(f)
7091
- });
7092
- const timerPlugin = ViewPlugin18.fromClass(class {
7093
- view;
7094
- // Map a simple key "from-to" to timer id.
7095
- _timers = /* @__PURE__ */ new Map();
7780
+ if (text.slice(tagNameNode.from, tagNameNode.to) !== tag) {
7781
+ return;
7782
+ }
7783
+ const contentFrom = openTag.to;
7784
+ const contentTo = closeTag?.from ?? node.node.to;
7785
+ if (hideDecoration) {
7786
+ ranges.push(hideDecoration.range(openTag.from, openTag.to));
7787
+ if (closeTag) {
7788
+ ranges.push(hideDecoration.range(closeTag.from, closeTag.to));
7789
+ }
7790
+ }
7791
+ if (contentDecoration && contentFrom < contentTo) {
7792
+ ranges.push(contentDecoration.range(contentFrom, contentTo));
7793
+ }
7794
+ if (lineDecoration && contentFrom <= view.state.doc.length) {
7795
+ let pos = contentFrom;
7796
+ while (pos <= contentTo && pos <= view.state.doc.length) {
7797
+ const line = view.state.doc.lineAt(pos);
7798
+ ranges.push(lineDecoration.range(line.from));
7799
+ if (line.to >= contentTo) {
7800
+ break;
7801
+ }
7802
+ pos = line.to + 1;
7803
+ }
7804
+ }
7805
+ }
7806
+ });
7807
+ return Decoration16.set(ranges, true);
7808
+ };
7809
+ return ViewPlugin22.fromClass(class {
7810
+ decorations;
7096
7811
  constructor(view) {
7097
- this.view = view;
7812
+ this.decorations = buildDecorations5(view);
7098
7813
  }
7099
7814
  update(update2) {
7100
- if (!update2.docChanged) {
7101
- return;
7815
+ if (update2.docChanged) {
7816
+ this.decorations = buildDecorations5(update2.view);
7102
7817
  }
7103
- update2.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7104
- if (toA !== update2.startState.doc.length || inserted.length === 0) {
7818
+ }
7819
+ }, {
7820
+ decorations: (instance) => instance.decorations
7821
+ });
7822
+ };
7823
+
7824
+ // src/extensions/tags/xml-formatting.ts
7825
+ import { xmlLanguage as xmlLanguage3 } from "@codemirror/lang-xml";
7826
+ import { Decoration as Decoration17, EditorView as EditorView31, ViewPlugin as ViewPlugin23 } from "@codemirror/view";
7827
+ var XML_TAG_NODES = /* @__PURE__ */ new Set([
7828
+ "OpenTag",
7829
+ "CloseTag",
7830
+ "SelfClosingTag",
7831
+ "MismatchedCloseTag"
7832
+ ]);
7833
+ var xmlElementMark = Decoration17.mark({
7834
+ class: "cm-xml-element"
7835
+ });
7836
+ var xmlTagMark = Decoration17.mark({
7837
+ class: "cm-xml-tag"
7838
+ });
7839
+ var xmlContentMark = Decoration17.mark({
7840
+ class: "cm-xml-content"
7841
+ });
7842
+ var xmlFormatting = ({ skip } = {}) => {
7843
+ const skipSet = skip && skip.length > 0 ? new Set(skip) : void 0;
7844
+ const buildDecorations5 = (view) => {
7845
+ const text = view.state.sliceDoc(0, view.state.doc.length);
7846
+ if (!text.includes("<")) {
7847
+ return Decoration17.none;
7848
+ }
7849
+ const tagNameAt = (node) => text.slice(node.from, node.to);
7850
+ const tree = xmlLanguage3.parser.parse(text);
7851
+ const ranges = [];
7852
+ tree.iterate({
7853
+ enter: (node) => {
7854
+ const name = node.type.name;
7855
+ if (name === "SelfClosingTag" && node.from < node.to) {
7856
+ if (skipSet) {
7857
+ const tagNameNode = node.node.getChild("TagName");
7858
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7859
+ return false;
7860
+ }
7861
+ }
7862
+ ranges.push(xmlElementMark.range(node.from, node.to));
7863
+ ranges.push(xmlTagMark.range(node.from, node.to));
7105
7864
  return;
7106
7865
  }
7107
- const key = `${fromB}-${toB}`;
7108
- if (this._timers.has(key)) {
7109
- clearTimeout(this._timers.get(key));
7866
+ if (XML_TAG_NODES.has(name) && node.from < node.to) {
7867
+ ranges.push(xmlTagMark.range(node.from, node.to));
7868
+ return;
7869
+ }
7870
+ if (name === "Element" && node.from < node.to) {
7871
+ const openTag = node.node.getChild("OpenTag");
7872
+ if (openTag && skipSet) {
7873
+ const tagNameNode = openTag.getChild("TagName");
7874
+ if (tagNameNode && skipSet.has(tagNameAt(tagNameNode))) {
7875
+ return false;
7876
+ }
7877
+ }
7878
+ const closeTag = node.node.getChild("CloseTag") ?? node.node.getChild("MismatchedCloseTag");
7879
+ ranges.push(xmlElementMark.range(node.from, node.to));
7880
+ if (openTag && closeTag && openTag.to < closeTag.from) {
7881
+ ranges.push(xmlContentMark.range(openTag.to, closeTag.from));
7882
+ }
7110
7883
  }
7111
- const totalDelay = FADE_IN_DURATION + removalDelay;
7112
- const id = setTimeout(() => {
7113
- this.view.dispatch({
7114
- effects: removeDecoration.of({
7115
- from: fromB,
7116
- to: toB
7117
- })
7118
- });
7119
- this._timers.delete(key);
7120
- }, totalDelay);
7121
- this._timers.set(key, id);
7122
- });
7123
- }
7124
- destroy() {
7125
- for (const id of this._timers.values()) {
7126
- clearTimeout(id);
7127
7884
  }
7128
- this._timers.clear();
7129
- }
7130
- });
7885
+ });
7886
+ return Decoration17.set(ranges, true);
7887
+ };
7131
7888
  return [
7132
- fadeField,
7133
- timerPlugin,
7134
- EditorView28.theme({
7135
- ".cm-line > span": {
7136
- opacity: "0.8"
7137
- },
7138
- ".cm-fade-in": {
7139
- animation: "fade-in 3s ease-out forwards"
7140
- },
7141
- "@keyframes fade-in": {
7142
- "0%": {
7143
- opacity: "0"
7144
- },
7145
- "80%": {
7146
- opacity: "1"
7147
- },
7148
- "100%": {
7149
- opacity: "0.8"
7889
+ ViewPlugin23.fromClass(class {
7890
+ decorations;
7891
+ constructor(view) {
7892
+ this.decorations = buildDecorations5(view);
7893
+ }
7894
+ update(update2) {
7895
+ if (update2.docChanged) {
7896
+ this.decorations = buildDecorations5(update2.view);
7150
7897
  }
7151
7898
  }
7899
+ }, {
7900
+ decorations: (instance) => instance.decorations
7901
+ }),
7902
+ EditorView31.baseTheme({
7903
+ ".cm-xml-element": {
7904
+ backgroundColor: "var(--color-current-surface)",
7905
+ borderRadius: "0.25rem",
7906
+ padding: "0.25rem"
7907
+ },
7908
+ ".cm-xml-tag": {
7909
+ color: "var(--color-blue-500)",
7910
+ fontFamily: "var(--font-mono)"
7911
+ },
7912
+ ".cm-xml-content": {}
7152
7913
  })
7153
7914
  ];
7154
7915
  };
7155
7916
 
7156
7917
  // src/extensions/tags/xml-tags.ts
7157
7918
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7158
- import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect9, StateField as StateField13 } from "@codemirror/state";
7159
- import { Decoration as Decoration15, EditorView as EditorView29, ViewPlugin as ViewPlugin19, WidgetType as WidgetType10, keymap as keymap13 } from "@codemirror/view";
7919
+ import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7920
+ import { Decoration as Decoration18, EditorView as EditorView32, ViewPlugin as ViewPlugin24, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
7160
7921
  import { invariant as invariant7 } from "@dxos/invariant";
7161
7922
  import { log as log11 } from "@dxos/log";
7923
+ import { Domino as Domino4 } from "@dxos/ui";
7162
7924
 
7163
7925
  // src/extensions/tags/xml-util.ts
7164
7926
  import { invariant as invariant6 } from "@dxos/invariant";
7165
7927
  var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7166
7928
  var nodeToJson = (state, node) => {
7167
- invariant6(node.type.name === "Element", "Node is not an Element", {
7168
- F: __dxlog_file16,
7169
- L: 18,
7170
- S: void 0,
7171
- A: [
7172
- "node.type.name === 'Element'",
7173
- "'Node is not an Element'"
7174
- ]
7175
- });
7929
+ 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'"] });
7176
7930
  const openTag = node.node.getChild("OpenTag") || node.node.getChild("SelfClosingTag");
7177
7931
  if (openTag) {
7178
7932
  const tagName = openTag.getChild("TagName");
@@ -7204,13 +7958,23 @@ var nodeToJson = (state, node) => {
7204
7958
  if (node.type.name === "Element" && openTag.type.name !== "SelfClosingTag") {
7205
7959
  const children = [];
7206
7960
  let child = node.node.firstChild;
7961
+ const appendText = (raw) => {
7962
+ if (raw.length === 0) {
7963
+ return;
7964
+ }
7965
+ const last = children[children.length - 1];
7966
+ if (typeof last === "string") {
7967
+ children[children.length - 1] = last + raw;
7968
+ } else {
7969
+ children.push(raw);
7970
+ }
7971
+ };
7207
7972
  while (child) {
7208
7973
  if (child.type.name !== "OpenTag" && child.type.name !== "CloseTag") {
7209
7974
  if (child.type.name === "Text") {
7210
- const text = state.doc.sliceString(child.from, child.to).trim();
7211
- if (text) {
7212
- children.push(text);
7213
- }
7975
+ appendText(state.doc.sliceString(child.from, child.to));
7976
+ } else if (child.type.name === "EntityReference" || child.type.name === "CharacterReference") {
7977
+ appendText(decodeXmlEntity(state.doc.sliceString(child.from, child.to)));
7214
7978
  } else if (child.type.name === "Element") {
7215
7979
  const data = nodeToJson(state, child);
7216
7980
  if (data) {
@@ -7220,26 +7984,63 @@ var nodeToJson = (state, node) => {
7220
7984
  }
7221
7985
  child = child.nextSibling;
7222
7986
  }
7987
+ if (children.length > 0 && typeof children[0] === "string") {
7988
+ children[0] = children[0].trimStart();
7989
+ }
7223
7990
  if (children.length > 0) {
7224
- tag.children = children;
7991
+ const lastIndex = children.length - 1;
7992
+ const last = children[lastIndex];
7993
+ if (typeof last === "string") {
7994
+ children[lastIndex] = last.trimEnd();
7995
+ }
7996
+ }
7997
+ const trimmed = children.filter((value) => typeof value !== "string" || value.length > 0);
7998
+ if (trimmed.length > 0) {
7999
+ tag.children = trimmed;
7225
8000
  }
7226
8001
  }
7227
8002
  return tag;
7228
8003
  }
7229
8004
  };
8005
+ var XML_NAMED_ENTITIES = {
8006
+ "&lt;": "<",
8007
+ "&gt;": ">",
8008
+ "&amp;": "&",
8009
+ "&quot;": '"',
8010
+ "&apos;": "'"
8011
+ };
8012
+ var decodeXmlEntity = (raw) => {
8013
+ const named = XML_NAMED_ENTITIES[raw];
8014
+ if (named !== void 0) {
8015
+ return named;
8016
+ }
8017
+ const numeric = /^&#(x?)([0-9a-fA-F]+);$/.exec(raw);
8018
+ if (numeric) {
8019
+ const code = parseInt(numeric[2], numeric[1] ? 16 : 10);
8020
+ if (Number.isFinite(code)) {
8021
+ try {
8022
+ return String.fromCodePoint(code);
8023
+ } catch {
8024
+ }
8025
+ }
8026
+ }
8027
+ return raw;
8028
+ };
7230
8029
 
7231
8030
  // src/extensions/tags/xml-tags.ts
7232
8031
  var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7233
- var navigatePreviousEffect = StateEffect9.define();
7234
- var navigateNextEffect = StateEffect9.define();
8032
+ var navigatePreviousEffect = StateEffect12.define();
8033
+ var navigateNextEffect = StateEffect12.define();
7235
8034
  var getXmlTextChild = (children) => {
7236
8035
  const child = children?.[0];
7237
8036
  return typeof child === "string" ? child : null;
7238
8037
  };
7239
- var xmlTagContextEffect = StateEffect9.define();
7240
- var xmlTagResetEffect = StateEffect9.define();
7241
- var xmlTagUpdateEffect = StateEffect9.define();
7242
- var widgetContextStateField = StateField13.define({
8038
+ var xmlWidgetId = (explicit, fallback) => typeof explicit === "string" && explicit.length > 0 ? explicit : fallback;
8039
+ var escapeRegExpSource3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8040
+ var xmlTagContextEffect = StateEffect12.define();
8041
+ var xmlTagResetEffect = StateEffect12.define();
8042
+ var xmlTagUpdateEffect = StateEffect12.define();
8043
+ var widgetContextStateField = StateField14.define({
7243
8044
  create: () => void 0,
7244
8045
  update: (value, tr) => {
7245
8046
  for (const effect of tr.effects) {
@@ -7250,7 +8051,7 @@ var widgetContextStateField = StateField13.define({
7250
8051
  return value;
7251
8052
  }
7252
8053
  });
7253
- var widgetStateMapStateField = StateField13.define({
8054
+ var widgetStateMapStateField = StateField14.define({
7254
8055
  create: () => ({}),
7255
8056
  update: (map, tr) => {
7256
8057
  for (const effect of tr.effects) {
@@ -7262,12 +8063,7 @@ var widgetStateMapStateField = StateField13.define({
7262
8063
  log11("widget updated", {
7263
8064
  id,
7264
8065
  value
7265
- }, {
7266
- F: __dxlog_file17,
7267
- L: 153,
7268
- S: void 0,
7269
- C: (f, a) => f(...a)
7270
- });
8066
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 59, S: void 0 });
7271
8067
  const state = typeof value === "function" ? value(map[id]) : value;
7272
8068
  return {
7273
8069
  ...map,
@@ -7297,12 +8093,7 @@ var createWidgetMap = (setWidgets) => {
7297
8093
  log11("widget mounted", {
7298
8094
  id: state.id,
7299
8095
  tag: state.props._tag
7300
- }, {
7301
- F: __dxlog_file17,
7302
- L: 206,
7303
- S: void 0,
7304
- C: (f, a) => f(...a)
7305
- });
8096
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 101, S: void 0 });
7306
8097
  widgets.set(state.id, state);
7307
8098
  setWidgets?.([
7308
8099
  ...widgets.values()
@@ -7313,12 +8104,7 @@ var createWidgetMap = (setWidgets) => {
7313
8104
  log11("widget unmounted", {
7314
8105
  id,
7315
8106
  tag: state?.props._tag
7316
- }, {
7317
- F: __dxlog_file17,
7318
- L: 212,
7319
- S: void 0,
7320
- C: (f, a) => f(...a)
7321
- });
8107
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 112, S: void 0 });
7322
8108
  widgets.delete(id);
7323
8109
  setWidgets?.([
7324
8110
  ...widgets.values()
@@ -7327,7 +8113,7 @@ var createWidgetMap = (setWidgets) => {
7327
8113
  };
7328
8114
  return notifier;
7329
8115
  };
7330
- var keyHandlers = keymap13.of([
8116
+ var keyHandlers = keymap14.of([
7331
8117
  {
7332
8118
  key: "Mod-ArrowUp",
7333
8119
  run: (view) => {
@@ -7348,7 +8134,7 @@ var keyHandlers = keymap13.of([
7348
8134
  }
7349
8135
  ]);
7350
8136
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7351
- return EditorView29.updateListener.of((update2) => {
8137
+ return EditorView32.updateListener.of((update2) => {
7352
8138
  update2.transactions.forEach((transaction) => {
7353
8139
  for (const effect of transaction.effects) {
7354
8140
  if (effect.is(navigatePreviousEffect)) {
@@ -7376,7 +8162,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7376
8162
  anchor: line.from,
7377
8163
  head: line.from
7378
8164
  },
7379
- effects: scrollerLineEffect.of({
8165
+ effects: crawlerLineEffect.of({
7380
8166
  line: line.number - 1,
7381
8167
  offset: -16
7382
8168
  })
@@ -7409,7 +8195,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7409
8195
  anchor: line.to,
7410
8196
  head: line.to
7411
8197
  },
7412
- effects: scrollerLineEffect.of({
8198
+ effects: crawlerLineEffect.of({
7413
8199
  line: line.number - 1,
7414
8200
  offset: -16
7415
8201
  })
@@ -7421,7 +8207,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7421
8207
  anchor: line.to,
7422
8208
  head: line.to
7423
8209
  },
7424
- effects: scrollerLineEffect.of({
8210
+ effects: crawlerLineEffect.of({
7425
8211
  line: line.number - 1,
7426
8212
  position: "end"
7427
8213
  })
@@ -7433,7 +8219,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7433
8219
  });
7434
8220
  });
7435
8221
  };
7436
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin19.fromClass(class {
8222
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
7437
8223
  update(update2) {
7438
8224
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
7439
8225
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -7460,14 +8246,14 @@ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin1
7460
8246
  }
7461
8247
  }
7462
8248
  });
7463
- var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.define({
8249
+ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.define({
7464
8250
  create: (state) => {
7465
8251
  return buildDecorations4(state, {
7466
8252
  from: 0,
7467
8253
  to: state.doc.length
7468
8254
  }, registry, notifier);
7469
8255
  },
7470
- update: ({ from, decorations: decorations2 }, tr) => {
8256
+ update: ({ from, streamingFrom, decorations: decorations2 }, tr) => {
7471
8257
  for (const effect of tr.effects) {
7472
8258
  if (effect.is(xmlTagResetEffect)) {
7473
8259
  if (tr.docChanged) {
@@ -7478,7 +8264,7 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7478
8264
  }
7479
8265
  return {
7480
8266
  from: 0,
7481
- decorations: Decoration15.none
8267
+ decorations: Decoration18.none
7482
8268
  };
7483
8269
  }
7484
8270
  }
@@ -7489,24 +8275,23 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7489
8275
  log11("document reset", {
7490
8276
  from,
7491
8277
  to: state.doc.length
7492
- }, {
7493
- F: __dxlog_file17,
7494
- L: 374,
7495
- S: void 0,
7496
- C: (f, a) => f(...a)
7497
- });
8278
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 298, S: void 0 });
7498
8279
  return buildDecorations4(state, {
7499
8280
  from: 0,
7500
8281
  to: state.doc.length
7501
8282
  }, registry, notifier);
7502
8283
  } else {
8284
+ const rebuildFrom = streamingFrom ?? from;
7503
8285
  const result = buildDecorations4(state, {
7504
- from,
8286
+ from: rebuildFrom,
7505
8287
  to: state.doc.length
7506
8288
  }, registry, notifier);
7507
8289
  return {
7508
8290
  from: result.from,
8291
+ streamingFrom: result.streamingFrom,
7509
8292
  decorations: decorations2.update({
8293
+ // Remove old streaming decorations — they are rebuilt each tick.
8294
+ filter: (_f, _t, deco) => !deco.spec.streaming,
7510
8295
  add: decorationSetToArray(result.decorations)
7511
8296
  })
7512
8297
  };
@@ -7514,12 +8299,13 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7514
8299
  }
7515
8300
  return {
7516
8301
  from,
8302
+ streamingFrom,
7517
8303
  decorations: decorations2
7518
8304
  };
7519
8305
  },
7520
8306
  provide: (field) => [
7521
- EditorView29.decorations.from(field, (v) => v.decorations),
7522
- EditorView29.atomicRanges.of((view) => view.state.field(field).decorations || Decoration15.none)
8307
+ EditorView32.decorations.from(field, (v) => v.decorations),
8308
+ EditorView32.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
7523
8309
  ]
7524
8310
  });
7525
8311
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -7530,10 +8316,11 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7530
8316
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
7531
8317
  return {
7532
8318
  from: range.from,
7533
- decorations: Decoration15.none
8319
+ decorations: Decoration18.none
7534
8320
  };
7535
8321
  }
7536
8322
  let last = range.from;
8323
+ let streamingFrom;
7537
8324
  tree.iterate({
7538
8325
  from: range.from,
7539
8326
  to: range.to,
@@ -7546,21 +8333,26 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7546
8333
  if (args) {
7547
8334
  const def = registry[args._tag];
7548
8335
  if (def) {
8336
+ if (def.streaming && !node.node.getChild("CloseTag")) {
8337
+ return false;
8338
+ }
7549
8339
  const { block, factory, Component } = def;
7550
- const widgetState = args.id ? widgetStateMap[args.id] : void 0;
7551
8340
  const nodeRange = {
7552
8341
  from: node.node.from,
7553
8342
  to: node.node.to
7554
8343
  };
8344
+ const widgetId = xmlWidgetId(args.id, def.streaming ? `cm-xml-${nodeRange.from}` : `cm-xml-${nodeRange.from}-${nodeRange.to}`);
8345
+ const widgetState = widgetStateMap[widgetId];
7555
8346
  const props = {
7556
- context,
8347
+ id: widgetId,
7557
8348
  range: nodeRange,
8349
+ context,
7558
8350
  ...args,
7559
8351
  ...widgetState
7560
8352
  };
7561
- const widget = factory ? factory(props) : Component ? args.id && new PlaceholderWidget2(args.id, Component, props, notifier) : void 0;
8353
+ const widget = factory ? factory(props) ?? void 0 : Component ? new PlaceholderWidget2(widgetId, Component, props, notifier) : void 0;
7562
8354
  if (widget) {
7563
- builder.add(nodeRange.from, nodeRange.to, Decoration15.replace({
8355
+ builder.add(nodeRange.from, nodeRange.to, Decoration18.replace({
7564
8356
  widget,
7565
8357
  block,
7566
8358
  atomic: true,
@@ -7572,20 +8364,72 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7572
8364
  }
7573
8365
  }
7574
8366
  } catch (err) {
7575
- log11.catch(err, void 0, {
7576
- F: __dxlog_file17,
7577
- L: 459,
7578
- S: void 0,
7579
- C: (f, a) => f(...a)
7580
- });
8367
+ log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 401, S: void 0 });
7581
8368
  }
7582
8369
  return false;
7583
8370
  }
7584
8371
  }
7585
8372
  }
7586
8373
  });
8374
+ const streamingTagNames = Object.entries(registry).filter(([, def]) => def.streaming).map(([name]) => name).sort((a, b) => b.length - a.length);
8375
+ if (streamingTagNames.length > 0) {
8376
+ const tailText = state.sliceDoc(range.from, range.to);
8377
+ const streamingPattern = streamingTagNames.map(escapeRegExpSource3).join("|");
8378
+ const tagPattern = new RegExp(`<(${streamingPattern})(\\s[^>]*)?>`, "g");
8379
+ let match;
8380
+ while ((match = tagPattern.exec(tailText)) !== null) {
8381
+ const tagName = match[1];
8382
+ const closeTag = `</${tagName}>`;
8383
+ const afterOpen = match.index + match[0].length;
8384
+ if (tailText.indexOf(closeTag, afterOpen) === -1) {
8385
+ const absoluteFrom = range.from + match.index;
8386
+ const contentFrom = range.from + afterOpen;
8387
+ const innerText = state.sliceDoc(contentFrom, range.to).trim();
8388
+ const def = registry[tagName];
8389
+ const props = {
8390
+ _tag: tagName,
8391
+ context,
8392
+ range: {
8393
+ from: absoluteFrom,
8394
+ to: range.to
8395
+ },
8396
+ children: innerText ? [
8397
+ innerText
8398
+ ] : void 0
8399
+ };
8400
+ const attrPattern = /(\w+)="([^"]*)"/g;
8401
+ let attrMatch;
8402
+ while ((attrMatch = attrPattern.exec(match[0])) !== null) {
8403
+ props[attrMatch[1]] = attrMatch[2];
8404
+ }
8405
+ const widgetId = xmlWidgetId(props.id, `cm-xml-${absoluteFrom}`);
8406
+ const widgetState = widgetStateMap[widgetId];
8407
+ const mergedProps = {
8408
+ ...props,
8409
+ id: widgetId,
8410
+ ...widgetState
8411
+ };
8412
+ const widget = def.factory ? def.factory(mergedProps) ?? void 0 : def.Component ? new PlaceholderWidget2(widgetId, def.Component, mergedProps, notifier, true) : void 0;
8413
+ if (widget) {
8414
+ builder.add(absoluteFrom, range.to, Decoration18.replace({
8415
+ widget,
8416
+ block: def.block,
8417
+ atomic: true,
8418
+ inclusive: true,
8419
+ tag: tagName,
8420
+ streaming: true,
8421
+ contentFrom
8422
+ }));
8423
+ streamingFrom = absoluteFrom;
8424
+ last = absoluteFrom;
8425
+ }
8426
+ break;
8427
+ }
8428
+ }
8429
+ }
7587
8430
  return {
7588
8431
  from: last,
8432
+ streamingFrom,
7589
8433
  decorations: builder.finish()
7590
8434
  };
7591
8435
  };
@@ -7594,108 +8438,62 @@ var PlaceholderWidget2 = class extends WidgetType10 {
7594
8438
  Component;
7595
8439
  props;
7596
8440
  notifier;
7597
- _root = null;
7598
- constructor(id, Component, props, notifier) {
7599
- super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier;
7600
- invariant7(id, void 0, {
7601
- F: __dxlog_file17,
7602
- L: 485,
7603
- S: this,
7604
- A: [
7605
- "id",
7606
- ""
7607
- ]
7608
- });
8441
+ streaming;
8442
+ #root = null;
8443
+ #view;
8444
+ constructor(id, Component, props, notifier, streaming) {
8445
+ super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier, this.streaming = streaming;
8446
+ invariant7(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 488, S: this, A: ["id", ""] });
7609
8447
  }
7610
8448
  get root() {
7611
- return this._root;
8449
+ return this.#root;
7612
8450
  }
7613
8451
  eq(other) {
8452
+ if (this.streaming) {
8453
+ return false;
8454
+ }
7614
8455
  return this.id === other.id;
7615
8456
  }
7616
8457
  ignoreEvent() {
7617
8458
  return true;
7618
8459
  }
7619
- toDOM(_view) {
7620
- this._root = document.createElement("span");
8460
+ toDOM(view) {
8461
+ this.#view = view;
8462
+ this.#root = Domino4.of("div").classNames("min-h-[24px]").root;
8463
+ const props = Object.assign({}, this.props, {
8464
+ view
8465
+ });
8466
+ this.notifier.mounted({
8467
+ id: this.id,
8468
+ root: this.#root,
8469
+ props,
8470
+ Component: this.Component
8471
+ });
8472
+ return this.#root;
8473
+ }
8474
+ updateDOM(dom) {
8475
+ this.#root = dom;
8476
+ const props = Object.assign({}, this.props, {
8477
+ view: this.#view
8478
+ });
7621
8479
  this.notifier.mounted({
7622
8480
  id: this.id,
7623
- root: this._root,
7624
- props: this.props,
8481
+ root: this.#root,
8482
+ props,
7625
8483
  Component: this.Component
7626
8484
  });
7627
- return this._root;
8485
+ return true;
7628
8486
  }
7629
8487
  destroy(_dom) {
7630
8488
  this.notifier.unmounted(this.id);
7631
- this._root = null;
8489
+ this.#root = null;
8490
+ this.#view = void 0;
7632
8491
  }
7633
8492
  };
7634
-
7635
- // src/extensions/typewriter.ts
7636
- import { keymap as keymap14 } from "@codemirror/view";
7637
- var defaultItems = [
7638
- "hello world!",
7639
- "this is a test.",
7640
- "this is [DXOS](https://dxos.org)"
7641
- ];
7642
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
7643
- let t;
7644
- let idx = 0;
7645
- return [
7646
- keymap14.of([
7647
- {
7648
- // Reset.
7649
- key: "alt-meta-'",
7650
- run: () => {
7651
- clearTimeout(t);
7652
- idx = 0;
7653
- return true;
7654
- }
7655
- },
7656
- {
7657
- // Next prompt.
7658
- // TODO(burdon): Press 1-9 to select prompt?
7659
- key: "Shift-Meta-'",
7660
- run: (view) => {
7661
- clearTimeout(t);
7662
- const text = items[idx++];
7663
- if (idx === items?.length) {
7664
- idx = 0;
7665
- }
7666
- let i = 0;
7667
- const insert = (d = 0) => {
7668
- t = setTimeout(() => {
7669
- const pos = view.state.selection.main.head;
7670
- view.dispatch({
7671
- changes: {
7672
- from: pos,
7673
- insert: text[i++]
7674
- },
7675
- selection: {
7676
- anchor: pos + 1
7677
- }
7678
- });
7679
- if (i < text.length) {
7680
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
7681
- }
7682
- }, d);
7683
- };
7684
- insert();
7685
- return true;
7686
- }
7687
- }
7688
- ])
7689
- ];
7690
- };
7691
8493
  export {
7692
8494
  Cursor,
7693
- EditorInputMode,
7694
- EditorInputModes,
7695
- EditorState3 as EditorState,
7696
- EditorView30 as EditorView,
7697
- EditorViewMode,
7698
- EditorViewModes,
8495
+ EditorState4 as EditorState,
8496
+ EditorView33 as EditorView,
7699
8497
  Inline,
7700
8498
  InputModeExtensions,
7701
8499
  List,
@@ -7712,6 +8510,7 @@ export {
7712
8510
  addStyle,
7713
8511
  annotations,
7714
8512
  autoScroll,
8513
+ autoScrollEffect,
7715
8514
  autocomplete,
7716
8515
  automerge,
7717
8516
  awareness,
@@ -7725,7 +8524,11 @@ export {
7725
8524
  commentClickedEffect,
7726
8525
  comments,
7727
8526
  commentsState,
8527
+ compactSlots,
7728
8528
  convertTreeToJson,
8529
+ crawler,
8530
+ crawlerActiveEffect,
8531
+ crawlerLineEffect,
7729
8532
  createBasicExtensions,
7730
8533
  createComment,
7731
8534
  createCrawler,
@@ -7748,12 +8551,12 @@ export {
7748
8551
  defaultThemeSlots,
7749
8552
  deleteItem,
7750
8553
  documentId,
8554
+ documentSlots,
7751
8555
  dropFile,
8556
+ editorClassNames,
7752
8557
  editorInputMode,
7753
- editorSlots,
7754
- editorWidth,
7755
- editorWithToolbarLayout,
7756
8558
  extendedMarkdown,
8559
+ fader,
7757
8560
  filterChars,
7758
8561
  flattenRect,
7759
8562
  focus,
@@ -7789,6 +8592,7 @@ export {
7789
8592
  markdownTagsExtensions,
7790
8593
  matchCompletion,
7791
8594
  mention,
8595
+ mobileSlots,
7792
8596
  modalStateEffect,
7793
8597
  modalStateField,
7794
8598
  moveItemDown,
@@ -7808,11 +8612,10 @@ export {
7808
8612
  removeList,
7809
8613
  removeStyle,
7810
8614
  replacer,
8615
+ scrollPastEnd,
7811
8616
  scrollThreadIntoView,
7812
8617
  scrollToLine,
7813
8618
  scroller,
7814
- scrollerCrawlEffect,
7815
- scrollerLineEffect,
7816
8619
  selectionState,
7817
8620
  setBlockquote,
7818
8621
  setComments,
@@ -7820,9 +8623,8 @@ export {
7820
8623
  setSelection,
7821
8624
  setStyle,
7822
8625
  singleValueFacet,
7823
- stackItemContentEditorClassNames,
8626
+ snippets2 as snippets,
7824
8627
  staticCompletion,
7825
- streamer,
7826
8628
  submit,
7827
8629
  tabbable,
7828
8630
  table,
@@ -7841,7 +8643,12 @@ export {
7841
8643
  treeFacet,
7842
8644
  typeahead,
7843
8645
  typewriter,
8646
+ typewriterBypass,
8647
+ typewriterDrainingEffect,
7844
8648
  wrapWithCatch,
8649
+ xmlBlockDecoration,
8650
+ xmlElementLength,
8651
+ xmlFormatting,
7845
8652
  xmlTagContextEffect,
7846
8653
  xmlTagResetEffect,
7847
8654
  xmlTagUpdateEffect,