@dxos/ui-editor 0.8.4-main.c85a9c8dae → 0.8.4-main.d05539e30a

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 (166) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +1865 -1053
  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 +1865 -1052
  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 -20
  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 +5 -7
  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} +68 -37
  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/selection.ts +1 -1
  133. package/src/extensions/snippets.ts +67 -0
  134. package/src/extensions/tags/extended-markdown.test.ts +120 -2
  135. package/src/extensions/tags/extended-markdown.ts +80 -1
  136. package/src/extensions/tags/fader.ts +195 -0
  137. package/src/extensions/tags/index.ts +4 -1
  138. package/src/extensions/tags/testing/text.md +36 -0
  139. package/src/extensions/tags/testing/text.txt +35 -0
  140. package/src/extensions/tags/typewriter.test.ts +65 -0
  141. package/src/extensions/tags/typewriter.ts +594 -0
  142. package/src/extensions/tags/xml-block-decoration.ts +123 -0
  143. package/src/extensions/tags/xml-formatting.ts +125 -0
  144. package/src/extensions/tags/xml-tags.ts +183 -35
  145. package/src/extensions/tags/xml-util.test.ts +199 -24
  146. package/src/extensions/tags/xml-util.ts +62 -5
  147. package/src/index.ts +0 -1
  148. package/src/styles/theme.ts +46 -31
  149. package/src/types/types.ts +10 -2
  150. package/src/typings.d.ts +8 -0
  151. package/src/util/cursor.ts +0 -1
  152. package/dist/lib/browser/chunk-HL3YF6WC.mjs +0 -22
  153. package/dist/lib/browser/chunk-HL3YF6WC.mjs.map +0 -7
  154. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs +0 -24
  155. package/dist/lib/node-esm/chunk-YJZGD3LY.mjs.map +0 -7
  156. package/dist/types/src/extensions/auto-scroll.d.ts +0 -6
  157. package/dist/types/src/extensions/auto-scroll.d.ts.map +0 -1
  158. package/dist/types/src/extensions/scroller.d.ts +0 -66
  159. package/dist/types/src/extensions/scroller.d.ts.map +0 -1
  160. package/dist/types/src/extensions/tags/streamer.d.ts +0 -12
  161. package/dist/types/src/extensions/tags/streamer.d.ts.map +0 -1
  162. package/dist/types/src/extensions/typewriter.d.ts +0 -10
  163. package/dist/types/src/extensions/typewriter.d.ts.map +0 -1
  164. package/src/extensions/auto-scroll.ts +0 -126
  165. package/src/extensions/tags/streamer.ts +0 -243
  166. package/src/extensions/typewriter.ts +0 -68
@@ -1,29 +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 = {
18
- scroll: {
19
- className: "pt-2"
20
- },
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 = {
21
36
  content: {
22
- className: editorWidth
37
+ className: "mx-2!"
23
38
  }
24
39
  };
25
- var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
26
- 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");
27
40
 
28
41
  // src/extensions/annotations.ts
29
42
  import { RangeSetBuilder } from "@codemirror/state";
@@ -207,7 +220,7 @@ var singleValueFacet = (defaultValue) => Facet.define({
207
220
  var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
208
221
  var defaultCursorConverter = {
209
222
  toCursor: (position) => position.toString(),
210
- fromCursor: (cursor2) => parseInt(cursor2)
223
+ fromCursor: (cursor) => parseInt(cursor)
211
224
  };
212
225
  var Cursor = class _Cursor {
213
226
  static converter = singleValueFacet(defaultCursorConverter);
@@ -220,9 +233,9 @@ var Cursor = class _Cursor {
220
233
  to
221
234
  ].join(":");
222
235
  };
223
- static getRangeFromCursor = (state, cursor2) => {
236
+ static getRangeFromCursor = (state, cursor) => {
224
237
  const cursorConverter2 = state.facet(_Cursor.converter);
225
- const parts = cursor2.split(":");
238
+ const parts = cursor.split(":");
226
239
  const from = cursorConverter2.fromCursor(parts[0]);
227
240
  const to = cursorConverter2.fromCursor(parts[1]);
228
241
  return from !== void 0 && to !== void 0 ? {
@@ -258,12 +271,7 @@ var wrapWithCatch = (fn, label) => {
258
271
  } catch (err) {
259
272
  log.catch(err, {
260
273
  label
261
- }, {
262
- F: __dxlog_file,
263
- L: 20,
264
- S: void 0,
265
- C: (f, a) => f(...a)
266
- });
274
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 13, S: void 0 });
267
275
  }
268
276
  };
269
277
  };
@@ -289,12 +297,7 @@ var logChanges = (trs) => {
289
297
  if (changes.length) {
290
298
  log("changes", {
291
299
  changes
292
- }, {
293
- F: __dxlog_file,
294
- L: 54,
295
- S: void 0,
296
- C: (f, a) => f(...a)
297
- });
300
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file, L: 44, S: void 0 });
298
301
  }
299
302
  };
300
303
 
@@ -360,30 +363,37 @@ var insertAtLineStart = (view, from, insert) => {
360
363
  };
361
364
 
362
365
  // src/extensions/autocomplete/placeholder.ts
363
- var placeholder = ({ content, delay = 3e3 }) => {
366
+ var placeholder = ({ content, delay = 3e3, focusOnly = false }) => {
364
367
  const plugin = ViewPlugin3.fromClass(class {
365
368
  _timeout;
366
369
  _decorations = Decoration3.none;
367
370
  update(update2) {
371
+ if (!update2.docChanged && !update2.selectionSet && !update2.focusChanged) {
372
+ return;
373
+ }
368
374
  if (this._timeout) {
369
375
  window.clearTimeout(this._timeout);
370
376
  this._timeout = void 0;
371
377
  }
378
+ this._decorations = Decoration3.none;
379
+ if (focusOnly && !update2.view.hasFocus) {
380
+ return;
381
+ }
372
382
  const activeLine = update2.view.state.doc.lineAt(update2.view.state.selection.main.head);
373
- const isEmpty = activeLine.text.trim() === "";
374
- if (isEmpty) {
375
- const lineStart = activeLine.from;
376
- this._timeout = setTimeout(() => {
377
- this._decorations = Decoration3.set([
378
- Decoration3.widget({
379
- widget: new PlaceholderWidget(content),
380
- side: 1
381
- }).range(lineStart)
382
- ]);
383
- update2.view.update([]);
384
- }, delay);
383
+ if (activeLine.text.trim() !== "") {
384
+ return;
385
385
  }
386
- 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);
387
397
  }
388
398
  destroy() {
389
399
  if (this._timeout) {
@@ -501,250 +511,10 @@ var typeahead = ({ onComplete } = {}) => {
501
511
  ];
502
512
  };
503
513
 
504
- // src/extensions/auto-scroll.ts
505
- import { EditorView as EditorView5, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
506
- import { addEventListener, combine, throttle } from "@dxos/async";
507
- import { Domino } from "@dxos/ui";
508
-
509
- // src/extensions/scroller.ts
510
- import { StateEffect } from "@codemirror/state";
511
- import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
512
- import { log as log2 } from "@dxos/log";
513
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scroller.ts";
514
- var scrollerLineEffect = StateEffect.define();
515
- var scrollerCrawlEffect = StateEffect.define();
516
- var scrollToLine = (view, options) => {
517
- view.dispatch({
518
- effects: scrollerLineEffect.of(options)
519
- });
520
- };
521
- var scroller = ({ overScroll = 0 } = {}) => {
522
- const scrollPlugin = ViewPlugin5.fromClass(class ScrollerPlugin {
523
- view;
524
- crawler;
525
- constructor(view) {
526
- this.view = view;
527
- this.crawler = createCrawler(this.view);
528
- }
529
- // No-op.
530
- destroy() {
531
- this.crawler.cancel();
532
- }
533
- cancel() {
534
- this.crawler.cancel();
535
- }
536
- crawl(start = false) {
537
- if (start) {
538
- this.crawler.scroll();
539
- } else {
540
- this.crawler.cancel();
541
- }
542
- }
543
- scroll({ line, offset = 0, position, behavior = "instant" }) {
544
- const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
545
- const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
546
- const doc = this.view.state.doc;
547
- let targetScrollTop = scrollHeight - clientHeight + offset;
548
- if (line >= 0 && line <= doc.lines - 1) {
549
- const lineStart = doc.line(line + 1).from;
550
- const coords = this.view.coordsAtPos(lineStart);
551
- if (coords) {
552
- const currentScrollTop = scrollTop;
553
- const maxScrollTop = scrollHeight - clientHeight;
554
- if (position === "end") {
555
- targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
556
- } else {
557
- targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
558
- }
559
- targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
560
- }
561
- }
562
- requestAnimationFrame(() => {
563
- this.view.scrollDOM.scrollTo({
564
- top: targetScrollTop,
565
- behavior
566
- });
567
- });
568
- }
569
- });
570
- return [
571
- scrollPlugin,
572
- // Listen for effect.s
573
- EditorView4.updateListener.of((update2) => {
574
- update2.transactions.forEach((transaction) => {
575
- try {
576
- const plugin = update2.view.plugin(scrollPlugin);
577
- if (plugin) {
578
- for (const effect of transaction.effects) {
579
- if (effect.is(scrollerCrawlEffect)) {
580
- plugin.crawl(effect.value);
581
- } else if (effect.is(scrollerLineEffect)) {
582
- plugin.scroll(effect.value);
583
- }
584
- }
585
- }
586
- } catch (err) {
587
- log2.catch(err, void 0, {
588
- F: __dxlog_file2,
589
- L: 145,
590
- S: void 0,
591
- C: (f, a) => f(...a)
592
- });
593
- }
594
- });
595
- }),
596
- // Styles.
597
- EditorView4.theme({
598
- ".cm-content": {
599
- paddingBottom: `${overScroll}px`
600
- },
601
- ".cm-scroller": {
602
- overflowAnchor: "none",
603
- paddingBottom: "0"
604
- },
605
- ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
606
- display: "none"
607
- },
608
- ".cm-scroller::-webkit-scrollbar-thumb": {
609
- background: "transparent",
610
- transition: "background 0.15s"
611
- },
612
- "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
613
- background: "var(--color-scrollbar-thumb)"
614
- },
615
- ".cm-scroll-button": {
616
- position: "absolute",
617
- bottom: "0.5rem",
618
- right: "1rem"
619
- }
620
- })
621
- ];
622
- };
623
- function createCrawler(view, k = 0.3, maxStep = 2, targetDelta = 0.5) {
624
- const el = view.scrollDOM;
625
- let currentTop = 0;
626
- let rafId = null;
627
- function frame() {
628
- const targetTop = el.scrollHeight - el.clientHeight;
629
- const delta = targetTop - currentTop;
630
- const absDelta = Math.abs(delta);
631
- if (absDelta < targetDelta) {
632
- el.scrollTop = targetTop;
633
- currentTop = targetTop;
634
- rafId = null;
635
- return;
636
- }
637
- const step = Math.sign(delta) * Math.min(absDelta, Math.max(1, Math.min(absDelta * k, maxStep)));
638
- currentTop += step;
639
- el.scrollTop = currentTop;
640
- rafId = requestAnimationFrame(frame);
641
- }
642
- return {
643
- scroll: () => {
644
- if (rafId === null) {
645
- currentTop = el.scrollTop;
646
- rafId = requestAnimationFrame(frame);
647
- }
648
- },
649
- cancel: () => {
650
- if (rafId !== null) {
651
- cancelAnimationFrame(rafId);
652
- rafId = null;
653
- }
654
- }
655
- };
656
- }
657
-
658
- // src/extensions/auto-scroll.ts
659
- var autoScroll = (_ = {}) => {
660
- let buttonContainer;
661
- let isPinned = true;
662
- const setPinned = (pinned) => {
663
- buttonContainer?.classList.toggle("opacity-0", pinned);
664
- isPinned = pinned;
665
- };
666
- return [
667
- // Update listener for logging when scrolling is needed.
668
- EditorView5.updateListener.of(({ view, heightChanged, state }) => {
669
- if (heightChanged) {
670
- if (isPinned) {
671
- const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
672
- const delta = scrollHeight - scrollTop - clientHeight;
673
- if (delta > 0 && scrollTop > 0) {
674
- setPinned(true);
675
- view.dispatch({
676
- effects: scrollerCrawlEffect.of(true)
677
- });
678
- } else if (delta < 0) {
679
- setPinned(false);
680
- }
681
- } else {
682
- if (state.doc.length === 0) {
683
- setPinned(true);
684
- }
685
- }
686
- }
687
- }),
688
- // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
689
- ViewPlugin6.fromClass(class {
690
- cleanup;
691
- constructor(view) {
692
- this.cleanup = createUserScrollDetector(view.scrollDOM, throttle(() => {
693
- requestAnimationFrame(() => {
694
- const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
695
- const delta = scrollHeight - scrollTop - clientHeight;
696
- const pinned = delta === 0;
697
- setPinned(pinned);
698
- if (!pinned) {
699
- view.dispatch({
700
- effects: scrollerCrawlEffect.of(false)
701
- });
702
- }
703
- });
704
- }, 500));
705
- }
706
- destroy() {
707
- this.cleanup();
708
- }
709
- }),
710
- // Scroll button.
711
- ViewPlugin6.fromClass(class {
712
- constructor(view) {
713
- const icon = Domino.of("dx-icon").attributes({
714
- icon: "ph--arrow-down--regular"
715
- });
716
- const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
717
- "data-density": "fine"
718
- }).children(icon).on("click", () => {
719
- setPinned(true);
720
- view.dispatch({
721
- effects: scrollerLineEffect.of({
722
- line: -1,
723
- position: "end",
724
- behavior: "smooth"
725
- })
726
- });
727
- });
728
- buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").children(button).root;
729
- view.scrollDOM.parentElement.appendChild(buttonContainer);
730
- }
731
- })
732
- ];
733
- };
734
- function createUserScrollDetector(element, onUserScroll) {
735
- return combine(addEventListener(element, "wheel", () => onUserScroll(), {
736
- passive: true
737
- }), addEventListener(element, "pointerdown", (event) => {
738
- if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
739
- onUserScroll();
740
- }
741
- }));
742
- }
743
-
744
514
  // src/extensions/automerge/automerge.ts
745
515
  import { next as A3 } from "@automerge/automerge";
746
516
  import { StateField, Transaction as Transaction2 } from "@codemirror/state";
747
- import { EditorView as EditorView6, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
517
+ import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
748
518
  import { DocAccessor } from "@dxos/echo-db";
749
519
 
750
520
  // src/extensions/state.ts
@@ -753,42 +523,32 @@ var initialSync = Transaction.userEvent.of("initial.sync");
753
523
 
754
524
  // src/extensions/automerge/cursor.ts
755
525
  import { fromCursor, toCursor } from "@dxos/echo-db";
756
- import { log as log3 } from "@dxos/log";
757
- 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";
758
528
  var cursorConverter = (accessor) => ({
759
529
  toCursor: (pos, assoc) => {
760
530
  try {
761
531
  return toCursor(accessor, pos, assoc);
762
532
  } catch (err) {
763
- log3.catch(err, void 0, {
764
- F: __dxlog_file3,
765
- L: 15,
766
- S: void 0,
767
- C: (f, a) => f(...a)
768
- });
533
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 11, S: void 0 });
769
534
  return "";
770
535
  }
771
536
  },
772
- fromCursor: (cursor2) => {
537
+ fromCursor: (cursor) => {
773
538
  try {
774
- return fromCursor(accessor, cursor2);
539
+ return fromCursor(accessor, cursor);
775
540
  } catch (err) {
776
- log3.catch(err, void 0, {
777
- F: __dxlog_file3,
778
- L: 24,
779
- S: void 0,
780
- C: (f, a) => f(...a)
781
- });
541
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 19, S: void 0 });
782
542
  return 0;
783
543
  }
784
544
  }
785
545
  });
786
546
 
787
547
  // src/extensions/automerge/defs.ts
788
- import { Annotation, StateEffect as StateEffect2 } from "@codemirror/state";
548
+ import { Annotation, StateEffect } from "@codemirror/state";
789
549
  var getPath = (state, field) => state.field(field).path;
790
550
  var getLastHeads = (state, field) => state.field(field).lastHeads;
791
- var updateHeadsEffect = StateEffect2.define({});
551
+ var updateHeadsEffect = StateEffect.define({});
792
552
  var updateHeads = (newHeads) => updateHeadsEffect.of({
793
553
  newHeads
794
554
  });
@@ -799,7 +559,7 @@ var isReconcile = (tr) => {
799
559
 
800
560
  // src/extensions/automerge/sync.ts
801
561
  import { next as A2 } from "@automerge/automerge";
802
- import { log as log4 } from "@dxos/log";
562
+ import { log as log3 } from "@dxos/log";
803
563
 
804
564
  // src/extensions/automerge/update-automerge.ts
805
565
  import { next as A } from "@automerge/automerge";
@@ -940,7 +700,7 @@ var charPath = (textPath, candidatePath) => {
940
700
  };
941
701
 
942
702
  // src/extensions/automerge/sync.ts
943
- 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";
944
704
  var Syncer = class {
945
705
  _handle;
946
706
  _state;
@@ -963,12 +723,7 @@ var Syncer = class {
963
723
  this._pending = false;
964
724
  }
965
725
  onEditorChange(view) {
966
- log4("onEditorChange", void 0, {
967
- F: __dxlog_file4,
968
- L: 45,
969
- S: this,
970
- C: (f, a) => f(...a)
971
- });
726
+ log3("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 35, S: this });
972
727
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
973
728
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
974
729
  if (newHeads) {
@@ -979,12 +734,7 @@ var Syncer = class {
979
734
  }
980
735
  }
981
736
  onAutomergeChange(view) {
982
- log4("onAutomergeChange", void 0, {
983
- F: __dxlog_file4,
984
- L: 60,
985
- S: this,
986
- C: (f, a) => f(...a)
987
- });
737
+ log3("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 47, S: this });
988
738
  const oldHeads = getLastHeads(view.state, this._state);
989
739
  const newHeads = A2.getHeads(this._handle.doc());
990
740
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1037,7 +787,7 @@ var automerge = (accessor) => {
1037
787
  // Track heads.
1038
788
  syncState,
1039
789
  // Reconcile external updates.
1040
- ViewPlugin7.fromClass(class {
790
+ ViewPlugin5.fromClass(class {
1041
791
  _view;
1042
792
  constructor(_view) {
1043
793
  this._view = _view;
@@ -1046,6 +796,17 @@ var automerge = (accessor) => {
1046
796
  const value = DocAccessor.getValue(accessor);
1047
797
  const current = this._view.state.doc.toString();
1048
798
  if (value !== current) {
799
+ this._view.dispatch({
800
+ changes: {
801
+ from: 0,
802
+ to: this._view.state.doc.length,
803
+ insert: value
804
+ },
805
+ annotations: [
806
+ initialSync,
807
+ reconcileAnnotation.of(true)
808
+ ]
809
+ });
1049
810
  }
1050
811
  });
1051
812
  }
@@ -1057,7 +818,7 @@ var automerge = (accessor) => {
1057
818
  };
1058
819
  }),
1059
820
  // Reconcile local updates.
1060
- EditorView6.updateListener.of(({ view, changes, transactions }) => {
821
+ EditorView4.updateListener.of(({ view, changes, transactions }) => {
1061
822
  if (!changes.empty) {
1062
823
  const isInitialSync = transactions.some((tr) => tr.annotation(Transaction2.userEvent) === initialSync.value);
1063
824
  if (!isInitialSync) {
@@ -1070,10 +831,10 @@ var automerge = (accessor) => {
1070
831
 
1071
832
  // src/extensions/awareness/awareness.ts
1072
833
  import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1073
- 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";
1074
835
  import { Event } from "@dxos/async";
1075
836
  import { Context } from "@dxos/context";
1076
- 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";
1077
838
  var dummyProvider = {
1078
839
  remoteStateChange: new Event(),
1079
840
  open: () => {
@@ -1089,17 +850,14 @@ var RemoteSelectionChangedAnnotation = Annotation2.define();
1089
850
  var awareness = (provider = dummyProvider) => {
1090
851
  return [
1091
852
  awarenessProvider.of(provider),
1092
- ViewPlugin8.fromClass(RemoteSelectionsDecorator, {
853
+ ViewPlugin6.fromClass(RemoteSelectionsDecorator, {
1093
854
  decorations: (value) => value.decorations
1094
855
  }),
1095
856
  styles
1096
857
  ];
1097
858
  };
1098
859
  var RemoteSelectionsDecorator = class {
1099
- _ctx = new Context(void 0, {
1100
- F: __dxlog_file5,
1101
- L: 80
1102
- });
860
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 33 });
1103
861
  _cursorConverter;
1104
862
  _provider;
1105
863
  _lastAnchor;
@@ -1248,7 +1006,7 @@ var RemoteCaretWidget = class extends WidgetType3 {
1248
1006
  return true;
1249
1007
  }
1250
1008
  };
1251
- var styles = EditorView7.theme({
1009
+ var styles = EditorView5.theme({
1252
1010
  ".cm-collab-selection": {},
1253
1011
  ".cm-collab-selectionLine": {
1254
1012
  padding: 0,
@@ -1310,8 +1068,8 @@ var styles = EditorView7.theme({
1310
1068
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
1311
1069
  import { Context as Context2 } from "@dxos/context";
1312
1070
  import { invariant } from "@dxos/invariant";
1313
- import { log as log5 } from "@dxos/log";
1314
- 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";
1315
1073
  var DEBOUNCE_INTERVAL = 100;
1316
1074
  var SpaceAwarenessProvider = class {
1317
1075
  _remoteStates = /* @__PURE__ */ new Map();
@@ -1330,10 +1088,7 @@ var SpaceAwarenessProvider = class {
1330
1088
  this._info = info;
1331
1089
  }
1332
1090
  open() {
1333
- this._ctx = new Context2(void 0, {
1334
- F: __dxlog_file6,
1335
- L: 57
1336
- });
1091
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 28 });
1337
1092
  this._postTask = new DeferredTask(this._ctx, async () => {
1338
1093
  if (this._localState) {
1339
1094
  await this._messenger.postMessage(this._channel, {
@@ -1358,14 +1113,9 @@ var SpaceAwarenessProvider = class {
1358
1113
  void this._messenger.postMessage(this._channel, {
1359
1114
  kind: "query"
1360
1115
  }).catch((err) => {
1361
- log5.debug("failed to query awareness", {
1116
+ log4.debug("failed to query awareness", {
1362
1117
  err
1363
- }, {
1364
- F: __dxlog_file6,
1365
- L: 91,
1366
- S: this,
1367
- C: (f, a) => f(...a)
1368
- });
1118
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 57, S: this });
1369
1119
  });
1370
1120
  }
1371
1121
  close() {
@@ -1377,15 +1127,7 @@ var SpaceAwarenessProvider = class {
1377
1127
  return Array.from(this._remoteStates.values());
1378
1128
  }
1379
1129
  update(position) {
1380
- invariant(this._postTask, void 0, {
1381
- F: __dxlog_file6,
1382
- L: 106,
1383
- S: this,
1384
- A: [
1385
- "this._postTask",
1386
- ""
1387
- ]
1388
- });
1130
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 71, S: this, A: ["this._postTask", ""] });
1389
1131
  this._localState = {
1390
1132
  peerId: this._peerId,
1391
1133
  position,
@@ -1394,38 +1136,22 @@ var SpaceAwarenessProvider = class {
1394
1136
  this._postTask.schedule();
1395
1137
  }
1396
1138
  _handleQueryMessage() {
1397
- invariant(this._postTask, void 0, {
1398
- F: __dxlog_file6,
1399
- L: 117,
1400
- S: this,
1401
- A: [
1402
- "this._postTask",
1403
- ""
1404
- ]
1405
- });
1139
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 80, S: this, A: ["this._postTask", ""] });
1406
1140
  this._postTask.schedule();
1407
1141
  }
1408
1142
  _handlePostMessage(message) {
1409
- invariant(message.kind === "post", void 0, {
1410
- F: __dxlog_file6,
1411
- L: 122,
1412
- S: this,
1413
- A: [
1414
- "message.kind === 'post'",
1415
- ""
1416
- ]
1417
- });
1143
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1418
1144
  this._remoteStates.set(message.state.peerId, message.state);
1419
1145
  this.remoteStateChange.emit();
1420
1146
  }
1421
1147
  };
1422
1148
 
1423
1149
  // src/extensions/blast.ts
1424
- import { EditorView as EditorView8, keymap as keymap3 } from "@codemirror/view";
1150
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1425
1151
  import defaultsDeep from "lodash.defaultsdeep";
1426
- import { throttle as throttle2 } from "@dxos/async";
1152
+ import { throttle } from "@dxos/async";
1427
1153
  import { invariant as invariant2 } from "@dxos/invariant";
1428
- 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";
1429
1155
  var defaultOptions = {
1430
1156
  effect: 2,
1431
1157
  maxParticles: 200,
@@ -1470,7 +1196,7 @@ var blast = (options = defaultOptions) => {
1470
1196
  };
1471
1197
  return [
1472
1198
  // Cursor moved.
1473
- EditorView8.updateListener.of((update2) => {
1199
+ EditorView6.updateListener.of((update2) => {
1474
1200
  if (blaster?.node !== update2.view.scrollDOM) {
1475
1201
  if (blaster) {
1476
1202
  blaster.destroy();
@@ -1543,15 +1269,7 @@ var Blaster = class {
1543
1269
  return this._node;
1544
1270
  }
1545
1271
  initialize() {
1546
- invariant2(!this._canvas && !this._ctx, void 0, {
1547
- F: __dxlog_file7,
1548
- L: 142,
1549
- S: this,
1550
- A: [
1551
- "!this._canvas && !this._ctx",
1552
- ""
1553
- ]
1554
- });
1272
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1555
1273
  this._canvas = document.createElement("canvas");
1556
1274
  this._canvas.id = "code-blast-canvas";
1557
1275
  this._canvas.style.position = "absolute";
@@ -1580,15 +1298,7 @@ var Blaster = class {
1580
1298
  }
1581
1299
  }
1582
1300
  start() {
1583
- invariant2(this._canvas && this._ctx, void 0, {
1584
- F: __dxlog_file7,
1585
- L: 181,
1586
- S: this,
1587
- A: [
1588
- "this._canvas && this._ctx",
1589
- ""
1590
- ]
1591
- });
1301
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1592
1302
  this._running = true;
1593
1303
  this.loop();
1594
1304
  }
@@ -1615,11 +1325,11 @@ var Blaster = class {
1615
1325
  this.drawParticles();
1616
1326
  requestAnimationFrame(this.loop.bind(this));
1617
1327
  }
1618
- shake = throttle2(({ time }) => {
1328
+ shake = throttle(({ time }) => {
1619
1329
  this._shakeTime = this._shakeTimeMax || time;
1620
1330
  this._shakeTimeMax = time;
1621
1331
  }, 100);
1622
- spawn = throttle2(({ element, point }) => {
1332
+ spawn = throttle(({ element, point }) => {
1623
1333
  const color = getRGBComponents(element, this._options.color);
1624
1334
  const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
1625
1335
  const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
@@ -1728,9 +1438,9 @@ var random = (min, max) => {
1728
1438
 
1729
1439
  // src/extensions/blocks.ts
1730
1440
  import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1731
- 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";
1732
1442
  import { mx as mx2 } from "@dxos/ui-theme";
1733
- var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1443
+ var paragraphBlockPlugin = ViewPlugin7.fromClass(class {
1734
1444
  decorations;
1735
1445
  constructor(view) {
1736
1446
  this.decorations = this.build(view);
@@ -1789,7 +1499,7 @@ var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1789
1499
  });
1790
1500
  var blocks = () => [
1791
1501
  paragraphBlockPlugin,
1792
- EditorView9.baseTheme({
1502
+ EditorView7.baseTheme({
1793
1503
  ".cm-line.block-line": {
1794
1504
  paddingLeft: "0.75rem",
1795
1505
  paddingRight: "0.75rem",
@@ -1823,13 +1533,13 @@ var blocks = () => [
1823
1533
  ];
1824
1534
 
1825
1535
  // src/extensions/bookmarks.ts
1826
- 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";
1827
1537
  import { keymap as keymap4 } from "@codemirror/view";
1828
- import { log as log6 } from "@dxos/log";
1829
- var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1830
- var addBookmark = StateEffect3.define();
1831
- var removeBookmark = StateEffect3.define();
1832
- 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();
1833
1543
  var bookmarks = () => {
1834
1544
  return [
1835
1545
  bookmarksField,
@@ -1838,12 +1548,7 @@ var bookmarks = () => {
1838
1548
  key: "Mod-ArrowUp",
1839
1549
  run: (view) => {
1840
1550
  const bookmarks2 = view.state.field(bookmarksField);
1841
- log6("up", bookmarks2, {
1842
- F: __dxlog_file8,
1843
- L: 29,
1844
- S: void 0,
1845
- C: (f, a) => f(...a)
1846
- });
1551
+ log5("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 18, S: void 0 });
1847
1552
  return true;
1848
1553
  }
1849
1554
  },
@@ -1851,12 +1556,7 @@ var bookmarks = () => {
1851
1556
  key: "Mod-ArrowDown",
1852
1557
  run: (view) => {
1853
1558
  const bookmarks2 = view.state.field(bookmarksField);
1854
- log6("down", bookmarks2, {
1855
- F: __dxlog_file8,
1856
- L: 37,
1857
- S: void 0,
1858
- C: (f, a) => f(...a)
1859
- });
1559
+ log5("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 26, S: void 0 });
1860
1560
  return true;
1861
1561
  }
1862
1562
  }
@@ -1893,27 +1593,27 @@ var bookmarksField = StateField2.define({
1893
1593
 
1894
1594
  // src/extensions/comments.ts
1895
1595
  import { invertedEffects } from "@codemirror/commands";
1896
- import { StateEffect as StateEffect4, StateField as StateField3 } from "@codemirror/state";
1897
- 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";
1898
1598
  import sortBy from "lodash.sortby";
1899
1599
  import { debounce as debounce2 } from "@dxos/async";
1900
- import { log as log7 } from "@dxos/log";
1600
+ import { log as log6 } from "@dxos/log";
1901
1601
  import { isNonNullable } from "@dxos/util";
1902
1602
 
1903
1603
  // src/extensions/selection.ts
1904
1604
  import { Transaction as Transaction3 } from "@codemirror/state";
1905
- import { EditorView as EditorView10, keymap as keymap5 } from "@codemirror/view";
1605
+ import { EditorView as EditorView8, keymap as keymap5 } from "@codemirror/view";
1906
1606
  import { debounce } from "@dxos/async";
1907
1607
  import { invariant as invariant3 } from "@dxos/invariant";
1908
1608
  import { isTruthy } from "@dxos/util";
1909
- 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";
1910
1610
  var documentId = singleValueFacet();
1911
- var stateRestoreAnnotation = "dxos.org/cm/state-restore";
1611
+ var stateRestoreAnnotation = "org.dxos.cm.state-restore";
1912
1612
  var createEditorStateTransaction = ({ scrollTo, selection }) => {
1913
1613
  return {
1914
1614
  selection,
1915
1615
  scrollIntoView: !scrollTo,
1916
- effects: scrollTo ? EditorView10.scrollIntoView(scrollTo, {
1616
+ effects: scrollTo ? EditorView8.scrollIntoView(scrollTo, {
1917
1617
  yMargin: 96
1918
1618
  }) : void 0,
1919
1619
  annotations: Transaction3.userEvent.of(stateRestoreAnnotation)
@@ -1921,28 +1621,12 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1921
1621
  };
1922
1622
  var createEditorStateStore = (keyPrefix) => ({
1923
1623
  getState: (id) => {
1924
- invariant3(id, void 0, {
1925
- F: __dxlog_file9,
1926
- L: 47,
1927
- S: void 0,
1928
- A: [
1929
- "id",
1930
- ""
1931
- ]
1932
- });
1624
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0, A: ["id", ""] });
1933
1625
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
1934
1626
  return state ? JSON.parse(state) : void 0;
1935
1627
  },
1936
1628
  setState: (id, state) => {
1937
- invariant3(id, void 0, {
1938
- F: __dxlog_file9,
1939
- L: 53,
1940
- S: void 0,
1941
- A: [
1942
- "id",
1943
- ""
1944
- ]
1945
- });
1629
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 31, S: void 0, A: ["id", ""] });
1946
1630
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
1947
1631
  }
1948
1632
  });
@@ -1955,7 +1639,7 @@ var selectionState = ({ getState, setState } = {}) => {
1955
1639
  // setStateDebounced(id, {});
1956
1640
  // },
1957
1641
  // }),
1958
- EditorView10.updateListener.of(({ view, transactions }) => {
1642
+ EditorView8.updateListener.of(({ view, transactions }) => {
1959
1643
  const id = view.state.facet(documentId);
1960
1644
  if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
1961
1645
  return;
@@ -1994,10 +1678,10 @@ var selectionState = ({ getState, setState } = {}) => {
1994
1678
  };
1995
1679
 
1996
1680
  // src/extensions/comments.ts
1997
- var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
1998
- var setComments = StateEffect4.define();
1999
- var setSelection = StateEffect4.define();
2000
- 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();
2001
1685
  var commentsState = StateField3.define({
2002
1686
  create: (state) => ({
2003
1687
  id: state.facet(documentId),
@@ -2036,7 +1720,7 @@ var commentsState = StateField3.define({
2036
1720
  return value;
2037
1721
  }
2038
1722
  });
2039
- var styles2 = EditorView11.theme({
1723
+ var styles2 = EditorView9.theme({
2040
1724
  ".cm-comment, .cm-comment-current": {
2041
1725
  padding: "3px 0",
2042
1726
  color: "var(--color-cm-comment-text)",
@@ -2057,19 +1741,14 @@ var createCommentMark = (id, isCurrent) => Decoration7.mark({
2057
1741
  "data-comment-id": id
2058
1742
  }
2059
1743
  });
2060
- var commentsDecorations = EditorView11.decorations.compute([
1744
+ var commentsDecorations = EditorView9.decorations.compute([
2061
1745
  commentsState
2062
1746
  ], (state) => {
2063
1747
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
2064
1748
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2065
1749
  const range = comment.range;
2066
1750
  if (!range) {
2067
- log7.warn("Invalid range:", range, {
2068
- F: __dxlog_file10,
2069
- L: 140,
2070
- S: void 0,
2071
- C: (f, a) => f(...a)
2072
- });
1751
+ log6.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 93, S: void 0 });
2073
1752
  return void 0;
2074
1753
  } else if (range.from === range.to) {
2075
1754
  return void 0;
@@ -2079,8 +1758,8 @@ var commentsDecorations = EditorView11.decorations.compute([
2079
1758
  }).filter(isNonNullable);
2080
1759
  return Decoration7.set(decorations2);
2081
1760
  });
2082
- var commentClickedEffect = StateEffect4.define();
2083
- var handleCommentClick = EditorView11.domEventHandlers({
1761
+ var commentClickedEffect = StateEffect3.define();
1762
+ var handleCommentClick = EditorView9.domEventHandlers({
2084
1763
  click: (event, view) => {
2085
1764
  let target = event.target;
2086
1765
  const editorRoot = view.dom;
@@ -2119,7 +1798,7 @@ var trackPastedComments = (onUpdate) => {
2119
1798
  }
2120
1799
  };
2121
1800
  return [
2122
- EditorView11.domEventHandlers({
1801
+ EditorView9.domEventHandlers({
2123
1802
  cut: handleTrack,
2124
1803
  copy: handleTrack
2125
1804
  }),
@@ -2141,7 +1820,7 @@ var trackPastedComments = (onUpdate) => {
2141
1820
  return effects;
2142
1821
  }),
2143
1822
  // Handle paste or the undo of comment deletion.
2144
- EditorView11.updateListener.of((update2) => {
1823
+ EditorView9.updateListener.of((update2) => {
2145
1824
  const restore = [];
2146
1825
  for (let i = 0; i < update2.transactions.length; i++) {
2147
1826
  const tr = update2.transactions[i];
@@ -2182,10 +1861,10 @@ var trackPastedComments = (onUpdate) => {
2182
1861
  const { comments: comments2 } = update2.startState.field(commentsState);
2183
1862
  const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
2184
1863
  if (!exists) {
2185
- const cursor2 = Cursor.getCursorFromRange(update2.state, comment);
1864
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
2186
1865
  onUpdate({
2187
1866
  id: comment.id,
2188
- cursor: cursor2
1867
+ cursor
2189
1868
  });
2190
1869
  }
2191
1870
  }
@@ -2197,7 +1876,7 @@ var mapTrackedComment = (comment, changes) => ({
2197
1876
  from: changes.mapPos(comment.from, 1),
2198
1877
  to: changes.mapPos(comment.to, 1)
2199
1878
  });
2200
- var restoreCommentEffect = StateEffect4.define({
1879
+ var restoreCommentEffect = StateEffect3.define({
2201
1880
  map: mapTrackedComment
2202
1881
  });
2203
1882
  var createComment = (view) => {
@@ -2214,13 +1893,13 @@ var createComment = (view) => {
2214
1893
  }
2215
1894
  });
2216
1895
  }
2217
- const cursor2 = Cursor.getCursorFromRange(view.state, {
1896
+ const cursor = Cursor.getCursorFromRange(view.state, {
2218
1897
  from,
2219
1898
  to
2220
1899
  });
2221
- if (cursor2) {
1900
+ if (cursor) {
2222
1901
  options.onCreate?.({
2223
- cursor: cursor2,
1902
+ cursor,
2224
1903
  from,
2225
1904
  location: view.coordsAtPos(from)
2226
1905
  });
@@ -2283,7 +1962,7 @@ var comments = (options = {}) => {
2283
1962
  //
2284
1963
  // Track deleted ranges and update ranges for decorations.
2285
1964
  //
2286
- EditorView11.updateListener.of(({ view, state, changes }) => {
1965
+ EditorView9.updateListener.of(({ view, state, changes }) => {
2287
1966
  let mod = false;
2288
1967
  const { comments: comments2, ...value } = state.field(commentsState);
2289
1968
  changes.iterChanges((from, to, from2, to2) => {
@@ -2315,7 +1994,7 @@ var comments = (options = {}) => {
2315
1994
  //
2316
1995
  // Track selection/proximity.
2317
1996
  //
2318
- EditorView11.updateListener.of(({ view, state }) => {
1997
+ EditorView9.updateListener.of(({ view, state }) => {
2319
1998
  let min = Infinity;
2320
1999
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
2321
2000
  const { head } = state.selection.main;
@@ -2369,7 +2048,7 @@ var scrollThreadIntoView = (view, id, center = true) => {
2369
2048
  anchor: range.from
2370
2049
  } : void 0,
2371
2050
  effects: [
2372
- needsScroll ? EditorView11.scrollIntoView(range.from, center ? {
2051
+ needsScroll ? EditorView9.scrollIntoView(range.from, center ? {
2373
2052
  y: "center"
2374
2053
  } : void 0) : [],
2375
2054
  needsSelectionUpdate ? setSelection.of({
@@ -2400,7 +2079,7 @@ var ExternalCommentSync = class {
2400
2079
  this.unsubscribe();
2401
2080
  };
2402
2081
  };
2403
- var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin10.fromClass(class {
2082
+ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin8.fromClass(class {
2404
2083
  constructor(view) {
2405
2084
  return new ExternalCommentSync(view, id, subscribe, getComments);
2406
2085
  }
@@ -2420,12 +2099,12 @@ var debugNodeLogger = (log12 = console.log) => {
2420
2099
  };
2421
2100
 
2422
2101
  // src/extensions/dnd.ts
2423
- import { EditorView as EditorView12, dropCursor } from "@codemirror/view";
2102
+ import { EditorView as EditorView10, dropCursor } from "@codemirror/view";
2424
2103
  var dropFile = (options = {}) => {
2425
2104
  return [
2426
2105
  styles3,
2427
2106
  dropCursor(),
2428
- EditorView12.domEventHandlers({
2107
+ EditorView10.domEventHandlers({
2429
2108
  drop: (event, view) => {
2430
2109
  event.preventDefault();
2431
2110
  const files = event.dataTransfer?.files;
@@ -2444,7 +2123,7 @@ var dropFile = (options = {}) => {
2444
2123
  })
2445
2124
  ];
2446
2125
  };
2447
- var styles3 = EditorView12.theme({
2126
+ var styles3 = EditorView10.theme({
2448
2127
  ".cm-dropCursor": {
2449
2128
  borderLeft: "2px solid var(--color-accent-text)",
2450
2129
  color: "var(--color-accent-text)",
@@ -2461,40 +2140,40 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap }
2461
2140
  import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
2462
2141
  import { searchKeymap } from "@codemirror/search";
2463
2142
  import { EditorState } from "@codemirror/state";
2464
- 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";
2465
2144
  import { vscodeDarkStyle, vscodeLightStyle } from "@uiw/codemirror-theme-vscode";
2466
2145
  import defaultsDeep2 from "lodash.defaultsdeep";
2467
2146
  import { generateName } from "@dxos/display-name";
2468
2147
  import { log as log8 } from "@dxos/log";
2469
- import { hexToHue, isTruthy as isTruthy2 } from "@dxos/util";
2148
+ import { hexToHue, isTruthy as isTruthy3 } from "@dxos/util";
2470
2149
 
2471
2150
  // src/styles/theme.ts
2472
- import { EditorView as EditorView13 } from "@codemirror/view";
2151
+ import { EditorView as EditorView11 } from "@codemirror/view";
2473
2152
  import { mx as mx3 } from "@dxos/ui-theme";
2474
2153
  var headings = {
2475
2154
  1: {
2476
- className: "text-4xl",
2477
- fontSize: "var(--text-4xl)",
2155
+ className: "text-3xl",
2156
+ fontSize: "var(--text-3xl)",
2478
2157
  lineHeight: "var(--text-4xl--line-height)"
2479
2158
  },
2480
2159
  2: {
2481
- className: "text-3xl",
2482
- fontSize: "var(--text-3xl)",
2160
+ className: "text-2xl",
2161
+ fontSize: "var(--text-2xl)",
2483
2162
  lineHeight: "var(--text-3xl--line-height)"
2484
2163
  },
2485
2164
  3: {
2486
- className: "text-2xl",
2487
- fontSize: "var(--text-2xl)",
2165
+ className: "text-xl",
2166
+ fontSize: "var(--text-xl)",
2488
2167
  lineHeight: "var(--text-2xl--line-height)"
2489
2168
  },
2490
2169
  4: {
2491
- className: "text-xl",
2492
- fontSize: "var(--text-xl)",
2170
+ className: "text-lg",
2171
+ fontSize: "var(--text-lg)",
2493
2172
  lineHeight: "var(--text-xl--line-height)"
2494
2173
  },
2495
2174
  5: {
2496
- className: "text-lg",
2497
- fontSize: "var(--text-lg)",
2175
+ className: "text-base",
2176
+ fontSize: "var(--text-base)",
2498
2177
  lineHeight: "var(--text-lg--line-height)"
2499
2178
  },
2500
2179
  6: {
@@ -2503,21 +2182,21 @@ var headings = {
2503
2182
  lineHeight: "var(--text-base--line-height)"
2504
2183
  }
2505
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';
2506
2187
  var markdownTheme = {
2507
- code: "font-mono no-underline! text-cm-code",
2508
- codeMark: "font-mono text-cm-code-mark",
2509
- mark: "opacity-50",
2188
+ code: "font-mono! cm-code-inline",
2189
+ codeMark: "font-mono! cm-code-mark",
2190
+ mark: "font-mono!",
2510
2191
  heading: (level) => ({
2511
- className: mx3(headings[level].className, "font-light text-cm-heading"),
2192
+ className: mx3(headings[level].className, "font-light text-(--color-cm-heading-number)"),
2512
2193
  color: "var(--color-cm-heading) !important",
2513
2194
  lineHeight: headings[level].lineHeight,
2514
2195
  fontSize: headings[level].fontSize,
2515
2196
  fontWeight: "100 !important"
2516
2197
  })
2517
2198
  };
2518
- var fontBody = "Inter Variable, ui-sans-serif, system-ui, sans-serif";
2519
- var fontMono = "JetBrains Mono Variable, ui-monospace, Cascadia Code, Source Code Pro, monospace";
2520
- var baseTheme = EditorView13.baseTheme({
2199
+ var baseTheme = EditorView11.baseTheme({
2521
2200
  /**
2522
2201
  * Outer frame.
2523
2202
  */
@@ -2529,12 +2208,21 @@ var baseTheme = EditorView13.baseTheme({
2529
2208
  * Scroller
2530
2209
  */
2531
2210
  ".cm-scroller": {
2532
- 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"
2533
2215
  },
2534
2216
  ".cm-scroller::-webkit-scrollbar": {
2535
- 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"
2536
2225
  },
2537
- ".cm-scroller::-webkit-scrollbar-track": {},
2538
2226
  ".cm-scroller::-webkit-scrollbar-thumb": {
2539
2227
  background: "transparent",
2540
2228
  transition: "background 0.15s"
@@ -2548,7 +2236,6 @@ var baseTheme = EditorView13.baseTheme({
2548
2236
  */
2549
2237
  ".cm-content": {
2550
2238
  padding: "unset",
2551
- lineHeight: "24px",
2552
2239
  color: "unset"
2553
2240
  },
2554
2241
  /**
@@ -2579,9 +2266,16 @@ var baseTheme = EditorView13.baseTheme({
2579
2266
  * Line.
2580
2267
  */
2581
2268
  ".cm-line": {
2582
- lineHeight: "24px",
2269
+ lineHeight: 1.5,
2583
2270
  paddingInline: 0
2584
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
+ },
2585
2279
  ".cm-activeLine": {
2586
2280
  background: "var(--color-cm-active-line)"
2587
2281
  },
@@ -2625,7 +2319,8 @@ var baseTheme = EditorView13.baseTheme({
2625
2319
  textDecorationThickness: "1px",
2626
2320
  textDecorationColor: "var(--color-separator)",
2627
2321
  textUnderlineOffset: "2px",
2628
- borderRadius: ".125rem"
2322
+ borderRadius: ".125rem",
2323
+ cursor: "pointer"
2629
2324
  },
2630
2325
  ".cm-link > span": {
2631
2326
  color: "var(--color-accent-text)"
@@ -2663,12 +2358,12 @@ var baseTheme = EditorView13.baseTheme({
2663
2358
  padding: "4px"
2664
2359
  },
2665
2360
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2666
- background: "var(--color-active-surface)",
2667
- color: "var(--color-base-surface-text)"
2361
+ background: "var(--color-current-surface)",
2362
+ color: "var(--color-base-foreground)"
2668
2363
  },
2669
2364
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2670
2365
  paddingLeft: "4px !important",
2671
- color: "var(--color-base-surface-text)"
2366
+ color: "var(--color-base-foreground)"
2672
2367
  },
2673
2368
  /**
2674
2369
  * Completion info.
@@ -2687,7 +2382,7 @@ var baseTheme = EditorView13.baseTheme({
2687
2382
  padding: "0 4px"
2688
2383
  },
2689
2384
  ".cm-completionMatchedText": {
2690
- color: "var(--color-base-surface-text)",
2385
+ color: "var(--color-base-foreground)",
2691
2386
  textDecoration: "none !important"
2692
2387
  },
2693
2388
  /**
@@ -2722,7 +2417,7 @@ var baseTheme = EditorView13.baseTheme({
2722
2417
  backgroundColor: "var(--color-input-surface)"
2723
2418
  },
2724
2419
  ".cm-panel input:focus, .cm-panel button:focus": {
2725
- outline: "1px solid var(--color-neutral-focus-indicator)"
2420
+ outline: "1px solid var(--color-focus-ring-subtle)"
2726
2421
  },
2727
2422
  ".cm-panel label": {
2728
2423
  display: "inline-flex",
@@ -2735,7 +2430,7 @@ var baseTheme = EditorView13.baseTheme({
2735
2430
  height: "8px",
2736
2431
  marginRight: "6px !important",
2737
2432
  padding: "2px !important",
2738
- color: "var(--color-neutral-focus-indicator)"
2433
+ color: "var(--color-focus-ring-subtle)"
2739
2434
  },
2740
2435
  ".cm-panel button": {
2741
2436
  "&:hover": {
@@ -2751,18 +2446,17 @@ var baseTheme = EditorView13.baseTheme({
2751
2446
  borderTop: "1px solid var(--color-separator)"
2752
2447
  }
2753
2448
  });
2754
- var editorGutter = EditorView13.theme({
2449
+ var editorGutter = EditorView11.theme({
2755
2450
  ".cm-gutters": {
2756
2451
  // NOTE: Non-transparent background required to cover content if scrolling horizontally.
2757
2452
  background: "var(--color-base-surface) !important",
2758
2453
  paddingRight: "1rem"
2759
2454
  }
2760
2455
  });
2761
- var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2762
- // Set metrics on the scroller (this is often what CM uses for layout).
2456
+ var createFontTheme = ({ monospace } = {}) => EditorView11.theme({
2457
+ // Main content.
2763
2458
  ".cm-scroller": {
2764
- fontFamily: monospace ? fontMono : fontBody,
2765
- fontSize: "16px"
2459
+ fontFamily: monospace ? fontMono : fontBody
2766
2460
  },
2767
2461
  // Maintain defaults for UI components.
2768
2462
  ".cm-content, .cm-gutters, .cm-panel": {
@@ -2772,9 +2466,9 @@ var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2772
2466
  });
2773
2467
 
2774
2468
  // src/extensions/focus.ts
2775
- import { StateEffect as StateEffect5, StateField as StateField5 } from "@codemirror/state";
2776
- import { EditorView as EditorView14 } from "@codemirror/view";
2777
- 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();
2778
2472
  var focusField = StateField5.define({
2779
2473
  create: () => false,
2780
2474
  update: (value, tr) => {
@@ -2788,7 +2482,7 @@ var focusField = StateField5.define({
2788
2482
  });
2789
2483
  var focus = [
2790
2484
  focusField,
2791
- EditorView14.domEventHandlers({
2485
+ EditorView12.domEventHandlers({
2792
2486
  focus: (_event, view) => {
2793
2487
  requestAnimationFrame(() => view.dispatch({
2794
2488
  effects: focusEffect.of(true)
@@ -2802,9 +2496,388 @@ var focus = [
2802
2496
  })
2803
2497
  ];
2804
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
+
2805
2878
  // src/extensions/factories.ts
2806
2879
  var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2807
- var tabbable = EditorView15.contentAttributes.of({
2880
+ var tabbable = EditorView16.contentAttributes.of({
2808
2881
  tabindex: "0"
2809
2882
  });
2810
2883
  var filterChars = (chars) => {
@@ -2857,13 +2930,8 @@ var createBasicExtensions = (propsProp) => {
2857
2930
  const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2858
2931
  return [
2859
2932
  // NOTE: Doesn't catch errors in keymap functions.
2860
- EditorView15.exceptionSink.of((err) => {
2861
- log8.catch(err, void 0, {
2862
- F: __dxlog_file11,
2863
- L: 131,
2864
- S: void 0,
2865
- C: (f, a) => f(...a)
2866
- });
2933
+ EditorView16.exceptionSink.of((err) => {
2934
+ log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
2867
2935
  }),
2868
2936
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2869
2937
  props.bracketMatching && bracketMatching(),
@@ -2872,7 +2940,7 @@ var createBasicExtensions = (propsProp) => {
2872
2940
  props.drawSelection && drawSelection({
2873
2941
  cursorBlinkRate: 1200
2874
2942
  }),
2875
- props.editable !== void 0 && EditorView15.editable.of(props.editable),
2943
+ props.editable !== void 0 && EditorView16.editable.of(props.editable),
2876
2944
  props.focus && focus,
2877
2945
  props.highlightActiveLine && highlightActiveLine(),
2878
2946
  props.history && history(),
@@ -2880,9 +2948,16 @@ var createBasicExtensions = (propsProp) => {
2880
2948
  lineNumbers(),
2881
2949
  editorGutter
2882
2950
  ],
2883
- props.lineWrapping && EditorView15.lineWrapping,
2951
+ props.lineWrapping && EditorView16.lineWrapping,
2884
2952
  props.placeholder && placeholder2(props.placeholder),
2885
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),
2886
2961
  props.scrollPastEnd && scrollPastEnd(),
2887
2962
  props.tabbable && tabbable,
2888
2963
  props.tabSize && EditorState.tabSize.of(props.tabSize),
@@ -2907,8 +2982,8 @@ var createBasicExtensions = (propsProp) => {
2907
2982
  preventDefault: true,
2908
2983
  run: () => true
2909
2984
  }
2910
- ].filter(isTruthy2))
2911
- ].filter(isTruthy2);
2985
+ ].filter(isTruthy3))
2986
+ ].filter(isTruthy3);
2912
2987
  };
2913
2988
  var grow = {
2914
2989
  editor: {
@@ -2925,27 +3000,32 @@ var defaultStyles = {
2925
3000
  dark: vscodeDarkStyle,
2926
3001
  light: vscodeLightStyle
2927
3002
  };
2928
- var createThemeExtensions = ({ monospace, themeMode, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp } = {}) => {
3003
+ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, syntaxHighlighting: syntaxHighlightingProp, themeMode } = {}) => {
2929
3004
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
2930
3005
  return [
2931
3006
  baseTheme,
2932
- EditorView15.darkTheme.of(themeMode === "dark"),
3007
+ EditorView16.darkTheme.of(themeMode === "dark"),
2933
3008
  createFontTheme({
2934
3009
  monospace
2935
3010
  }),
2936
3011
  syntaxHighlightingProp && syntaxHighlighting(HighlightStyle.define(themeMode === "dark" ? defaultStyles.dark : defaultStyles.light)),
2937
- slots.editor?.className && EditorView15.editorAttributes.of({
3012
+ slots.editor?.className && EditorView16.editorAttributes.of({
2938
3013
  class: slots.editor.className
2939
3014
  }),
2940
- slots.content?.className && EditorView15.contentAttributes.of({
3015
+ slots.content?.className && EditorView16.contentAttributes.of({
2941
3016
  class: slots.content.className
2942
3017
  }),
2943
- slots.scroll?.className && ViewPlugin11.fromClass(class {
3018
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
2944
3019
  constructor(view) {
2945
- 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
+ }
2946
3026
  }
2947
3027
  })
2948
- ].filter(isTruthy2);
3028
+ ].filter(isTruthy3);
2949
3029
  };
2950
3030
  var createDataExtensions = ({ id, text, messenger, identity }) => {
2951
3031
  const extensions = [];
@@ -2971,7 +3051,7 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
2971
3051
 
2972
3052
  // src/extensions/folding.ts
2973
3053
  import { codeFolding, foldGutter } from "@codemirror/language";
2974
- import { EditorView as EditorView16 } from "@codemirror/view";
3054
+ import { EditorView as EditorView17 } from "@codemirror/view";
2975
3055
  import { Domino as Domino2, mx as mx4 } from "@dxos/ui";
2976
3056
  var folding = () => {
2977
3057
  return [
@@ -2979,13 +3059,14 @@ var folding = () => {
2979
3059
  placeholderDOM: () => Domino2.of("span").root
2980
3060
  }),
2981
3061
  foldGutter({
3062
+ // NOTE: We can't animate since the element is remounted on state change.
2982
3063
  markerDOM: (open) => {
2983
- 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({
2984
3065
  href: Domino2.icon("ph--caret-right--regular")
2985
3066
  }))).root;
2986
3067
  }
2987
3068
  }),
2988
- EditorView16.theme({
3069
+ EditorView17.theme({
2989
3070
  ".cm-foldGutter": {
2990
3071
  opacity: 0.3,
2991
3072
  transition: "opacity 0.3s",
@@ -2999,7 +3080,7 @@ var folding = () => {
2999
3080
  };
3000
3081
 
3001
3082
  // src/extensions/hashtag.ts
3002
- 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";
3003
3084
  import { getHashStyles, mx as mx5 } from "@dxos/ui-theme";
3004
3085
  var TagWidget = class extends WidgetType4 {
3005
3086
  _text;
@@ -3020,7 +3101,7 @@ var tagMatcher = new MatchDecorator({
3020
3101
  })
3021
3102
  });
3022
3103
  var hashtag = () => [
3023
- ViewPlugin12.fromClass(class {
3104
+ ViewPlugin13.fromClass(class {
3024
3105
  tags;
3025
3106
  constructor(view) {
3026
3107
  this.tags = tagMatcher.createDeco(view);
@@ -3030,11 +3111,11 @@ var hashtag = () => [
3030
3111
  }
3031
3112
  }, {
3032
3113
  decorations: (instance) => instance.tags,
3033
- provide: (plugin) => EditorView17.atomicRanges.of((view) => {
3114
+ provide: (plugin) => EditorView18.atomicRanges.of((view) => {
3034
3115
  return view.plugin(plugin)?.tags || Decoration8.none;
3035
3116
  })
3036
3117
  }),
3037
- EditorView17.theme({
3118
+ EditorView18.theme({
3038
3119
  ".cm-tag": {
3039
3120
  borderRadius: "4px",
3040
3121
  marginRight: "6px",
@@ -3089,18 +3170,18 @@ var schemaLinter = (validate) => (view) => {
3089
3170
  };
3090
3171
 
3091
3172
  // src/extensions/listener.ts
3092
- import { EditorView as EditorView18 } from "@codemirror/view";
3173
+ import { EditorView as EditorView19 } from "@codemirror/view";
3093
3174
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
3094
3175
  var listener = ({ onFocus, onChange }) => {
3095
3176
  return [
3096
- onFocus && EditorView18.focusChangeEffect.of((state, focusing) => {
3177
+ onFocus && EditorView19.focusChangeEffect.of((state, focusing) => {
3097
3178
  onFocus({
3098
3179
  id: state.facet(documentId),
3099
3180
  focusing
3100
3181
  });
3101
3182
  return null;
3102
3183
  }),
3103
- onChange && EditorView18.updateListener.of(({ state, docChanged }) => {
3184
+ onChange && EditorView19.updateListener.of(({ state, docChanged }) => {
3104
3185
  if (docChanged) {
3105
3186
  onChange({
3106
3187
  id: state.facet(documentId),
@@ -3115,7 +3196,7 @@ var listener = ({ onFocus, onChange }) => {
3115
3196
  import { snippet } from "@codemirror/autocomplete";
3116
3197
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3117
3198
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
3118
- import { EditorView as EditorView19, keymap as keymap8 } from "@codemirror/view";
3199
+ import { EditorView as EditorView20, keymap as keymap8 } from "@codemirror/view";
3119
3200
  import { debounceAndThrottle } from "@dxos/async";
3120
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;
3121
3202
  var Inline = /* @__PURE__ */ (function(Inline2) {
@@ -4204,7 +4285,7 @@ var getFormatting = (state) => {
4204
4285
  };
4205
4286
  };
4206
4287
  var formattingListener = (onStateChange, delay = 100) => {
4207
- return EditorView19.updateListener.of(debounceAndThrottle((update2) => {
4288
+ return EditorView20.updateListener.of(debounceAndThrottle((update2) => {
4208
4289
  if (update2.docChanged || update2.selectionSet) {
4209
4290
  onStateChange(getFormatting(update2.state));
4210
4291
  }
@@ -4265,22 +4346,16 @@ import { completionKeymap } from "@codemirror/autocomplete";
4265
4346
  import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
4266
4347
  import { jsonLanguage } from "@codemirror/lang-json";
4267
4348
  import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
4268
- import { xml } from "@codemirror/lang-xml";
4269
- import { LanguageDescription, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4349
+ import { foldNodeProp, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4270
4350
  import { languages } from "@codemirror/language-data";
4271
4351
  import { keymap as keymap9 } from "@codemirror/view";
4272
- import { isTruthy as isTruthy3 } from "@dxos/util";
4352
+ import { isTruthy as isTruthy4 } from "@dxos/util";
4273
4353
 
4274
4354
  // src/extensions/markdown/highlight.ts
4275
4355
  import { markdownLanguage } from "@codemirror/lang-markdown";
4276
4356
  import { HighlightStyle as HighlightStyle2 } from "@codemirror/language";
4277
4357
  import { Tag, styleTags, tags } from "@lezer/highlight";
4278
4358
  import { Table } from "@lezer/markdown";
4279
- var styles4 = {
4280
- code: "font-mono no-underline! text-cm-code",
4281
- codeMark: "font-mono text-cm-code-mark",
4282
- mark: "opacity-50"
4283
- };
4284
4359
  var markdownTags = {
4285
4360
  Blockquote: Tag.define(),
4286
4361
  CodeMark: Tag.define(),
@@ -4362,7 +4437,7 @@ var markdownHighlightStyle = (_options = {}) => {
4362
4437
  markdownTags.LinkReference,
4363
4438
  markdownTags.ListMark
4364
4439
  ],
4365
- class: styles4.mark
4440
+ class: markdownTheme.mark
4366
4441
  },
4367
4442
  // Markdown marks.
4368
4443
  {
@@ -4373,7 +4448,7 @@ var markdownHighlightStyle = (_options = {}) => {
4373
4448
  markdownTags.QuoteMark,
4374
4449
  markdownTags.EmphasisMark
4375
4450
  ],
4376
- class: styles4.mark
4451
+ class: markdownTheme.mark
4377
4452
  },
4378
4453
  // E.g., code block language (after ```).
4379
4454
  {
@@ -4382,7 +4457,7 @@ var markdownHighlightStyle = (_options = {}) => {
4382
4457
  tags.function(tags.variableName),
4383
4458
  tags.labelName
4384
4459
  ],
4385
- class: styles4.codeMark
4460
+ class: markdownTheme.codeMark
4386
4461
  },
4387
4462
  // Fonts.
4388
4463
  {
@@ -4448,7 +4523,7 @@ var markdownHighlightStyle = (_options = {}) => {
4448
4523
  markdownTags.CodeText,
4449
4524
  markdownTags.InlineCode
4450
4525
  ],
4451
- class: styles4.code
4526
+ class: markdownTheme.code
4452
4527
  },
4453
4528
  {
4454
4529
  tag: [
@@ -4478,15 +4553,23 @@ var createMarkdownExtensions = (options = {}) => {
4478
4553
  // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
4479
4554
  base: markdownLanguage2,
4480
4555
  // Languages for syntax highlighting fenced code blocks.
4556
+ // Caller-supplied languages are checked first so they can override defaults.
4481
4557
  defaultCodeLanguage: jsonLanguage,
4482
- codeLanguages: languages,
4558
+ codeLanguages: [
4559
+ ...options.codeLanguages ?? [],
4560
+ ...languages
4561
+ ],
4483
4562
  // Don't complete HTML tags.
4484
4563
  completeHTMLTags: false,
4485
4564
  // Parser extensions.
4486
4565
  extensions: [
4487
4566
  // GFM provided by default.
4488
4567
  markdownTagsExtensions,
4489
- ...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
4490
4573
  ]
4491
4574
  }),
4492
4575
  // Custom styles.
@@ -4498,21 +4581,16 @@ var createMarkdownExtensions = (options = {}) => {
4498
4581
  ...defaultKeymap2,
4499
4582
  // TODO(burdon): Remove?
4500
4583
  ...completionKeymap
4501
- ].filter(isTruthy3))
4584
+ ].filter(isTruthy4))
4502
4585
  ];
4503
4586
  };
4504
- var xmlLanguageDesc = LanguageDescription.of({
4505
- name: "xml",
4506
- alias: [
4507
- "html",
4508
- "xhtml"
4509
- ],
4510
- extensions: [
4511
- "xml",
4512
- "xhtml"
4513
- ],
4514
- load: async () => xml()
4515
- });
4587
+ var noFencedCodeFolding = {
4588
+ props: [
4589
+ foldNodeProp.add({
4590
+ FencedCode: () => null
4591
+ })
4592
+ ]
4593
+ };
4516
4594
  var defaultExtensions = () => [
4517
4595
  noSetExtHeading,
4518
4596
  noHtml
@@ -4532,19 +4610,19 @@ var debugTree = (cb) => StateField6.define({
4532
4610
  update: (value, tr) => cb(convertTreeToJson(tr.state))
4533
4611
  });
4534
4612
  var convertTreeToJson = (state) => {
4535
- const treeToJson = (cursor2) => {
4613
+ const treeToJson = (cursor) => {
4536
4614
  const node = {
4537
- type: cursor2.type.name,
4538
- from: cursor2.from,
4539
- to: cursor2.to,
4540
- 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(),
4541
4619
  children: []
4542
4620
  };
4543
- if (cursor2.firstChild()) {
4621
+ if (cursor.firstChild()) {
4544
4622
  do {
4545
- node.children.push(treeToJson(cursor2));
4546
- } while (cursor2.nextSibling());
4547
- cursor2.parent();
4623
+ node.children.push(treeToJson(cursor));
4624
+ } while (cursor.nextSibling());
4625
+ cursor.parent();
4548
4626
  }
4549
4627
  return node;
4550
4628
  };
@@ -4553,17 +4631,16 @@ var convertTreeToJson = (state) => {
4553
4631
 
4554
4632
  // src/extensions/markdown/decorate.ts
4555
4633
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4556
- import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect6 } from "@codemirror/state";
4557
- 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";
4558
4636
  import { invariant as invariant4 } from "@dxos/invariant";
4559
- import { mx as mx6 } from "@dxos/ui-theme";
4560
4637
 
4561
4638
  // src/extensions/markdown/changes.ts
4562
4639
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4563
4640
  import { Transaction as Transaction4 } from "@codemirror/state";
4564
- import { ViewPlugin as ViewPlugin13 } from "@codemirror/view";
4641
+ import { ViewPlugin as ViewPlugin14 } from "@codemirror/view";
4565
4642
  var adjustChanges = () => {
4566
- return ViewPlugin13.fromClass(class {
4643
+ return ViewPlugin14.fromClass(class {
4567
4644
  update(update2) {
4568
4645
  const tree = syntaxTree4(update2.state);
4569
4646
  const adjustments = [];
@@ -4705,7 +4782,7 @@ var getValidUrl = (str) => {
4705
4782
  // src/extensions/markdown/image.ts
4706
4783
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4707
4784
  import { StateField as StateField7 } from "@codemirror/state";
4708
- 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";
4709
4786
  var image = (_options = {}) => {
4710
4787
  return [
4711
4788
  StateField7.define({
@@ -4716,10 +4793,10 @@ var image = (_options = {}) => {
4716
4793
  if (!tr.docChanged && !tr.selection) {
4717
4794
  return value;
4718
4795
  }
4719
- const cursor2 = tr.state.selection.main.head;
4796
+ const cursor = tr.state.selection.main.head;
4720
4797
  const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
4721
- let from = Math.min(cursor2, oldCursor);
4722
- let to = Math.max(cursor2, oldCursor);
4798
+ let from = Math.min(cursor, oldCursor);
4799
+ let to = Math.max(cursor, oldCursor);
4723
4800
  tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
4724
4801
  from = Math.min(from, fromB);
4725
4802
  to = Math.max(to, toB);
@@ -4733,19 +4810,19 @@ var image = (_options = {}) => {
4733
4810
  add: buildDecorations(tr.state, from, to)
4734
4811
  });
4735
4812
  },
4736
- provide: (field) => EditorView20.decorations.from(field)
4813
+ provide: (field) => EditorView21.decorations.from(field)
4737
4814
  })
4738
4815
  ];
4739
4816
  };
4740
4817
  var buildDecorations = (state, from, to) => {
4741
4818
  const decorations2 = [];
4742
- const cursor2 = state.selection.main.head;
4819
+ const cursor = state.selection.main.head;
4743
4820
  syntaxTree5(state).iterate({
4744
4821
  enter: (node) => {
4745
4822
  if (node.name === "Image") {
4746
4823
  const urlNode = node.node.getChild("URL");
4747
4824
  if (urlNode) {
4748
- 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);
4749
4826
  const url = state.sliceDoc(urlNode.from, urlNode.to);
4750
4827
  if (url.match(/^https?:\/\//) === null && url.match(/^file?:\/\//) === null) {
4751
4828
  return;
@@ -4793,10 +4870,10 @@ var ImageWidget = class extends WidgetType5 {
4793
4870
  };
4794
4871
 
4795
4872
  // src/extensions/markdown/styles.ts
4796
- import { EditorView as EditorView21 } from "@codemirror/view";
4873
+ import { EditorView as EditorView22 } from "@codemirror/view";
4797
4874
  var bulletListIndentationWidth = 24;
4798
4875
  var orderedListIndentationWidth = 36;
4799
- var formattingStyles = EditorView21.theme({
4876
+ var formattingStyles = EditorView22.theme({
4800
4877
  /**
4801
4878
  * Horizontal rule.
4802
4879
  */
@@ -4831,13 +4908,38 @@ var formattingStyles = EditorView21.theme({
4831
4908
  background: "var(--color-cm-codeblock)",
4832
4909
  borderLeft: "2px solid var(--color-cm-separator)",
4833
4910
  paddingLeft: "1rem",
4834
- margin: "0"
4911
+ margin: 0
4835
4912
  },
4836
4913
  /**
4837
4914
  * Code and codeblocks.
4838
4915
  */
4916
+ "& code": {
4917
+ fontFamily: fontMono,
4918
+ color: "var(--color-cm-code)",
4919
+ whiteSpace: "nowrap"
4920
+ },
4839
4921
  "& .cm-code": {
4840
- 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"
4841
4943
  },
4842
4944
  "& .cm-codeblock-line": {
4843
4945
  background: "var(--color-cm-codeblock)",
@@ -4869,16 +4971,24 @@ var formattingStyles = EditorView21.theme({
4869
4971
  */
4870
4972
  ".cm-table *": {
4871
4973
  fontFamily: fontMono,
4974
+ lineHeight: 1.5,
4872
4975
  textDecoration: "none !important"
4873
4976
  },
4874
4977
  ".cm-table-head": {
4875
4978
  padding: "2px 16px 2px 0px",
4979
+ overflowWrap: "break-word",
4980
+ whiteSpace: "pre-wrap",
4981
+ wordBreak: "keep-all",
4876
4982
  textAlign: "left",
4877
- borderBottom: "1px solid var(--color-cm-separator)",
4878
- color: "var(--color-subdued)"
4983
+ color: "var(--color-subdued)",
4984
+ borderBottom: "1px solid var(--color-cm-separator)"
4879
4985
  },
4880
4986
  ".cm-table-cell": {
4881
- 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"
4882
4992
  },
4883
4993
  /**
4884
4994
  * Image.
@@ -4894,12 +5004,12 @@ var formattingStyles = EditorView21.theme({
4894
5004
  },
4895
5005
  ".cm-image-with-loader": {
4896
5006
  display: "block",
4897
- opacity: "0",
5007
+ opacity: 0,
4898
5008
  transitionDuration: "350ms",
4899
5009
  transitionProperty: "opacity"
4900
5010
  },
4901
5011
  ".cm-image-with-loader.cm-loaded-image": {
4902
- opacity: "1"
5012
+ opacity: 1
4903
5013
  },
4904
5014
  ".cm-image-wrapper": {
4905
5015
  "grid-template-columns": "1fr",
@@ -4918,17 +5028,17 @@ var formattingStyles = EditorView21.theme({
4918
5028
  // src/extensions/markdown/table.ts
4919
5029
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4920
5030
  import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4921
- 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";
4922
5032
  var table = (options = {}) => {
4923
5033
  return StateField8.define({
4924
5034
  create: (state) => update(state, options),
4925
5035
  update: (_, tr) => update(tr.state, options),
4926
- provide: (field) => EditorView22.decorations.from(field)
5036
+ provide: (field) => EditorView23.decorations.from(field)
4927
5037
  });
4928
5038
  };
4929
5039
  var update = (state, _options) => {
4930
5040
  const builder = new RangeSetBuilder4();
4931
- const cursor2 = state.selection.main.head;
5041
+ const cursor = state.selection.main.head;
4932
5042
  const tables = [];
4933
5043
  const getTable = () => tables[tables.length - 1];
4934
5044
  const getRow = () => {
@@ -4966,7 +5076,7 @@ var update = (state, _options) => {
4966
5076
  }
4967
5077
  });
4968
5078
  tables.forEach((table2) => {
4969
- const replace = state.readOnly || cursor2 < table2.from || cursor2 > table2.to;
5079
+ const replace = state.readOnly || cursor < table2.from || cursor > table2.to;
4970
5080
  if (replace) {
4971
5081
  builder.add(table2.from, table2.to, Decoration10.replace({
4972
5082
  block: true,
@@ -4980,6 +5090,26 @@ var update = (state, _options) => {
4980
5090
  });
4981
5091
  return builder.finish();
4982
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
+ };
4983
5113
  var TableWidget = class extends WidgetType6 {
4984
5114
  _table;
4985
5115
  constructor(_table) {
@@ -4999,7 +5129,7 @@ var TableWidget = class extends WidgetType6 {
4999
5129
  this._table.header?.forEach((cell) => {
5000
5130
  const th = document.createElement("th");
5001
5131
  th.setAttribute("class", "cm-table-head");
5002
- tr.appendChild(th).textContent = cell;
5132
+ renderCellContent(tr.appendChild(th), cell);
5003
5133
  });
5004
5134
  const body = table2.appendChild(document.createElement("tbody"));
5005
5135
  this._table.rows?.forEach((row) => {
@@ -5007,7 +5137,7 @@ var TableWidget = class extends WidgetType6 {
5007
5137
  row.forEach((cell) => {
5008
5138
  const td = document.createElement("td");
5009
5139
  td.setAttribute("class", "cm-table-cell");
5010
- tr2.appendChild(td).textContent = cell;
5140
+ renderCellContent(tr2.appendChild(td), cell);
5011
5141
  });
5012
5142
  });
5013
5143
  return div;
@@ -5114,10 +5244,10 @@ var fencedCodeLine = Decoration11.line({
5114
5244
  class: "cm-code cm-codeblock-line"
5115
5245
  });
5116
5246
  var fencedCodeLineFirst = Decoration11.line({
5117
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-start")
5247
+ class: "cm-code cm-codeblock-line cm-codeblock-start"
5118
5248
  });
5119
5249
  var fencedCodeLineLast = Decoration11.line({
5120
- class: mx6("cm-code cm-codeblock-line", "cm-codeblock-end")
5250
+ class: "cm-code cm-codeblock-line cm-codeblock-end"
5121
5251
  });
5122
5252
  var commentBlockLine = fencedCodeLine;
5123
5253
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -5149,15 +5279,7 @@ var buildDecorations2 = (view, options, focus2) => {
5149
5279
  const { state } = view;
5150
5280
  const headerLevels = [];
5151
5281
  const getHeaderLevels = (node, level) => {
5152
- invariant4(level > 0, void 0, {
5153
- F: __dxlog_file12,
5154
- L: 179,
5155
- S: void 0,
5156
- A: [
5157
- "level > 0",
5158
- ""
5159
- ]
5160
- });
5282
+ invariant4(level > 0, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 160, S: void 0, A: ["level > 0", ""] });
5161
5283
  if (level > headerLevels.length) {
5162
5284
  const len = headerLevels.length;
5163
5285
  headerLevels.length = level;
@@ -5188,15 +5310,7 @@ var buildDecorations2 = (view, options, focus2) => {
5188
5310
  listLevels.pop();
5189
5311
  };
5190
5312
  const getCurrentListLevel = () => {
5191
- invariant4(listLevels.length, void 0, {
5192
- F: __dxlog_file12,
5193
- L: 201,
5194
- S: void 0,
5195
- A: [
5196
- "listLevels.length",
5197
- ""
5198
- ]
5199
- });
5313
+ invariant4(listLevels.length, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file12, L: 192, S: void 0, A: ["listLevels.length", ""] });
5200
5314
  return listLevels[listLevels.length - 1];
5201
5315
  };
5202
5316
  const enterNode = (node) => {
@@ -5234,7 +5348,7 @@ var buildDecorations2 = (view, options, focus2) => {
5234
5348
  deco: hide
5235
5349
  });
5236
5350
  } else {
5237
- 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(".") + "). ";
5238
5352
  if (num.length) {
5239
5353
  atomicDecoRanges.push({
5240
5354
  from: mark.from,
@@ -5417,11 +5531,11 @@ var buildDecorations2 = (view, options, focus2) => {
5417
5531
  }
5418
5532
  decoRanges.push({
5419
5533
  from: marks[0].to,
5420
- to: marks[1].from,
5534
+ to: !editing && options.renderLinkButton ? node.to : marks[1].from,
5421
5535
  deco: Decoration11.mark({
5422
5536
  tagName: "a",
5423
5537
  attributes: {
5424
- class: "cm-link",
5538
+ class: options.renderLinkButton ? "cm-link cm-link-with-button" : "cm-link",
5425
5539
  href: url,
5426
5540
  rel: "noreferrer",
5427
5541
  target: "_blank"
@@ -5499,18 +5613,21 @@ var buildDecorations2 = (view, options, focus2) => {
5499
5613
  deco.add(from, to, d);
5500
5614
  }
5501
5615
  const atomicDeco = new RangeSetBuilder5();
5502
- for (const { from, to, deco: d } of atomicDecoRanges) {
5503
- 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);
5504
5621
  }
5505
5622
  return {
5506
5623
  deco: deco.finish(),
5507
5624
  atomicDeco: atomicDeco.finish()
5508
5625
  };
5509
5626
  };
5510
- var forceUpdate = StateEffect6.define();
5627
+ var forceUpdate = StateEffect7.define();
5511
5628
  var decorateMarkdown = (options = {}) => {
5512
5629
  return [
5513
- ViewPlugin14.fromClass(class {
5630
+ ViewPlugin15.fromClass(class {
5514
5631
  deco;
5515
5632
  atomicDeco;
5516
5633
  pendingUpdate;
@@ -5545,9 +5662,9 @@ var decorateMarkdown = (options = {}) => {
5545
5662
  }
5546
5663
  }, {
5547
5664
  provide: (plugin) => [
5548
- Prec4.low(EditorView23.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5549
- EditorView23.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5550
- 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)
5551
5668
  ]
5552
5669
  }),
5553
5670
  image(),
@@ -5615,12 +5732,7 @@ var mention = ({ debug, onSearch }) => {
5615
5732
  (context) => {
5616
5733
  log9.info("completion context", {
5617
5734
  context
5618
- }, {
5619
- F: __dxlog_file13,
5620
- L: 27,
5621
- S: void 0,
5622
- C: (f, a) => f(...a)
5623
- });
5735
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file13, L: 18, S: void 0 });
5624
5736
  const match = context.matchBefore(/@(\w+)?/);
5625
5737
  if (!match || match.from === match.to && !context.explicit) {
5626
5738
  return null;
@@ -5637,8 +5749,8 @@ var mention = ({ debug, onSearch }) => {
5637
5749
  };
5638
5750
 
5639
5751
  // src/extensions/modal.ts
5640
- import { StateEffect as StateEffect7, StateField as StateField9 } from "@codemirror/state";
5641
- var modalStateEffect = StateEffect7.define();
5752
+ import { StateEffect as StateEffect8, StateField as StateField9 } from "@codemirror/state";
5753
+ var modalStateEffect = StateEffect8.define();
5642
5754
  var modalStateField = StateField9.define({
5643
5755
  create: () => false,
5644
5756
  update: (value, tr) => {
@@ -5853,15 +5965,7 @@ var outlinerTree = (_options = {}) => {
5853
5965
  break;
5854
5966
  }
5855
5967
  case "BulletList": {
5856
- invariant5(current, void 0, {
5857
- F: __dxlog_file14,
5858
- L: 219,
5859
- S: void 0,
5860
- A: [
5861
- "current",
5862
- ""
5863
- ]
5864
- });
5968
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 169, S: void 0, A: ["current", ""] });
5865
5969
  parent = current;
5866
5970
  if (current) {
5867
5971
  current.lineRange.to = current.node.from;
@@ -5870,15 +5974,7 @@ var outlinerTree = (_options = {}) => {
5870
5974
  break;
5871
5975
  }
5872
5976
  case "ListItem": {
5873
- invariant5(parent, void 0, {
5874
- F: __dxlog_file14,
5875
- L: 228,
5876
- S: void 0,
5877
- A: [
5878
- "parent",
5879
- ""
5880
- ]
5881
- });
5977
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 179, S: void 0, A: ["parent", ""] });
5882
5978
  const nextSibling = node.node.nextSibling ?? node.node.parent?.nextSibling;
5883
5979
  const docRange = {
5884
5980
  from: state.doc.lineAt(node.from).from,
@@ -5912,42 +6008,18 @@ var outlinerTree = (_options = {}) => {
5912
6008
  break;
5913
6009
  }
5914
6010
  case "ListMark": {
5915
- invariant5(current, void 0, {
5916
- F: __dxlog_file14,
5917
- L: 272,
5918
- S: void 0,
5919
- A: [
5920
- "current",
5921
- ""
5922
- ]
5923
- });
6011
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 219, S: void 0, A: ["current", ""] });
5924
6012
  current.type = "bullet";
5925
6013
  current.contentRange.from = node.from + "- ".length;
5926
6014
  break;
5927
6015
  }
5928
6016
  case "Task": {
5929
- invariant5(current, void 0, {
5930
- F: __dxlog_file14,
5931
- L: 278,
5932
- S: void 0,
5933
- A: [
5934
- "current",
5935
- ""
5936
- ]
5937
- });
6017
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 226, S: void 0, A: ["current", ""] });
5938
6018
  current.type = "task";
5939
6019
  break;
5940
6020
  }
5941
6021
  case "TaskMarker": {
5942
- invariant5(current, void 0, {
5943
- F: __dxlog_file14,
5944
- L: 283,
5945
- S: void 0,
5946
- A: [
5947
- "current",
5948
- ""
5949
- ]
5950
- });
6022
+ invariant5(current, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 232, S: void 0, A: ["current", ""] });
5951
6023
  current.contentRange.from = node.from + "[ ] ".length;
5952
6024
  break;
5953
6025
  }
@@ -5955,29 +6027,13 @@ var outlinerTree = (_options = {}) => {
5955
6027
  },
5956
6028
  leave: (node) => {
5957
6029
  if (node.name === "BulletList") {
5958
- invariant5(parent, void 0, {
5959
- F: __dxlog_file14,
5960
- L: 291,
5961
- S: void 0,
5962
- A: [
5963
- "parent",
5964
- ""
5965
- ]
5966
- });
6030
+ invariant5(parent, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 240, S: void 0, A: ["parent", ""] });
5967
6031
  prevSiblings[level--] = void 0;
5968
6032
  parent = parent.parent;
5969
6033
  }
5970
6034
  }
5971
6035
  });
5972
- invariant5(tree, void 0, {
5973
- F: __dxlog_file14,
5974
- L: 298,
5975
- S: void 0,
5976
- A: [
5977
- "tree",
5978
- ""
5979
- ]
5980
- });
6036
+ invariant5(tree, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file14, L: 246, S: void 0, A: ["tree", ""] });
5981
6037
  return tree;
5982
6038
  };
5983
6039
  return [
@@ -6262,17 +6318,17 @@ var commands = () => keymap11.of([
6262
6318
 
6263
6319
  // src/extensions/outliner/outliner.ts
6264
6320
  import { Prec as Prec5 } from "@codemirror/state";
6265
- import { Decoration as Decoration12, EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6266
- 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";
6267
6323
 
6268
6324
  // src/extensions/outliner/editor.ts
6269
6325
  import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
6270
- import { ViewPlugin as ViewPlugin15 } from "@codemirror/view";
6326
+ import { ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6271
6327
  import { log as log10 } from "@dxos/log";
6272
6328
  var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6273
6329
  var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
6274
6330
  var initialize = () => {
6275
- return ViewPlugin15.fromClass(class {
6331
+ return ViewPlugin16.fromClass(class {
6276
6332
  constructor(view) {
6277
6333
  const first = view.state.doc.lineAt(0);
6278
6334
  const text = view.state.sliceDoc(first.from, first.to);
@@ -6419,35 +6475,20 @@ var editor = () => [
6419
6475
  text: insert.toString(),
6420
6476
  length: insert.length
6421
6477
  }
6422
- }, {
6423
- F: __dxlog_file15,
6424
- L: 164,
6425
- S: void 0,
6426
- C: (f, a) => f(...a)
6427
- });
6478
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 174, S: void 0 });
6428
6479
  }
6429
6480
  });
6430
6481
  if (changes.length > 0) {
6431
6482
  log10("modified,", {
6432
6483
  changes
6433
- }, {
6434
- F: __dxlog_file15,
6435
- L: 175,
6436
- S: void 0,
6437
- C: (f, a) => f(...a)
6438
- });
6484
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 196, S: void 0 });
6439
6485
  return [
6440
6486
  {
6441
6487
  changes
6442
6488
  }
6443
6489
  ];
6444
6490
  } else if (cancel) {
6445
- log10("cancel", void 0, {
6446
- F: __dxlog_file15,
6447
- L: 178,
6448
- S: void 0,
6449
- C: (f, a) => f(...a)
6450
- });
6491
+ log10("cancel", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file15, L: 205, S: void 0 });
6451
6492
  return [];
6452
6493
  }
6453
6494
  return tr;
@@ -6455,10 +6496,10 @@ var editor = () => [
6455
6496
  ];
6456
6497
 
6457
6498
  // src/extensions/outliner/menu.ts
6458
- import { EditorView as EditorView24, ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6499
+ import { EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6459
6500
  import { addEventListener as addEventListener2 } from "@dxos/async";
6460
6501
  var menu = (options = {}) => [
6461
- ViewPlugin16.fromClass(class {
6502
+ ViewPlugin17.fromClass(class {
6462
6503
  view;
6463
6504
  tag;
6464
6505
  rafId;
@@ -6520,7 +6561,7 @@ var menu = (options = {}) => [
6520
6561
  this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
6521
6562
  }
6522
6563
  }),
6523
- EditorView24.theme({
6564
+ EditorView25.theme({
6524
6565
  ".cm-popover-trigger": {
6525
6566
  position: "fixed",
6526
6567
  padding: "0",
@@ -6556,12 +6597,12 @@ var outliner = (_options = {}) => [
6556
6597
  listPaddingLeft: 8
6557
6598
  }),
6558
6599
  // Researve space for menu.
6559
- EditorView25.contentAttributes.of({
6600
+ EditorView26.contentAttributes.of({
6560
6601
  class: "w-full !mr-[3rem]"
6561
6602
  })
6562
6603
  ];
6563
6604
  var decorations = () => [
6564
- ViewPlugin17.fromClass(class {
6605
+ ViewPlugin18.fromClass(class {
6565
6606
  decorations = Decoration12.none;
6566
6607
  constructor(view) {
6567
6608
  this.updateDecorations(view.state, view);
@@ -6586,7 +6627,7 @@ var decorations = () => [
6586
6627
  const lineTo = doc.lineAt(item.contentRange.to);
6587
6628
  const isSelected = selection.includes(item.index) || item === current;
6588
6629
  decorations2.push(Decoration12.line({
6589
- 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"))
6590
6631
  }).range(line.from, line.from));
6591
6632
  }
6592
6633
  }
@@ -6596,7 +6637,7 @@ var decorations = () => [
6596
6637
  decorations: (v) => v.decorations
6597
6638
  }),
6598
6639
  // Theme.
6599
- EditorView25.theme(Object.assign({
6640
+ EditorView26.theme(Object.assign({
6600
6641
  ".cm-list-item": {
6601
6642
  borderLeftWidth: "1px",
6602
6643
  borderRightWidth: "1px",
@@ -6621,7 +6662,7 @@ var decorations = () => [
6621
6662
  marginBottom: "2px"
6622
6663
  },
6623
6664
  ".cm-list-item-focused": {
6624
- borderColor: "var(--color-neutral-focus-indicator)"
6665
+ borderColor: "var(--color-focus-ring-subtle)"
6625
6666
  },
6626
6667
  "&:focus-within .cm-list-item-selected": {
6627
6668
  borderColor: "var(--color-separator)"
@@ -6631,28 +6672,57 @@ var decorations = () => [
6631
6672
 
6632
6673
  // src/extensions/preview/preview.ts
6633
6674
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
6634
- import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField11 } from "@codemirror/state";
6635
- 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();
6636
6679
  var preview = (options = {}) => {
6680
+ const viewRef = {
6681
+ current: void 0
6682
+ };
6637
6683
  return [
6638
6684
  // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
6639
6685
  // "Block decorations may not be specified via plugins".
6640
6686
  StateField11.define({
6641
- create: (state) => buildDecorations3(state, options),
6687
+ create: (state) => buildDecorations3(state, options, viewRef),
6642
6688
  update: (decorations2, tr) => {
6643
- if (tr.docChanged) {
6644
- return buildDecorations3(tr.state, options);
6689
+ if (tr.docChanged || tr.effects.some((effect) => effect.is(labelResolvedEffect))) {
6690
+ return buildDecorations3(tr.state, options, viewRef);
6645
6691
  }
6646
6692
  return decorations2.map(tr.changes);
6647
6693
  },
6648
6694
  provide: (field) => [
6649
- EditorView26.decorations.from(field),
6650
- EditorView26.atomicRanges.of((view) => view.state.field(field))
6695
+ EditorView27.decorations.from(field),
6696
+ EditorView27.atomicRanges.of((view) => view.state.field(field))
6651
6697
  ]
6698
+ }),
6699
+ ViewPlugin19.define((view) => {
6700
+ viewRef.current = view;
6701
+ return {
6702
+ destroy() {
6703
+ viewRef.current = void 0;
6704
+ }
6705
+ };
6652
6706
  })
6653
6707
  ];
6654
6708
  };
6655
- 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) => {
6656
6726
  const builder = new RangeSetBuilder6();
6657
6727
  syntaxTree10(state).iterate({
6658
6728
  enter: (node) => {
@@ -6664,8 +6734,13 @@ var buildDecorations3 = (state, options) => {
6664
6734
  case "Link": {
6665
6735
  const link = getLinkRef(state, node.node);
6666
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;
6667
6742
  builder.add(node.from, node.to, Decoration13.replace({
6668
- widget: new PreviewInlineWidget(options, link),
6743
+ widget: new PreviewInlineWidget(options, displayLink),
6669
6744
  side: 1
6670
6745
  }));
6671
6746
  }
@@ -6757,7 +6832,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6757
6832
  };
6758
6833
 
6759
6834
  // src/extensions/replacer.ts
6760
- import { EditorView as EditorView27 } from "@codemirror/view";
6835
+ import { EditorView as EditorView28 } from "@codemirror/view";
6761
6836
  var defaultReplacements = [
6762
6837
  {
6763
6838
  input: "--",
@@ -6820,7 +6895,7 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6820
6895
  const sortedReplacements = [
6821
6896
  ...replacements
6822
6897
  ].sort((a, b) => b.input.length - a.input.length);
6823
- return EditorView27.inputHandler.of((view, from, to, insert) => {
6898
+ return EditorView28.inputHandler.of((view, from, to, insert) => {
6824
6899
  if (insert.length !== 1) {
6825
6900
  return false;
6826
6901
  }
@@ -6854,12 +6929,69 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6854
6929
  });
6855
6930
  };
6856
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
+
6857
6989
  // src/extensions/submit.ts
6858
6990
  import { Prec as Prec6 } from "@codemirror/state";
6859
- import { keymap as keymap12 } from "@codemirror/view";
6991
+ import { keymap as keymap13 } from "@codemirror/view";
6860
6992
  var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6861
6993
  return [
6862
- Prec6.highest(keymap12.of([
6994
+ Prec6.highest(keymap13.of([
6863
6995
  {
6864
6996
  key: "Enter",
6865
6997
  preventDefault: true,
@@ -6904,6 +7036,7 @@ var submit = ({ fireIfEmpty = false, onSubmit } = {}) => {
6904
7036
  // src/extensions/tags/extended-markdown.ts
6905
7037
  import { xmlLanguage } from "@codemirror/lang-xml";
6906
7038
  import { parseMixed } from "@lezer/common";
7039
+ var escapeRegExpSource = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6907
7040
  var extendedMarkdown = ({ registry } = {}) => {
6908
7041
  return [
6909
7042
  createMarkdownExtensions({
@@ -6915,13 +7048,65 @@ var extendedMarkdown = ({ registry } = {}) => {
6915
7048
  {
6916
7049
  name: "SetextHeading",
6917
7050
  parse: () => false
6918
- }
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)
6919
7055
  ]
6920
7056
  }
6921
7057
  ]
6922
7058
  })
6923
7059
  ];
6924
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
+ };
6925
7110
  var mixedParser = (registry) => {
6926
7111
  const customTags = Object.keys(registry ?? {});
6927
7112
  const tagPattern = new RegExp(`<(${customTags.join("|")})`);
@@ -6948,226 +7133,800 @@ var mixedParser = (registry) => {
6948
7133
  parser: xmlLanguage.parser
6949
7134
  };
6950
7135
  }
6951
- 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);
6952
7514
  }
6953
7515
  }
6954
- return null;
6955
7516
  });
6956
- };
6957
-
6958
- // src/extensions/tags/streamer.ts
6959
- import { StateEffect as StateEffect8, StateField as StateField12 } from "@codemirror/state";
6960
- import { Decoration as Decoration14, EditorView as EditorView28, ViewPlugin as ViewPlugin18, WidgetType as WidgetType9 } from "@codemirror/view";
6961
- import { Domino as Domino3 } from "@dxos/ui";
6962
- import { isTruthy as isTruthy4 } from "@dxos/util";
6963
- var BLINK_RATE = 2e3;
6964
- var streamer = (options = {}) => {
6965
7517
  return [
6966
- options.cursor && cursor(),
6967
- options.fadeIn && fadeIn(typeof options.fadeIn === "object" ? options.fadeIn : {})
6968
- ].filter(isTruthy4);
6969
- };
6970
- var cursor = () => {
6971
- const hideCursor = StateEffect8.define();
6972
- const showCursor = StateField12.define({
6973
- 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
+ }),
6974
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
+ }
6975
7551
  for (const effect of tr.effects) {
6976
7552
  if (effect.is(hideCursor)) {
6977
- return false;
7553
+ return {
7554
+ ...value,
7555
+ visible: false
7556
+ };
6978
7557
  }
6979
7558
  }
6980
- if (tr.docChanged) {
6981
- return true;
6982
- }
6983
7559
  return value;
6984
7560
  }
6985
7561
  });
6986
- 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 {
6987
7582
  view;
6988
- timer;
7583
+ _timer;
6989
7584
  constructor(view) {
6990
7585
  this.view = view;
6991
7586
  }
6992
7587
  update(update2) {
6993
- if (update2.docChanged) {
6994
- clearTimeout(this.timer);
6995
- 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(() => {
6996
7596
  this.view.dispatch({
6997
7597
  effects: hideCursor.of(null)
6998
7598
  });
6999
- }, BLINK_RATE);
7599
+ this._timer = void 0;
7600
+ }, CURSOR_LINGER);
7000
7601
  }
7001
7602
  }
7002
7603
  destroy() {
7003
- clearTimeout(this.timer);
7604
+ clearTimeout(this._timer);
7004
7605
  }
7005
7606
  });
7006
- const cursorDecoration = StateField12.define({
7007
- create: () => Decoration14.none,
7008
- update: (_decorations, tr) => {
7009
- const show = tr.state.field(showCursor);
7010
- if (!show) {
7011
- return Decoration14.none;
7012
- }
7013
- const endPos = tr.state.doc.length;
7014
- return Decoration14.set([
7015
- Decoration14.widget({
7016
- widget: new CursorWidget(),
7017
- side: 1
7018
- }).range(endPos)
7019
- ]);
7020
- },
7021
- provide: (f) => EditorView28.decorations.from(f)
7022
- });
7023
7607
  return [
7024
- showCursor,
7025
- timerPlugin,
7026
- cursorDecoration
7608
+ visibilityField,
7609
+ decorationField,
7610
+ timerPlugin
7027
7611
  ];
7028
7612
  };
7029
- 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
+ }
7030
7619
  toDOM() {
7031
- const inner = Domino3.of("span").text("\u258F").style({
7032
- animation: "blink 2s infinite"
7620
+ const inner = Domino3.of("span").text("\u2217").style({
7621
+ animation: "blink 1s infinite",
7622
+ animationDelay: "250ms"
7033
7623
  });
7034
7624
  return Domino3.of("span").style({
7035
7625
  opacity: "0.8"
7036
- }).children(inner).root;
7626
+ }).append(inner).root;
7037
7627
  }
7038
7628
  };
7039
- var fadeIn = (options = {}) => {
7040
- const FADE_IN_DURATION = 1e3;
7041
- const DEFAULT_REMOVAL_DELAY = 3e3;
7042
- const removalDelay = options.removalDelay ?? DEFAULT_REMOVAL_DELAY;
7043
- const removeDecoration = StateEffect8.define();
7044
- const fadeField = StateField12.define({
7045
- create: () => Decoration14.none,
7046
- update: (decorations2, tr) => {
7047
- let next = decorations2;
7048
- for (const effect of tr.effects) {
7049
- if (effect.is(removeDecoration)) {
7050
- const target = effect.value;
7051
- next = next.update({
7052
- filter: (from, to) => !(from === target.from && to === target.to)
7053
- });
7054
- }
7055
- }
7056
- if (!tr.docChanged) {
7057
- 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
+ };
7058
7665
  }
7059
- let isReset = tr.state.doc.length === 0;
7060
- if (!isReset) {
7061
- tr.changes.iterChanges((fromA, toA) => {
7062
- if (fromA === 0 && toA === tr.startState.doc.length) {
7063
- isReset = true;
7064
- }
7065
- });
7666
+ if (buffer[close - 1] === "/") {
7667
+ return {
7668
+ count: close + 1 - start
7669
+ };
7066
7670
  }
7067
- if (isReset) {
7068
- 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;
7069
7726
  }
7070
- const add = [];
7071
- tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7072
- 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") {
7073
7772
  return;
7074
7773
  }
7075
- if (toA === tr.startState.doc.length && inserted.length > 0) {
7076
- add.push(Decoration14.mark({
7077
- class: "cm-fade-in"
7078
- }).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;
7079
7779
  }
7080
- });
7081
- return next.update({
7082
- add
7083
- });
7084
- },
7085
- provide: (f) => EditorView28.decorations.from(f)
7086
- });
7087
- const timerPlugin = ViewPlugin18.fromClass(class {
7088
- view;
7089
- // Map a simple key "from-to" to timer id.
7090
- _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;
7091
7811
  constructor(view) {
7092
- this.view = view;
7812
+ this.decorations = buildDecorations5(view);
7093
7813
  }
7094
7814
  update(update2) {
7095
- if (!update2.docChanged) {
7096
- return;
7815
+ if (update2.docChanged) {
7816
+ this.decorations = buildDecorations5(update2.view);
7097
7817
  }
7098
- update2.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
7099
- 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));
7100
7864
  return;
7101
7865
  }
7102
- const key = `${fromB}-${toB}`;
7103
- if (this._timers.has(key)) {
7104
- 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
+ }
7105
7883
  }
7106
- const totalDelay = FADE_IN_DURATION + removalDelay;
7107
- const id = setTimeout(() => {
7108
- this.view.dispatch({
7109
- effects: removeDecoration.of({
7110
- from: fromB,
7111
- to: toB
7112
- })
7113
- });
7114
- this._timers.delete(key);
7115
- }, totalDelay);
7116
- this._timers.set(key, id);
7117
- });
7118
- }
7119
- destroy() {
7120
- for (const id of this._timers.values()) {
7121
- clearTimeout(id);
7122
7884
  }
7123
- this._timers.clear();
7124
- }
7125
- });
7885
+ });
7886
+ return Decoration17.set(ranges, true);
7887
+ };
7126
7888
  return [
7127
- fadeField,
7128
- timerPlugin,
7129
- EditorView28.theme({
7130
- ".cm-line > span": {
7131
- opacity: "0.8"
7132
- },
7133
- ".cm-fade-in": {
7134
- animation: "fade-in 3s ease-out forwards"
7135
- },
7136
- "@keyframes fade-in": {
7137
- "0%": {
7138
- opacity: "0"
7139
- },
7140
- "80%": {
7141
- opacity: "1"
7142
- },
7143
- "100%": {
7144
- 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);
7145
7897
  }
7146
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": {}
7147
7913
  })
7148
7914
  ];
7149
7915
  };
7150
7916
 
7151
7917
  // src/extensions/tags/xml-tags.ts
7152
7918
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7153
- import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect9, StateField as StateField13 } from "@codemirror/state";
7154
- 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";
7155
7921
  import { invariant as invariant7 } from "@dxos/invariant";
7156
7922
  import { log as log11 } from "@dxos/log";
7923
+ import { Domino as Domino4 } from "@dxos/ui";
7157
7924
 
7158
7925
  // src/extensions/tags/xml-util.ts
7159
7926
  import { invariant as invariant6 } from "@dxos/invariant";
7160
7927
  var __dxlog_file16 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-util.ts";
7161
7928
  var nodeToJson = (state, node) => {
7162
- invariant6(node.type.name === "Element", "Node is not an Element", {
7163
- F: __dxlog_file16,
7164
- L: 18,
7165
- S: void 0,
7166
- A: [
7167
- "node.type.name === 'Element'",
7168
- "'Node is not an Element'"
7169
- ]
7170
- });
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'"] });
7171
7930
  const openTag = node.node.getChild("OpenTag") || node.node.getChild("SelfClosingTag");
7172
7931
  if (openTag) {
7173
7932
  const tagName = openTag.getChild("TagName");
@@ -7199,13 +7958,23 @@ var nodeToJson = (state, node) => {
7199
7958
  if (node.type.name === "Element" && openTag.type.name !== "SelfClosingTag") {
7200
7959
  const children = [];
7201
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
+ };
7202
7972
  while (child) {
7203
7973
  if (child.type.name !== "OpenTag" && child.type.name !== "CloseTag") {
7204
7974
  if (child.type.name === "Text") {
7205
- const text = state.doc.sliceString(child.from, child.to).trim();
7206
- if (text) {
7207
- children.push(text);
7208
- }
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)));
7209
7978
  } else if (child.type.name === "Element") {
7210
7979
  const data = nodeToJson(state, child);
7211
7980
  if (data) {
@@ -7215,26 +7984,63 @@ var nodeToJson = (state, node) => {
7215
7984
  }
7216
7985
  child = child.nextSibling;
7217
7986
  }
7987
+ if (children.length > 0 && typeof children[0] === "string") {
7988
+ children[0] = children[0].trimStart();
7989
+ }
7218
7990
  if (children.length > 0) {
7219
- 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;
7220
8000
  }
7221
8001
  }
7222
8002
  return tag;
7223
8003
  }
7224
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
+ };
7225
8029
 
7226
8030
  // src/extensions/tags/xml-tags.ts
7227
8031
  var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7228
- var navigatePreviousEffect = StateEffect9.define();
7229
- var navigateNextEffect = StateEffect9.define();
8032
+ var navigatePreviousEffect = StateEffect12.define();
8033
+ var navigateNextEffect = StateEffect12.define();
7230
8034
  var getXmlTextChild = (children) => {
7231
8035
  const child = children?.[0];
7232
8036
  return typeof child === "string" ? child : null;
7233
8037
  };
7234
- var xmlTagContextEffect = StateEffect9.define();
7235
- var xmlTagResetEffect = StateEffect9.define();
7236
- var xmlTagUpdateEffect = StateEffect9.define();
7237
- 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({
7238
8044
  create: () => void 0,
7239
8045
  update: (value, tr) => {
7240
8046
  for (const effect of tr.effects) {
@@ -7245,7 +8051,7 @@ var widgetContextStateField = StateField13.define({
7245
8051
  return value;
7246
8052
  }
7247
8053
  });
7248
- var widgetStateMapStateField = StateField13.define({
8054
+ var widgetStateMapStateField = StateField14.define({
7249
8055
  create: () => ({}),
7250
8056
  update: (map, tr) => {
7251
8057
  for (const effect of tr.effects) {
@@ -7257,12 +8063,7 @@ var widgetStateMapStateField = StateField13.define({
7257
8063
  log11("widget updated", {
7258
8064
  id,
7259
8065
  value
7260
- }, {
7261
- F: __dxlog_file17,
7262
- L: 153,
7263
- S: void 0,
7264
- C: (f, a) => f(...a)
7265
- });
8066
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 59, S: void 0 });
7266
8067
  const state = typeof value === "function" ? value(map[id]) : value;
7267
8068
  return {
7268
8069
  ...map,
@@ -7292,12 +8093,7 @@ var createWidgetMap = (setWidgets) => {
7292
8093
  log11("widget mounted", {
7293
8094
  id: state.id,
7294
8095
  tag: state.props._tag
7295
- }, {
7296
- F: __dxlog_file17,
7297
- L: 206,
7298
- S: void 0,
7299
- C: (f, a) => f(...a)
7300
- });
8096
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 101, S: void 0 });
7301
8097
  widgets.set(state.id, state);
7302
8098
  setWidgets?.([
7303
8099
  ...widgets.values()
@@ -7308,12 +8104,7 @@ var createWidgetMap = (setWidgets) => {
7308
8104
  log11("widget unmounted", {
7309
8105
  id,
7310
8106
  tag: state?.props._tag
7311
- }, {
7312
- F: __dxlog_file17,
7313
- L: 212,
7314
- S: void 0,
7315
- C: (f, a) => f(...a)
7316
- });
8107
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 112, S: void 0 });
7317
8108
  widgets.delete(id);
7318
8109
  setWidgets?.([
7319
8110
  ...widgets.values()
@@ -7322,7 +8113,7 @@ var createWidgetMap = (setWidgets) => {
7322
8113
  };
7323
8114
  return notifier;
7324
8115
  };
7325
- var keyHandlers = keymap13.of([
8116
+ var keyHandlers = keymap14.of([
7326
8117
  {
7327
8118
  key: "Mod-ArrowUp",
7328
8119
  run: (view) => {
@@ -7343,7 +8134,7 @@ var keyHandlers = keymap13.of([
7343
8134
  }
7344
8135
  ]);
7345
8136
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7346
- return EditorView29.updateListener.of((update2) => {
8137
+ return EditorView32.updateListener.of((update2) => {
7347
8138
  update2.transactions.forEach((transaction) => {
7348
8139
  for (const effect of transaction.effects) {
7349
8140
  if (effect.is(navigatePreviousEffect)) {
@@ -7371,7 +8162,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7371
8162
  anchor: line.from,
7372
8163
  head: line.from
7373
8164
  },
7374
- effects: scrollerLineEffect.of({
8165
+ effects: crawlerLineEffect.of({
7375
8166
  line: line.number - 1,
7376
8167
  offset: -16
7377
8168
  })
@@ -7404,7 +8195,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7404
8195
  anchor: line.to,
7405
8196
  head: line.to
7406
8197
  },
7407
- effects: scrollerLineEffect.of({
8198
+ effects: crawlerLineEffect.of({
7408
8199
  line: line.number - 1,
7409
8200
  offset: -16
7410
8201
  })
@@ -7416,7 +8207,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7416
8207
  anchor: line.to,
7417
8208
  head: line.to
7418
8209
  },
7419
- effects: scrollerLineEffect.of({
8210
+ effects: crawlerLineEffect.of({
7420
8211
  line: line.number - 1,
7421
8212
  position: "end"
7422
8213
  })
@@ -7428,7 +8219,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
7428
8219
  });
7429
8220
  });
7430
8221
  };
7431
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin19.fromClass(class {
8222
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
7432
8223
  update(update2) {
7433
8224
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
7434
8225
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -7455,14 +8246,14 @@ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin1
7455
8246
  }
7456
8247
  }
7457
8248
  });
7458
- var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.define({
8249
+ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.define({
7459
8250
  create: (state) => {
7460
8251
  return buildDecorations4(state, {
7461
8252
  from: 0,
7462
8253
  to: state.doc.length
7463
8254
  }, registry, notifier);
7464
8255
  },
7465
- update: ({ from, decorations: decorations2 }, tr) => {
8256
+ update: ({ from, streamingFrom, decorations: decorations2 }, tr) => {
7466
8257
  for (const effect of tr.effects) {
7467
8258
  if (effect.is(xmlTagResetEffect)) {
7468
8259
  if (tr.docChanged) {
@@ -7473,7 +8264,7 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7473
8264
  }
7474
8265
  return {
7475
8266
  from: 0,
7476
- decorations: Decoration15.none
8267
+ decorations: Decoration18.none
7477
8268
  };
7478
8269
  }
7479
8270
  }
@@ -7484,24 +8275,23 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7484
8275
  log11("document reset", {
7485
8276
  from,
7486
8277
  to: state.doc.length
7487
- }, {
7488
- F: __dxlog_file17,
7489
- L: 374,
7490
- S: void 0,
7491
- C: (f, a) => f(...a)
7492
- });
8278
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 298, S: void 0 });
7493
8279
  return buildDecorations4(state, {
7494
8280
  from: 0,
7495
8281
  to: state.doc.length
7496
8282
  }, registry, notifier);
7497
8283
  } else {
8284
+ const rebuildFrom = streamingFrom ?? from;
7498
8285
  const result = buildDecorations4(state, {
7499
- from,
8286
+ from: rebuildFrom,
7500
8287
  to: state.doc.length
7501
8288
  }, registry, notifier);
7502
8289
  return {
7503
8290
  from: result.from,
8291
+ streamingFrom: result.streamingFrom,
7504
8292
  decorations: decorations2.update({
8293
+ // Remove old streaming decorations — they are rebuilt each tick.
8294
+ filter: (_f, _t, deco) => !deco.spec.streaming,
7505
8295
  add: decorationSetToArray(result.decorations)
7506
8296
  })
7507
8297
  };
@@ -7509,12 +8299,13 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField13.def
7509
8299
  }
7510
8300
  return {
7511
8301
  from,
8302
+ streamingFrom,
7512
8303
  decorations: decorations2
7513
8304
  };
7514
8305
  },
7515
8306
  provide: (field) => [
7516
- EditorView29.decorations.from(field, (v) => v.decorations),
7517
- 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)
7518
8309
  ]
7519
8310
  });
7520
8311
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -7525,10 +8316,11 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7525
8316
  if (!tree || tree.type.name === "Program" && tree.length === 0) {
7526
8317
  return {
7527
8318
  from: range.from,
7528
- decorations: Decoration15.none
8319
+ decorations: Decoration18.none
7529
8320
  };
7530
8321
  }
7531
8322
  let last = range.from;
8323
+ let streamingFrom;
7532
8324
  tree.iterate({
7533
8325
  from: range.from,
7534
8326
  to: range.to,
@@ -7541,21 +8333,26 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7541
8333
  if (args) {
7542
8334
  const def = registry[args._tag];
7543
8335
  if (def) {
8336
+ if (def.streaming && !node.node.getChild("CloseTag")) {
8337
+ return false;
8338
+ }
7544
8339
  const { block, factory, Component } = def;
7545
- const widgetState = args.id ? widgetStateMap[args.id] : void 0;
7546
8340
  const nodeRange = {
7547
8341
  from: node.node.from,
7548
8342
  to: node.node.to
7549
8343
  };
8344
+ const widgetId = xmlWidgetId(args.id, def.streaming ? `cm-xml-${nodeRange.from}` : `cm-xml-${nodeRange.from}-${nodeRange.to}`);
8345
+ const widgetState = widgetStateMap[widgetId];
7550
8346
  const props = {
7551
- context,
8347
+ id: widgetId,
7552
8348
  range: nodeRange,
8349
+ context,
7553
8350
  ...args,
7554
8351
  ...widgetState
7555
8352
  };
7556
- 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;
7557
8354
  if (widget) {
7558
- builder.add(nodeRange.from, nodeRange.to, Decoration15.replace({
8355
+ builder.add(nodeRange.from, nodeRange.to, Decoration18.replace({
7559
8356
  widget,
7560
8357
  block,
7561
8358
  atomic: true,
@@ -7567,20 +8364,72 @@ var buildDecorations4 = (state, range, registry, notifier) => {
7567
8364
  }
7568
8365
  }
7569
8366
  } catch (err) {
7570
- log11.catch(err, void 0, {
7571
- F: __dxlog_file17,
7572
- L: 459,
7573
- S: void 0,
7574
- C: (f, a) => f(...a)
7575
- });
8367
+ log11.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file17, L: 401, S: void 0 });
7576
8368
  }
7577
8369
  return false;
7578
8370
  }
7579
8371
  }
7580
8372
  }
7581
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
+ }
7582
8430
  return {
7583
8431
  from: last,
8432
+ streamingFrom,
7584
8433
  decorations: builder.finish()
7585
8434
  };
7586
8435
  };
@@ -7589,108 +8438,62 @@ var PlaceholderWidget2 = class extends WidgetType10 {
7589
8438
  Component;
7590
8439
  props;
7591
8440
  notifier;
7592
- _root = null;
7593
- constructor(id, Component, props, notifier) {
7594
- super(), this.id = id, this.Component = Component, this.props = props, this.notifier = notifier;
7595
- invariant7(id, void 0, {
7596
- F: __dxlog_file17,
7597
- L: 485,
7598
- S: this,
7599
- A: [
7600
- "id",
7601
- ""
7602
- ]
7603
- });
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", ""] });
7604
8447
  }
7605
8448
  get root() {
7606
- return this._root;
8449
+ return this.#root;
7607
8450
  }
7608
8451
  eq(other) {
8452
+ if (this.streaming) {
8453
+ return false;
8454
+ }
7609
8455
  return this.id === other.id;
7610
8456
  }
7611
8457
  ignoreEvent() {
7612
8458
  return true;
7613
8459
  }
7614
- toDOM(_view) {
7615
- 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
+ });
7616
8479
  this.notifier.mounted({
7617
8480
  id: this.id,
7618
- root: this._root,
7619
- props: this.props,
8481
+ root: this.#root,
8482
+ props,
7620
8483
  Component: this.Component
7621
8484
  });
7622
- return this._root;
8485
+ return true;
7623
8486
  }
7624
8487
  destroy(_dom) {
7625
8488
  this.notifier.unmounted(this.id);
7626
- this._root = null;
8489
+ this.#root = null;
8490
+ this.#view = void 0;
7627
8491
  }
7628
8492
  };
7629
-
7630
- // src/extensions/typewriter.ts
7631
- import { keymap as keymap14 } from "@codemirror/view";
7632
- var defaultItems = [
7633
- "hello world!",
7634
- "this is a test.",
7635
- "this is [DXOS](https://dxos.org)"
7636
- ];
7637
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
7638
- let t;
7639
- let idx = 0;
7640
- return [
7641
- keymap14.of([
7642
- {
7643
- // Reset.
7644
- key: "alt-meta-'",
7645
- run: () => {
7646
- clearTimeout(t);
7647
- idx = 0;
7648
- return true;
7649
- }
7650
- },
7651
- {
7652
- // Next prompt.
7653
- // TODO(burdon): Press 1-9 to select prompt?
7654
- key: "Shift-Meta-'",
7655
- run: (view) => {
7656
- clearTimeout(t);
7657
- const text = items[idx++];
7658
- if (idx === items?.length) {
7659
- idx = 0;
7660
- }
7661
- let i = 0;
7662
- const insert = (d = 0) => {
7663
- t = setTimeout(() => {
7664
- const pos = view.state.selection.main.head;
7665
- view.dispatch({
7666
- changes: {
7667
- from: pos,
7668
- insert: text[i++]
7669
- },
7670
- selection: {
7671
- anchor: pos + 1
7672
- }
7673
- });
7674
- if (i < text.length) {
7675
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
7676
- }
7677
- }, d);
7678
- };
7679
- insert();
7680
- return true;
7681
- }
7682
- }
7683
- ])
7684
- ];
7685
- };
7686
8493
  export {
7687
8494
  Cursor,
7688
- EditorInputMode,
7689
- EditorInputModes,
7690
- EditorState3 as EditorState,
7691
- EditorView30 as EditorView,
7692
- EditorViewMode,
7693
- EditorViewModes,
8495
+ EditorState4 as EditorState,
8496
+ EditorView33 as EditorView,
7694
8497
  Inline,
7695
8498
  InputModeExtensions,
7696
8499
  List,
@@ -7707,6 +8510,7 @@ export {
7707
8510
  addStyle,
7708
8511
  annotations,
7709
8512
  autoScroll,
8513
+ autoScrollEffect,
7710
8514
  autocomplete,
7711
8515
  automerge,
7712
8516
  awareness,
@@ -7720,7 +8524,11 @@ export {
7720
8524
  commentClickedEffect,
7721
8525
  comments,
7722
8526
  commentsState,
8527
+ compactSlots,
7723
8528
  convertTreeToJson,
8529
+ crawler,
8530
+ crawlerActiveEffect,
8531
+ crawlerLineEffect,
7724
8532
  createBasicExtensions,
7725
8533
  createComment,
7726
8534
  createCrawler,
@@ -7743,12 +8551,12 @@ export {
7743
8551
  defaultThemeSlots,
7744
8552
  deleteItem,
7745
8553
  documentId,
8554
+ documentSlots,
7746
8555
  dropFile,
8556
+ editorClassNames,
7747
8557
  editorInputMode,
7748
- editorSlots,
7749
- editorWidth,
7750
- editorWithToolbarLayout,
7751
8558
  extendedMarkdown,
8559
+ fader,
7752
8560
  filterChars,
7753
8561
  flattenRect,
7754
8562
  focus,
@@ -7784,6 +8592,7 @@ export {
7784
8592
  markdownTagsExtensions,
7785
8593
  matchCompletion,
7786
8594
  mention,
8595
+ mobileSlots,
7787
8596
  modalStateEffect,
7788
8597
  modalStateField,
7789
8598
  moveItemDown,
@@ -7803,11 +8612,10 @@ export {
7803
8612
  removeList,
7804
8613
  removeStyle,
7805
8614
  replacer,
8615
+ scrollPastEnd,
7806
8616
  scrollThreadIntoView,
7807
8617
  scrollToLine,
7808
8618
  scroller,
7809
- scrollerCrawlEffect,
7810
- scrollerLineEffect,
7811
8619
  selectionState,
7812
8620
  setBlockquote,
7813
8621
  setComments,
@@ -7815,9 +8623,8 @@ export {
7815
8623
  setSelection,
7816
8624
  setStyle,
7817
8625
  singleValueFacet,
7818
- stackItemContentEditorClassNames,
8626
+ snippets2 as snippets,
7819
8627
  staticCompletion,
7820
- streamer,
7821
8628
  submit,
7822
8629
  tabbable,
7823
8630
  table,
@@ -7836,7 +8643,12 @@ export {
7836
8643
  treeFacet,
7837
8644
  typeahead,
7838
8645
  typewriter,
8646
+ typewriterBypass,
8647
+ typewriterDrainingEffect,
7839
8648
  wrapWithCatch,
8649
+ xmlBlockDecoration,
8650
+ xmlElementLength,
8651
+ xmlFormatting,
7840
8652
  xmlTagContextEffect,
7841
8653
  xmlTagResetEffect,
7842
8654
  xmlTagUpdateEffect,