@dxos/ui-editor 0.8.4-main.fcc0d83b33 → 0.8.4-staging.60fe92afc8

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 (81) hide show
  1. package/LICENSE +102 -5
  2. package/README.md +1 -1
  3. package/dist/lib/browser/index.mjs +796 -614
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/node-esm/index.mjs +796 -614
  7. package/dist/lib/node-esm/index.mjs.map +4 -4
  8. package/dist/lib/node-esm/meta.json +1 -1
  9. package/dist/types/src/defaults.d.ts.map +1 -1
  10. package/dist/types/src/extensions/autocomplete/placeholder.d.ts +5 -2
  11. package/dist/types/src/extensions/autocomplete/placeholder.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
  13. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  14. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  15. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  16. package/dist/types/src/extensions/automerge/sync.d.ts +1 -1
  17. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  19. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  20. package/dist/types/src/extensions/comments.d.ts +19 -1
  21. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  22. package/dist/types/src/extensions/factories.d.ts +1 -1
  23. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  24. package/dist/types/src/extensions/index.d.ts +2 -3
  25. package/dist/types/src/extensions/index.d.ts.map +1 -1
  26. package/dist/types/src/extensions/json.d.ts +1 -1
  27. package/dist/types/src/extensions/json.d.ts.map +1 -1
  28. package/dist/types/src/extensions/markdown/image.d.ts +13 -2
  29. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  30. package/dist/types/src/extensions/markdown/image.test.d.ts +2 -0
  31. package/dist/types/src/extensions/markdown/image.test.d.ts.map +1 -0
  32. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  33. package/dist/types/src/extensions/preview/preview.d.ts +2 -2
  34. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  35. package/dist/types/src/extensions/scrolling/auto-scroll.d.ts.map +1 -0
  36. package/dist/types/src/extensions/{scroller.d.ts → scrolling/crawler.d.ts} +23 -8
  37. package/dist/types/src/extensions/scrolling/crawler.d.ts.map +1 -0
  38. package/dist/types/src/extensions/scrolling/index.d.ts +6 -0
  39. package/dist/types/src/extensions/scrolling/index.d.ts.map +1 -0
  40. package/dist/types/src/extensions/scrolling/scroll-past-end.d.ts.map +1 -0
  41. package/dist/types/src/extensions/scrolling/scrollbar-autohide.d.ts +15 -0
  42. package/dist/types/src/extensions/scrolling/scrollbar-autohide.d.ts.map +1 -0
  43. package/dist/types/src/extensions/scrolling/scroller.d.ts +16 -0
  44. package/dist/types/src/extensions/scrolling/scroller.d.ts.map +1 -0
  45. package/dist/types/src/extensions/spacing.d.ts +3 -0
  46. package/dist/types/src/extensions/spacing.d.ts.map +1 -0
  47. package/dist/types/src/styles/theme.d.ts.map +1 -1
  48. package/dist/types/tsconfig.tsbuildinfo +1 -1
  49. package/package.json +54 -53
  50. package/src/defaults.ts +1 -2
  51. package/src/extensions/autocomplete/placeholder.ts +37 -18
  52. package/src/extensions/automerge/automerge.test.tsx +9 -3
  53. package/src/extensions/automerge/automerge.ts +1 -1
  54. package/src/extensions/automerge/cursor.ts +1 -1
  55. package/src/extensions/automerge/sync.ts +1 -1
  56. package/src/extensions/automerge/update-automerge.ts +1 -1
  57. package/src/extensions/comments.ts +54 -31
  58. package/src/extensions/factories.ts +2 -2
  59. package/src/extensions/index.ts +2 -3
  60. package/src/extensions/json.ts +1 -1
  61. package/src/extensions/markdown/decorate.ts +1 -1
  62. package/src/extensions/markdown/image.test.ts +54 -0
  63. package/src/extensions/markdown/image.ts +70 -9
  64. package/src/extensions/markdown/link.ts +7 -2
  65. package/src/extensions/outliner/outliner.ts +1 -1
  66. package/src/extensions/preview/preview.ts +14 -12
  67. package/src/extensions/{auto-scroll.ts → scrolling/auto-scroll.ts} +57 -28
  68. package/src/extensions/{scroller.ts → scrolling/crawler.ts} +48 -18
  69. package/src/extensions/scrolling/index.ts +9 -0
  70. package/src/extensions/{scroll-past-end.ts → scrolling/scroll-past-end.ts} +6 -6
  71. package/src/extensions/scrolling/scrollbar-autohide.ts +61 -0
  72. package/src/extensions/scrolling/scroller.ts +27 -0
  73. package/src/extensions/spacing.ts +15 -0
  74. package/src/extensions/tags/xml-formatting.ts +1 -1
  75. package/src/extensions/tags/xml-tags.ts +4 -4
  76. package/src/styles/theme.ts +11 -10
  77. package/dist/types/src/extensions/auto-scroll.d.ts.map +0 -1
  78. package/dist/types/src/extensions/scroll-past-end.d.ts.map +0 -1
  79. package/dist/types/src/extensions/scroller.d.ts.map +0 -1
  80. /package/dist/types/src/extensions/{auto-scroll.d.ts → scrolling/auto-scroll.d.ts} +0 -0
  81. /package/dist/types/src/extensions/{scroll-past-end.d.ts → scrolling/scroll-past-end.d.ts} +0 -0
@@ -1,12 +1,12 @@
1
1
  // src/index.ts
2
2
  import { EditorState as EditorState4 } from "@codemirror/state";
3
- import { EditorView as EditorView33, keymap as keymap15 } from "@codemirror/view";
3
+ import { EditorView as EditorView35, keymap as keymap15 } from "@codemirror/view";
4
4
  import { tags as tags2 } from "@lezer/highlight";
5
5
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
6
6
 
7
7
  // src/defaults.ts
8
8
  import { mx } from "@dxos/ui-theme";
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");
9
+ var editorClassNames = (role) => mx("dx-attention-surface 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
10
  var documentSlots = {
11
11
  content: {
12
12
  /**
@@ -363,30 +363,37 @@ var insertAtLineStart = (view, from, insert) => {
363
363
  };
364
364
 
365
365
  // src/extensions/autocomplete/placeholder.ts
366
- var placeholder = ({ content, delay = 3e3 }) => {
366
+ var placeholder = ({ content, delay = 3e3, focusOnly = false }) => {
367
367
  const plugin = ViewPlugin3.fromClass(class {
368
368
  _timeout;
369
369
  _decorations = Decoration3.none;
370
370
  update(update2) {
371
+ if (!update2.docChanged && !update2.selectionSet && !update2.focusChanged) {
372
+ return;
373
+ }
371
374
  if (this._timeout) {
372
375
  window.clearTimeout(this._timeout);
373
376
  this._timeout = void 0;
374
377
  }
378
+ this._decorations = Decoration3.none;
379
+ if (focusOnly && !update2.view.hasFocus) {
380
+ return;
381
+ }
375
382
  const activeLine = update2.view.state.doc.lineAt(update2.view.state.selection.main.head);
376
- const isEmpty = activeLine.text.trim() === "";
377
- if (isEmpty) {
378
- const lineStart = activeLine.from;
379
- this._timeout = setTimeout(() => {
380
- this._decorations = Decoration3.set([
381
- Decoration3.widget({
382
- widget: new PlaceholderWidget(content),
383
- side: 1
384
- }).range(lineStart)
385
- ]);
386
- update2.view.update([]);
387
- }, delay);
383
+ if (activeLine.text.trim() !== "") {
384
+ return;
388
385
  }
389
- 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);
390
397
  }
391
398
  destroy() {
392
399
  if (this._timeout) {
@@ -504,357 +511,26 @@ var typeahead = ({ onComplete } = {}) => {
504
511
  ];
505
512
  };
506
513
 
507
- // src/extensions/auto-scroll.ts
508
- import { StateEffect as StateEffect2 } from "@codemirror/state";
509
- import { EditorView as EditorView5, ViewPlugin as ViewPlugin6 } from "@codemirror/view";
510
- import { addEventListener, combine, throttle } from "@dxos/async";
511
- import { Domino } from "@dxos/ui";
512
- import { getSize } from "@dxos/ui-theme";
513
-
514
- // src/extensions/scroller.ts
515
- import { StateEffect } from "@codemirror/state";
516
- import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
517
- import { log as log2 } from "@dxos/log";
518
- var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scroller.ts";
519
- var scrollerLineEffect = StateEffect.define();
520
- var scrollerCrawlEffect = StateEffect.define();
521
- var scrollToLine = (view, options) => {
522
- view.dispatch({
523
- effects: scrollerLineEffect.of(options)
524
- });
525
- };
526
- var scroller = ({ overScroll = 0 } = {}) => {
527
- const scrollPlugin = ViewPlugin5.fromClass(class ScrollerPlugin {
528
- view;
529
- crawler;
530
- constructor(view) {
531
- this.view = view;
532
- this.crawler = createCrawler(this.view);
533
- }
534
- // No-op.
535
- destroy() {
536
- this.crawler.cancel();
537
- }
538
- cancel() {
539
- this.crawler.cancel();
540
- }
541
- crawl(start = false) {
542
- if (start) {
543
- this.crawler.scroll();
544
- } else {
545
- this.crawler.cancel();
546
- }
547
- }
548
- scroll({ line, offset = 0, position, behavior = "instant" }) {
549
- const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
550
- const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
551
- const doc = this.view.state.doc;
552
- let targetScrollTop = scrollHeight - clientHeight + offset;
553
- if (line >= 0 && line <= doc.lines - 1) {
554
- const lineStart = doc.line(line + 1).from;
555
- const coords = this.view.coordsAtPos(lineStart);
556
- if (coords) {
557
- const currentScrollTop = scrollTop;
558
- const maxScrollTop = scrollHeight - clientHeight;
559
- if (position === "end") {
560
- targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
561
- } else {
562
- targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
563
- }
564
- targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
565
- }
566
- }
567
- requestAnimationFrame(() => {
568
- this.view.scrollDOM.scrollTo({
569
- top: targetScrollTop
570
- });
571
- });
572
- }
573
- });
574
- return [
575
- scrollPlugin,
576
- // Listen for effect.s
577
- EditorView4.updateListener.of((update2) => {
578
- update2.transactions.forEach((transaction) => {
579
- try {
580
- const plugin = update2.view.plugin(scrollPlugin);
581
- if (plugin) {
582
- for (const effect of transaction.effects) {
583
- if (effect.is(scrollerCrawlEffect)) {
584
- plugin.crawl(effect.value);
585
- } else if (effect.is(scrollerLineEffect)) {
586
- plugin.scroll(effect.value);
587
- }
588
- }
589
- }
590
- } catch (err) {
591
- log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 91, S: void 0 });
592
- }
593
- });
594
- }),
595
- // Styles.
596
- EditorView4.theme({
597
- ".cm-scroller": {
598
- overflowY: "scroll",
599
- // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks
600
- // expanding their TogglePanel), the browser picks a stable element near the viewport
601
- // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning
602
- // logic still has the final word when pinned (forces scrollTop to scrollHeight).
603
- overflowAnchor: "auto"
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
- // Spacer below the last text line. Implemented as a real block pseudo-element
616
- // (rather than `padding-bottom` on `.cm-content`) so it materializes in the
617
- // scroller's `scrollHeight` regardless of how `padding` is reset by the base
618
- // theme or downstream classes — this is what gives auto-scroll its head-room
619
- // so the last line stays `overScroll` px above the viewport bottom.
620
- ".cm-content::after": {
621
- content: '""',
622
- display: "block",
623
- height: `${overScroll}px`
624
- },
625
- ".cm-scroll-button": {
626
- position: "absolute",
627
- bottom: "0.5rem",
628
- right: "1rem"
629
- }
630
- })
631
- ];
632
- };
633
- function createCrawler(view, omega = 5, snapThreshold = 5, snapVelocity = 50) {
634
- const el = view.scrollDOM;
635
- let currentTop = 0;
636
- let velocity = 0;
637
- let rafId = null;
638
- let lastTime = 0;
639
- function frame(now) {
640
- const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1e3);
641
- lastTime = now;
642
- const targetTop = el.scrollHeight - el.clientHeight;
643
- const delta = targetTop - currentTop;
644
- if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {
645
- el.scrollTop = targetTop;
646
- currentTop = targetTop;
647
- velocity = 0;
648
- rafId = null;
649
- lastTime = 0;
650
- return;
651
- }
652
- const accel = omega * omega * delta - 2 * omega * velocity;
653
- velocity += accel * dt;
654
- currentTop += velocity * dt;
655
- el.scrollTop = currentTop;
656
- rafId = requestAnimationFrame(frame);
657
- }
658
- return {
659
- scroll: () => {
660
- if (rafId === null) {
661
- currentTop = el.scrollTop;
662
- lastTime = 0;
663
- rafId = requestAnimationFrame(frame);
664
- }
665
- },
666
- cancel: () => {
667
- if (rafId !== null) {
668
- cancelAnimationFrame(rafId);
669
- velocity = 0;
670
- lastTime = 0;
671
- rafId = null;
672
- }
673
- }
674
- };
675
- }
676
-
677
- // src/extensions/auto-scroll.ts
678
- var autoScrollEffect = StateEffect2.define();
679
- var autoScroll = ({ scrollOnResize = true } = {}) => {
680
- let buttonContainer;
681
- let isPinned = true;
682
- let jumpPending = false;
683
- let enabled = true;
684
- let firstUpdate = true;
685
- const setPinned = (pinned) => {
686
- buttonContainer?.classList.toggle("opacity-0", pinned);
687
- isPinned = pinned;
688
- };
689
- return [
690
- // Update listener for scrolling when content changes.
691
- EditorView5.updateListener.of((update2) => {
692
- const { view, heightChanged, state, startState } = update2;
693
- for (const tr of update2.transactions) {
694
- for (const effect of tr.effects) {
695
- if (effect.is(autoScrollEffect)) {
696
- enabled = effect.value;
697
- if (enabled) {
698
- setPinned(true);
699
- view.dispatch({
700
- effects: scrollerCrawlEffect.of(true)
701
- });
702
- } else {
703
- view.dispatch({
704
- effects: scrollerCrawlEffect.of(false)
705
- });
706
- }
707
- }
708
- }
709
- }
710
- if (!enabled) {
711
- return;
712
- }
713
- if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
714
- firstUpdate = false;
715
- jumpPending = true;
716
- requestAnimationFrame(() => {
717
- view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
718
- jumpPending = false;
719
- });
720
- return;
721
- }
722
- firstUpdate = false;
723
- if (jumpPending) {
724
- return;
725
- }
726
- if (heightChanged) {
727
- if (isPinned) {
728
- const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
729
- const delta = scrollHeight - scrollTop - clientHeight;
730
- if (delta > 0) {
731
- setPinned(true);
732
- view.dispatch({
733
- effects: scrollerCrawlEffect.of(true)
734
- });
735
- } else if (delta < -1) {
736
- setPinned(false);
737
- }
738
- } else {
739
- if (state.doc.length === 0) {
740
- setPinned(true);
741
- }
742
- }
743
- }
744
- }),
745
- // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,
746
- // window resize). Doc-driven height changes are handled by the updateListener above; this
747
- // observer covers the case where the viewport changes while the doc length is unchanged.
748
- scrollOnResize ? ViewPlugin6.fromClass(class {
749
- observer;
750
- firstObservation = true;
751
- destroyed = false;
752
- constructor(view) {
753
- const onResize = throttle(() => {
754
- if (this.destroyed || !enabled) {
755
- return;
756
- }
757
- setPinned(true);
758
- requestAnimationFrame(() => {
759
- if (this.destroyed) {
760
- return;
761
- }
762
- view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
763
- view.dispatch({
764
- effects: scrollerCrawlEffect.of(true)
765
- });
766
- });
767
- }, 100);
768
- this.observer = new ResizeObserver(() => {
769
- if (this.firstObservation) {
770
- this.firstObservation = false;
771
- return;
772
- }
773
- onResize();
774
- });
775
- this.observer.observe(view.scrollDOM);
776
- }
777
- destroy() {
778
- this.destroyed = true;
779
- this.observer.disconnect();
780
- }
781
- }) : [],
782
- // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
783
- ViewPlugin6.fromClass(class {
784
- cleanup;
785
- constructor(view) {
786
- this.cleanup = createUserScrollDetector(view.scrollDOM, throttle(() => {
787
- requestAnimationFrame(() => {
788
- const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
789
- const delta = scrollHeight - scrollTop - clientHeight;
790
- const pinned = delta === 0;
791
- setPinned(pinned);
792
- if (!pinned) {
793
- view.dispatch({
794
- effects: scrollerCrawlEffect.of(false)
795
- });
796
- }
797
- });
798
- }, 500));
799
- }
800
- destroy() {
801
- this.cleanup();
802
- }
803
- }),
804
- // Scroll button.
805
- ViewPlugin6.fromClass(class {
806
- constructor(view) {
807
- const icon = Domino.of("dx-icon").classNames(getSize(4)).attributes({
808
- icon: "ph--arrow-down--regular"
809
- });
810
- const button = Domino.of("button").classNames("dx-button bg-accent-surface").attributes({
811
- "data-density": "fine"
812
- }).append(icon).on("click", () => {
813
- setPinned(true);
814
- view.dispatch({
815
- effects: scrollerLineEffect.of({
816
- line: -1,
817
- position: "end",
818
- behavior: "smooth"
819
- })
820
- });
821
- });
822
- buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0").append(button).root;
823
- view.scrollDOM.parentElement.appendChild(buttonContainer);
824
- }
825
- })
826
- ];
827
- };
828
- function createUserScrollDetector(element, onUserScroll) {
829
- return combine(addEventListener(element, "wheel", () => onUserScroll(), {
830
- passive: true
831
- }), addEventListener(element, "pointerdown", (event) => {
832
- if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
833
- onUserScroll();
834
- }
835
- }));
836
- }
837
-
838
514
  // src/extensions/automerge/automerge.ts
839
515
  import { next as A3 } from "@automerge/automerge";
840
516
  import { StateField, Transaction as Transaction2 } from "@codemirror/state";
841
- import { EditorView as EditorView6, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
842
- import { DocAccessor } from "@dxos/echo-db";
517
+ import { EditorView as EditorView4, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
518
+ import { DocAccessor } from "@dxos/echo-client";
843
519
 
844
520
  // src/extensions/state.ts
845
521
  import { Transaction } from "@codemirror/state";
846
522
  var initialSync = Transaction.userEvent.of("initial.sync");
847
523
 
848
524
  // src/extensions/automerge/cursor.ts
849
- import { fromCursor, toCursor } from "@dxos/echo-db";
850
- import { log as log3 } from "@dxos/log";
851
- var __dxlog_file3 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
525
+ import { fromCursor, toCursor } from "@dxos/echo-client";
526
+ import { log as log2 } from "@dxos/log";
527
+ var __dxlog_file2 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/automerge/cursor.ts";
852
528
  var cursorConverter = (accessor) => ({
853
529
  toCursor: (pos, assoc) => {
854
530
  try {
855
531
  return toCursor(accessor, pos, assoc);
856
532
  } catch (err) {
857
- log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 11, S: void 0 });
533
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 11, S: void 0 });
858
534
  return "";
859
535
  }
860
536
  },
@@ -862,17 +538,17 @@ var cursorConverter = (accessor) => ({
862
538
  try {
863
539
  return fromCursor(accessor, cursor);
864
540
  } catch (err) {
865
- log3.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 19, S: void 0 });
541
+ log2.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file2, L: 19, S: void 0 });
866
542
  return 0;
867
543
  }
868
544
  }
869
545
  });
870
546
 
871
547
  // src/extensions/automerge/defs.ts
872
- import { Annotation, StateEffect as StateEffect3 } from "@codemirror/state";
548
+ import { Annotation, StateEffect } from "@codemirror/state";
873
549
  var getPath = (state, field) => state.field(field).path;
874
550
  var getLastHeads = (state, field) => state.field(field).lastHeads;
875
- var updateHeadsEffect = StateEffect3.define({});
551
+ var updateHeadsEffect = StateEffect.define({});
876
552
  var updateHeads = (newHeads) => updateHeadsEffect.of({
877
553
  newHeads
878
554
  });
@@ -883,7 +559,7 @@ var isReconcile = (tr) => {
883
559
 
884
560
  // src/extensions/automerge/sync.ts
885
561
  import { next as A2 } from "@automerge/automerge";
886
- import { log as log4 } from "@dxos/log";
562
+ import { log as log3 } from "@dxos/log";
887
563
 
888
564
  // src/extensions/automerge/update-automerge.ts
889
565
  import { next as A } from "@automerge/automerge";
@@ -1024,7 +700,7 @@ var charPath = (textPath, candidatePath) => {
1024
700
  };
1025
701
 
1026
702
  // src/extensions/automerge/sync.ts
1027
- 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";
1028
704
  var Syncer = class {
1029
705
  _handle;
1030
706
  _state;
@@ -1047,7 +723,7 @@ var Syncer = class {
1047
723
  this._pending = false;
1048
724
  }
1049
725
  onEditorChange(view) {
1050
- log4("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 35, S: this });
726
+ log3("onEditorChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 35, S: this });
1051
727
  const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
1052
728
  const newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
1053
729
  if (newHeads) {
@@ -1058,7 +734,7 @@ var Syncer = class {
1058
734
  }
1059
735
  }
1060
736
  onAutomergeChange(view) {
1061
- log4("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 47, S: this });
737
+ log3("onAutomergeChange", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file3, L: 47, S: this });
1062
738
  const oldHeads = getLastHeads(view.state, this._state);
1063
739
  const newHeads = A2.getHeads(this._handle.doc());
1064
740
  const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
@@ -1111,7 +787,7 @@ var automerge = (accessor) => {
1111
787
  // Track heads.
1112
788
  syncState,
1113
789
  // Reconcile external updates.
1114
- ViewPlugin7.fromClass(class {
790
+ ViewPlugin5.fromClass(class {
1115
791
  _view;
1116
792
  constructor(_view) {
1117
793
  this._view = _view;
@@ -1142,7 +818,7 @@ var automerge = (accessor) => {
1142
818
  };
1143
819
  }),
1144
820
  // Reconcile local updates.
1145
- EditorView6.updateListener.of(({ view, changes, transactions }) => {
821
+ EditorView4.updateListener.of(({ view, changes, transactions }) => {
1146
822
  if (!changes.empty) {
1147
823
  const isInitialSync = transactions.some((tr) => tr.annotation(Transaction2.userEvent) === initialSync.value);
1148
824
  if (!isInitialSync) {
@@ -1155,10 +831,10 @@ var automerge = (accessor) => {
1155
831
 
1156
832
  // src/extensions/awareness/awareness.ts
1157
833
  import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1158
- 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";
1159
835
  import { Event } from "@dxos/async";
1160
836
  import { Context } from "@dxos/context";
1161
- 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";
1162
838
  var dummyProvider = {
1163
839
  remoteStateChange: new Event(),
1164
840
  open: () => {
@@ -1174,14 +850,14 @@ var RemoteSelectionChangedAnnotation = Annotation2.define();
1174
850
  var awareness = (provider = dummyProvider) => {
1175
851
  return [
1176
852
  awarenessProvider.of(provider),
1177
- ViewPlugin8.fromClass(RemoteSelectionsDecorator, {
853
+ ViewPlugin6.fromClass(RemoteSelectionsDecorator, {
1178
854
  decorations: (value) => value.decorations
1179
855
  }),
1180
856
  styles
1181
857
  ];
1182
858
  };
1183
859
  var RemoteSelectionsDecorator = class {
1184
- _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 33 });
860
+ _ctx = new Context(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file4, L: 33 });
1185
861
  _cursorConverter;
1186
862
  _provider;
1187
863
  _lastAnchor;
@@ -1330,7 +1006,7 @@ var RemoteCaretWidget = class extends WidgetType3 {
1330
1006
  return true;
1331
1007
  }
1332
1008
  };
1333
- var styles = EditorView7.theme({
1009
+ var styles = EditorView5.theme({
1334
1010
  ".cm-collab-selection": {},
1335
1011
  ".cm-collab-selectionLine": {
1336
1012
  padding: 0,
@@ -1392,8 +1068,8 @@ var styles = EditorView7.theme({
1392
1068
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
1393
1069
  import { Context as Context2 } from "@dxos/context";
1394
1070
  import { invariant } from "@dxos/invariant";
1395
- import { log as log5 } from "@dxos/log";
1396
- 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";
1397
1073
  var DEBOUNCE_INTERVAL = 100;
1398
1074
  var SpaceAwarenessProvider = class {
1399
1075
  _remoteStates = /* @__PURE__ */ new Map();
@@ -1412,7 +1088,7 @@ var SpaceAwarenessProvider = class {
1412
1088
  this._info = info;
1413
1089
  }
1414
1090
  open() {
1415
- this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 28 });
1091
+ this._ctx = new Context2(void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 28 });
1416
1092
  this._postTask = new DeferredTask(this._ctx, async () => {
1417
1093
  if (this._localState) {
1418
1094
  await this._messenger.postMessage(this._channel, {
@@ -1437,9 +1113,9 @@ var SpaceAwarenessProvider = class {
1437
1113
  void this._messenger.postMessage(this._channel, {
1438
1114
  kind: "query"
1439
1115
  }).catch((err) => {
1440
- log5.debug("failed to query awareness", {
1116
+ log4.debug("failed to query awareness", {
1441
1117
  err
1442
- }, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 57, S: this });
1118
+ }, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 57, S: this });
1443
1119
  });
1444
1120
  }
1445
1121
  close() {
@@ -1451,7 +1127,7 @@ var SpaceAwarenessProvider = class {
1451
1127
  return Array.from(this._remoteStates.values());
1452
1128
  }
1453
1129
  update(position) {
1454
- invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 71, S: this, A: ["this._postTask", ""] });
1130
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 71, S: this, A: ["this._postTask", ""] });
1455
1131
  this._localState = {
1456
1132
  peerId: this._peerId,
1457
1133
  position,
@@ -1460,22 +1136,22 @@ var SpaceAwarenessProvider = class {
1460
1136
  this._postTask.schedule();
1461
1137
  }
1462
1138
  _handleQueryMessage() {
1463
- invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 80, S: this, A: ["this._postTask", ""] });
1139
+ invariant(this._postTask, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 80, S: this, A: ["this._postTask", ""] });
1464
1140
  this._postTask.schedule();
1465
1141
  }
1466
1142
  _handlePostMessage(message) {
1467
- invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1143
+ invariant(message.kind === "post", void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file5, L: 84, S: this, A: ["message.kind === 'post'", ""] });
1468
1144
  this._remoteStates.set(message.state.peerId, message.state);
1469
1145
  this.remoteStateChange.emit();
1470
1146
  }
1471
1147
  };
1472
1148
 
1473
1149
  // src/extensions/blast.ts
1474
- import { EditorView as EditorView8, keymap as keymap3 } from "@codemirror/view";
1150
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1475
1151
  import defaultsDeep from "lodash.defaultsdeep";
1476
- import { throttle as throttle2 } from "@dxos/async";
1152
+ import { throttle } from "@dxos/async";
1477
1153
  import { invariant as invariant2 } from "@dxos/invariant";
1478
- 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";
1479
1155
  var defaultOptions = {
1480
1156
  effect: 2,
1481
1157
  maxParticles: 200,
@@ -1520,7 +1196,7 @@ var blast = (options = defaultOptions) => {
1520
1196
  };
1521
1197
  return [
1522
1198
  // Cursor moved.
1523
- EditorView8.updateListener.of((update2) => {
1199
+ EditorView6.updateListener.of((update2) => {
1524
1200
  if (blaster?.node !== update2.view.scrollDOM) {
1525
1201
  if (blaster) {
1526
1202
  blaster.destroy();
@@ -1593,7 +1269,7 @@ var Blaster = class {
1593
1269
  return this._node;
1594
1270
  }
1595
1271
  initialize() {
1596
- invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1272
+ invariant2(!this._canvas && !this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 134, S: this, A: ["!this._canvas && !this._ctx", ""] });
1597
1273
  this._canvas = document.createElement("canvas");
1598
1274
  this._canvas.id = "code-blast-canvas";
1599
1275
  this._canvas.style.position = "absolute";
@@ -1622,7 +1298,7 @@ var Blaster = class {
1622
1298
  }
1623
1299
  }
1624
1300
  start() {
1625
- invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1301
+ invariant2(this._canvas && this._ctx, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file6, L: 166, S: this, A: ["this._canvas && this._ctx", ""] });
1626
1302
  this._running = true;
1627
1303
  this.loop();
1628
1304
  }
@@ -1649,11 +1325,11 @@ var Blaster = class {
1649
1325
  this.drawParticles();
1650
1326
  requestAnimationFrame(this.loop.bind(this));
1651
1327
  }
1652
- shake = throttle2(({ time }) => {
1328
+ shake = throttle(({ time }) => {
1653
1329
  this._shakeTime = this._shakeTimeMax || time;
1654
1330
  this._shakeTimeMax = time;
1655
1331
  }, 100);
1656
- spawn = throttle2(({ element, point }) => {
1332
+ spawn = throttle(({ element, point }) => {
1657
1333
  const color = getRGBComponents(element, this._options.color);
1658
1334
  const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
1659
1335
  const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
@@ -1762,9 +1438,9 @@ var random = (min, max) => {
1762
1438
 
1763
1439
  // src/extensions/blocks.ts
1764
1440
  import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1765
- 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";
1766
1442
  import { mx as mx2 } from "@dxos/ui-theme";
1767
- var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1443
+ var paragraphBlockPlugin = ViewPlugin7.fromClass(class {
1768
1444
  decorations;
1769
1445
  constructor(view) {
1770
1446
  this.decorations = this.build(view);
@@ -1823,7 +1499,7 @@ var paragraphBlockPlugin = ViewPlugin9.fromClass(class {
1823
1499
  });
1824
1500
  var blocks = () => [
1825
1501
  paragraphBlockPlugin,
1826
- EditorView9.baseTheme({
1502
+ EditorView7.baseTheme({
1827
1503
  ".cm-line.block-line": {
1828
1504
  paddingLeft: "0.75rem",
1829
1505
  paddingRight: "0.75rem",
@@ -1857,13 +1533,13 @@ var blocks = () => [
1857
1533
  ];
1858
1534
 
1859
1535
  // src/extensions/bookmarks.ts
1860
- import { Prec as Prec3, StateEffect as StateEffect4, StateField as StateField2 } from "@codemirror/state";
1536
+ import { Prec as Prec3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
1861
1537
  import { keymap as keymap4 } from "@codemirror/view";
1862
- import { log as log6 } from "@dxos/log";
1863
- var __dxlog_file8 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/bookmarks.ts";
1864
- var addBookmark = StateEffect4.define();
1865
- var removeBookmark = StateEffect4.define();
1866
- var clearBookmarks = StateEffect4.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();
1867
1543
  var bookmarks = () => {
1868
1544
  return [
1869
1545
  bookmarksField,
@@ -1872,7 +1548,7 @@ var bookmarks = () => {
1872
1548
  key: "Mod-ArrowUp",
1873
1549
  run: (view) => {
1874
1550
  const bookmarks2 = view.state.field(bookmarksField);
1875
- log6("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 18, S: void 0 });
1551
+ log5("up", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 18, S: void 0 });
1876
1552
  return true;
1877
1553
  }
1878
1554
  },
@@ -1880,7 +1556,7 @@ var bookmarks = () => {
1880
1556
  key: "Mod-ArrowDown",
1881
1557
  run: (view) => {
1882
1558
  const bookmarks2 = view.state.field(bookmarksField);
1883
- log6("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0 });
1559
+ log5("down", bookmarks2, { "~LogMeta": "~LogMeta", F: __dxlog_file7, L: 26, S: void 0 });
1884
1560
  return true;
1885
1561
  }
1886
1562
  }
@@ -1917,27 +1593,27 @@ var bookmarksField = StateField2.define({
1917
1593
 
1918
1594
  // src/extensions/comments.ts
1919
1595
  import { invertedEffects } from "@codemirror/commands";
1920
- import { StateEffect as StateEffect5, StateField as StateField3 } from "@codemirror/state";
1921
- 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";
1922
1598
  import sortBy from "lodash.sortby";
1923
1599
  import { debounce as debounce2 } from "@dxos/async";
1924
- import { log as log7 } from "@dxos/log";
1600
+ import { log as log6 } from "@dxos/log";
1925
1601
  import { isNonNullable } from "@dxos/util";
1926
1602
 
1927
1603
  // src/extensions/selection.ts
1928
1604
  import { Transaction as Transaction3 } from "@codemirror/state";
1929
- import { EditorView as EditorView10, keymap as keymap5 } from "@codemirror/view";
1605
+ import { EditorView as EditorView8, keymap as keymap5 } from "@codemirror/view";
1930
1606
  import { debounce } from "@dxos/async";
1931
1607
  import { invariant as invariant3 } from "@dxos/invariant";
1932
1608
  import { isTruthy } from "@dxos/util";
1933
- 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";
1934
1610
  var documentId = singleValueFacet();
1935
1611
  var stateRestoreAnnotation = "org.dxos.cm.state-restore";
1936
1612
  var createEditorStateTransaction = ({ scrollTo, selection }) => {
1937
1613
  return {
1938
1614
  selection,
1939
1615
  scrollIntoView: !scrollTo,
1940
- effects: scrollTo ? EditorView10.scrollIntoView(scrollTo, {
1616
+ effects: scrollTo ? EditorView8.scrollIntoView(scrollTo, {
1941
1617
  yMargin: 96
1942
1618
  }) : void 0,
1943
1619
  annotations: Transaction3.userEvent.of(stateRestoreAnnotation)
@@ -1945,12 +1621,12 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
1945
1621
  };
1946
1622
  var createEditorStateStore = (keyPrefix) => ({
1947
1623
  getState: (id) => {
1948
- invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 26, S: void 0, A: ["id", ""] });
1624
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 26, S: void 0, A: ["id", ""] });
1949
1625
  const state = localStorage.getItem(`${keyPrefix}/${id}`);
1950
1626
  return state ? JSON.parse(state) : void 0;
1951
1627
  },
1952
1628
  setState: (id, state) => {
1953
- invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 31, S: void 0, A: ["id", ""] });
1629
+ invariant3(id, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file8, L: 31, S: void 0, A: ["id", ""] });
1954
1630
  localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state));
1955
1631
  }
1956
1632
  });
@@ -1963,7 +1639,7 @@ var selectionState = ({ getState, setState } = {}) => {
1963
1639
  // setStateDebounced(id, {});
1964
1640
  // },
1965
1641
  // }),
1966
- EditorView10.updateListener.of(({ view, transactions }) => {
1642
+ EditorView8.updateListener.of(({ view, transactions }) => {
1967
1643
  const id = view.state.facet(documentId);
1968
1644
  if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
1969
1645
  return;
@@ -2002,10 +1678,10 @@ var selectionState = ({ getState, setState } = {}) => {
2002
1678
  };
2003
1679
 
2004
1680
  // src/extensions/comments.ts
2005
- var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/comments.ts";
2006
- var setComments = StateEffect5.define();
2007
- var setSelection = StateEffect5.define();
2008
- var setCommentState = StateEffect5.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();
2009
1685
  var commentsState = StateField3.define({
2010
1686
  create: (state) => ({
2011
1687
  id: state.facet(documentId),
@@ -2044,35 +1720,35 @@ var commentsState = StateField3.define({
2044
1720
  return value;
2045
1721
  }
2046
1722
  });
2047
- var styles2 = EditorView11.theme({
2048
- ".cm-comment, .cm-comment-current": {
2049
- padding: "3px 0",
2050
- color: "var(--color-cm-comment-text)",
2051
- backgroundColor: "var(--color-cm-comment-surface)"
2052
- },
2053
- ".cm-comment > span, .cm-comment-current > span": {
1723
+ var styles2 = EditorView9.theme({
1724
+ ".cm-comment > span": {
2054
1725
  boxDecorationBreak: "clone",
2055
- boxShadow: "0 0 1px 3px var(--color-cm-comment-surface)",
1726
+ boxShadow: "0 0 0 3px var(--color-cm-comment-surface)",
2056
1727
  backgroundColor: "var(--color-cm-comment-surface)",
2057
- color: "var(--color-cm-comment-text)",
1728
+ color: "var(--color-cm-comment-text) !important",
2058
1729
  cursor: "pointer"
1730
+ },
1731
+ '.cm-comment[data-current="1"] > span': {
1732
+ boxShadow: "0 0 0 3px var(--color-cm-comment-current-surface)",
1733
+ backgroundColor: "var(--color-cm-comment-current-surface)"
2059
1734
  }
2060
1735
  });
2061
1736
  var createCommentMark = (id, isCurrent) => Decoration7.mark({
2062
- class: isCurrent ? "cm-comment-current" : "cm-comment",
1737
+ class: "cm-comment",
2063
1738
  attributes: {
2064
1739
  "data-testid": "cm-comment",
2065
- "data-comment-id": id
1740
+ "data-comment-id": id,
1741
+ "data-current": isCurrent ? "1" : "0"
2066
1742
  }
2067
1743
  });
2068
- var commentsDecorations = EditorView11.decorations.compute([
1744
+ var commentsDecorations = EditorView9.decorations.compute([
2069
1745
  commentsState
2070
1746
  ], (state) => {
2071
1747
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
2072
1748
  const decorations2 = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
2073
1749
  const range = comment.range;
2074
1750
  if (!range) {
2075
- log7.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 93, S: void 0 });
1751
+ log6.warn("Invalid range:", range, { "~LogMeta": "~LogMeta", F: __dxlog_file9, L: 93, S: void 0 });
2076
1752
  return void 0;
2077
1753
  } else if (range.from === range.to) {
2078
1754
  return void 0;
@@ -2082,8 +1758,8 @@ var commentsDecorations = EditorView11.decorations.compute([
2082
1758
  }).filter(isNonNullable);
2083
1759
  return Decoration7.set(decorations2);
2084
1760
  });
2085
- var commentClickedEffect = StateEffect5.define();
2086
- var handleCommentClick = EditorView11.domEventHandlers({
1761
+ var commentClickedEffect = StateEffect3.define();
1762
+ var handleCommentClick = EditorView9.domEventHandlers({
2087
1763
  click: (event, view) => {
2088
1764
  let target = event.target;
2089
1765
  const editorRoot = view.dom;
@@ -2122,7 +1798,7 @@ var trackPastedComments = (onUpdate) => {
2122
1798
  }
2123
1799
  };
2124
1800
  return [
2125
- EditorView11.domEventHandlers({
1801
+ EditorView9.domEventHandlers({
2126
1802
  cut: handleTrack,
2127
1803
  copy: handleTrack
2128
1804
  }),
@@ -2144,7 +1820,7 @@ var trackPastedComments = (onUpdate) => {
2144
1820
  return effects;
2145
1821
  }),
2146
1822
  // Handle paste or the undo of comment deletion.
2147
- EditorView11.updateListener.of((update2) => {
1823
+ EditorView9.updateListener.of((update2) => {
2148
1824
  const restore = [];
2149
1825
  for (let i = 0; i < update2.transactions.length; i++) {
2150
1826
  const tr = update2.transactions[i];
@@ -2200,7 +1876,7 @@ var mapTrackedComment = (comment, changes) => ({
2200
1876
  from: changes.mapPos(comment.from, 1),
2201
1877
  to: changes.mapPos(comment.to, 1)
2202
1878
  });
2203
- var restoreCommentEffect = StateEffect5.define({
1879
+ var restoreCommentEffect = StateEffect3.define({
2204
1880
  map: mapTrackedComment
2205
1881
  });
2206
1882
  var createComment = (view) => {
@@ -2286,7 +1962,7 @@ var comments = (options = {}) => {
2286
1962
  //
2287
1963
  // Track deleted ranges and update ranges for decorations.
2288
1964
  //
2289
- EditorView11.updateListener.of(({ view, state, changes }) => {
1965
+ EditorView9.updateListener.of(({ view, state, changes }) => {
2290
1966
  let mod = false;
2291
1967
  const { comments: comments2, ...value } = state.field(commentsState);
2292
1968
  changes.iterChanges((from, to, from2, to2) => {
@@ -2318,7 +1994,7 @@ var comments = (options = {}) => {
2318
1994
  //
2319
1995
  // Track selection/proximity.
2320
1996
  //
2321
- EditorView11.updateListener.of(({ view, state }) => {
1997
+ EditorView9.updateListener.of(({ view, state }) => {
2322
1998
  let min = Infinity;
2323
1999
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
2324
2000
  const { head } = state.selection.main;
@@ -2354,34 +2030,41 @@ var comments = (options = {}) => {
2354
2030
  options.onUpdate && trackPastedComments(options.onUpdate)
2355
2031
  ].filter(isNonNullable);
2356
2032
  };
2357
- var scrollThreadIntoView = (view, id, center = true) => {
2033
+ var isRangeVisible = (view, range) => {
2034
+ const from = view.coordsAtPos(range.from);
2035
+ const to = view.coordsAtPos(range.to);
2036
+ if (!from || !to) {
2037
+ return false;
2038
+ }
2039
+ const { top, bottom } = view.scrollDOM.getBoundingClientRect();
2040
+ return from.top >= top && to.bottom <= bottom;
2041
+ };
2042
+ var scrollThreadIntoView = (view, id, { y = "center", yMargin } = {}) => {
2358
2043
  const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
2359
2044
  if (!comment?.comment.cursor) {
2360
2045
  return;
2361
2046
  }
2362
2047
  const range = Cursor.getRangeFromCursor(view.state, comment.comment.cursor);
2363
- if (range) {
2364
- const currentSelection = view.state.selection.main;
2365
- const currentScrollPosition = view.scrollDOM.scrollTop;
2366
- const targetScrollPosition = view.coordsAtPos(range.from)?.top;
2367
- const needsScroll = targetScrollPosition !== void 0 && (targetScrollPosition < currentScrollPosition || targetScrollPosition > currentScrollPosition + view.scrollDOM.clientHeight);
2368
- const needsSelectionUpdate = currentSelection.from !== range.from || currentSelection.to !== range.from;
2369
- if (needsScroll || needsSelectionUpdate) {
2370
- view.dispatch({
2371
- selection: needsSelectionUpdate ? {
2372
- anchor: range.from
2373
- } : void 0,
2374
- effects: [
2375
- needsScroll ? EditorView11.scrollIntoView(range.from, center ? {
2376
- y: "center"
2377
- } : void 0) : [],
2378
- needsSelectionUpdate ? setSelection.of({
2379
- current: id
2380
- }) : []
2381
- ].flat()
2382
- });
2383
- }
2048
+ if (!range) {
2049
+ return;
2384
2050
  }
2051
+ const { from, to } = view.state.selection.main;
2052
+ const needsSelectionUpdate = from !== range.from || to !== range.from;
2053
+ view.dispatch({
2054
+ selection: needsSelectionUpdate ? {
2055
+ anchor: range.from
2056
+ } : void 0,
2057
+ effects: [
2058
+ isRangeVisible(view, range) ? [] : EditorView9.scrollIntoView(range.from, {
2059
+ y,
2060
+ yMargin
2061
+ }),
2062
+ // Always mark this thread current so the highlight follows the selected thread.
2063
+ setSelection.of({
2064
+ current: id
2065
+ })
2066
+ ].flat()
2067
+ });
2385
2068
  };
2386
2069
  var ExternalCommentSync = class {
2387
2070
  unsubscribe;
@@ -2403,7 +2086,7 @@ var ExternalCommentSync = class {
2403
2086
  this.unsubscribe();
2404
2087
  };
2405
2088
  };
2406
- var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin10.fromClass(class {
2089
+ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin8.fromClass(class {
2407
2090
  constructor(view) {
2408
2091
  return new ExternalCommentSync(view, id, subscribe, getComments);
2409
2092
  }
@@ -2423,12 +2106,12 @@ var debugNodeLogger = (log12 = console.log) => {
2423
2106
  };
2424
2107
 
2425
2108
  // src/extensions/dnd.ts
2426
- import { EditorView as EditorView12, dropCursor } from "@codemirror/view";
2109
+ import { EditorView as EditorView10, dropCursor } from "@codemirror/view";
2427
2110
  var dropFile = (options = {}) => {
2428
2111
  return [
2429
2112
  styles3,
2430
2113
  dropCursor(),
2431
- EditorView12.domEventHandlers({
2114
+ EditorView10.domEventHandlers({
2432
2115
  drop: (event, view) => {
2433
2116
  event.preventDefault();
2434
2117
  const files = event.dataTransfer?.files;
@@ -2447,7 +2130,7 @@ var dropFile = (options = {}) => {
2447
2130
  })
2448
2131
  ];
2449
2132
  };
2450
- var styles3 = EditorView12.theme({
2133
+ var styles3 = EditorView10.theme({
2451
2134
  ".cm-dropCursor": {
2452
2135
  borderLeft: "2px solid var(--color-accent-text)",
2453
2136
  color: "var(--color-accent-text)",
@@ -2464,15 +2147,15 @@ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap }
2464
2147
  import { HighlightStyle, bracketMatching, syntaxHighlighting } from "@codemirror/language";
2465
2148
  import { searchKeymap } from "@codemirror/search";
2466
2149
  import { EditorState } from "@codemirror/state";
2467
- import { EditorView as EditorView16, ViewPlugin as ViewPlugin12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2 } from "@codemirror/view";
2150
+ import { EditorView as EditorView17, ViewPlugin as ViewPlugin13, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap7, lineNumbers, placeholder as placeholder2 } from "@codemirror/view";
2468
2151
  import { vscodeDarkStyle, vscodeLightStyle } from "@uiw/codemirror-theme-vscode";
2469
2152
  import defaultsDeep2 from "lodash.defaultsdeep";
2470
2153
  import { generateName } from "@dxos/display-name";
2471
2154
  import { log as log8 } from "@dxos/log";
2472
- import { hexToHue, isTruthy as isTruthy2 } from "@dxos/util";
2155
+ import { hexToHue, isTruthy as isTruthy3 } from "@dxos/util";
2473
2156
 
2474
2157
  // src/styles/theme.ts
2475
- import { EditorView as EditorView13 } from "@codemirror/view";
2158
+ import { EditorView as EditorView11 } from "@codemirror/view";
2476
2159
  import { mx as mx3 } from "@dxos/ui-theme";
2477
2160
  var headings = {
2478
2161
  1: {
@@ -2520,7 +2203,7 @@ var markdownTheme = {
2520
2203
  fontWeight: "100 !important"
2521
2204
  })
2522
2205
  };
2523
- var baseTheme = EditorView13.baseTheme({
2206
+ var baseTheme = EditorView11.baseTheme({
2524
2207
  /**
2525
2208
  * Outer frame.
2526
2209
  */
@@ -2532,7 +2215,7 @@ var baseTheme = EditorView13.baseTheme({
2532
2215
  * Scroller
2533
2216
  */
2534
2217
  ".cm-scroller": {
2535
- // Browser scroll-anchoring: see comment in `scroller.ts`. `auto` lets the browser pin a
2218
+ // Browser scroll-anchoring: see comment in `scrolling/crawler.ts`. `auto` lets the browser pin a
2536
2219
  // stable element near the viewport top so widget resizes (e.g. tool-block TogglePanel
2537
2220
  // open/close) don't jump the user's view.
2538
2221
  overflowAnchor: "auto"
@@ -2643,7 +2326,8 @@ var baseTheme = EditorView13.baseTheme({
2643
2326
  textDecorationThickness: "1px",
2644
2327
  textDecorationColor: "var(--color-separator)",
2645
2328
  textUnderlineOffset: "2px",
2646
- borderRadius: ".125rem"
2329
+ borderRadius: ".125rem",
2330
+ cursor: "pointer"
2647
2331
  },
2648
2332
  ".cm-link > span": {
2649
2333
  color: "var(--color-accent-text)"
@@ -2681,12 +2365,12 @@ var baseTheme = EditorView13.baseTheme({
2681
2365
  padding: "4px"
2682
2366
  },
2683
2367
  ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {
2684
- background: "var(--color-active-surface)",
2685
- color: "var(--color-base-surface-text)"
2368
+ background: "var(--color-current-surface)",
2369
+ color: "var(--color-base-fg)"
2686
2370
  },
2687
2371
  ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2688
2372
  paddingLeft: "4px !important",
2689
- color: "var(--color-base-surface-text)"
2373
+ color: "var(--color-base-fg)"
2690
2374
  },
2691
2375
  /**
2692
2376
  * Completion info.
@@ -2705,7 +2389,7 @@ var baseTheme = EditorView13.baseTheme({
2705
2389
  padding: "0 4px"
2706
2390
  },
2707
2391
  ".cm-completionMatchedText": {
2708
- color: "var(--color-base-surface-text)",
2392
+ color: "var(--color-base-fg)",
2709
2393
  textDecoration: "none !important"
2710
2394
  },
2711
2395
  /**
@@ -2740,7 +2424,7 @@ var baseTheme = EditorView13.baseTheme({
2740
2424
  backgroundColor: "var(--color-input-surface)"
2741
2425
  },
2742
2426
  ".cm-panel input:focus, .cm-panel button:focus": {
2743
- outline: "1px solid var(--color-neutral-focus-indicator)"
2427
+ outline: "1px solid var(--color-focus-ring-subtle)"
2744
2428
  },
2745
2429
  ".cm-panel label": {
2746
2430
  display: "inline-flex",
@@ -2753,15 +2437,15 @@ var baseTheme = EditorView13.baseTheme({
2753
2437
  height: "8px",
2754
2438
  marginRight: "6px !important",
2755
2439
  padding: "2px !important",
2756
- color: "var(--color-neutral-focus-indicator)"
2440
+ color: "var(--color-focus-ring-subtle)"
2757
2441
  },
2758
2442
  ".cm-panel button": {
2759
2443
  "&:hover": {
2760
- // TODO(burdon): Replace with layer and @apply bg-accent-surface-hover
2761
- backgroundColor: "var(--color-accent-surface-hover) !important"
2444
+ // TODO(burdon): Replace with layer and @apply bg-accent-bg-hover
2445
+ backgroundColor: "var(--color-accent-bg-hover) !important"
2762
2446
  },
2763
2447
  "&:active": {
2764
- backgroundColor: "var(--color-accent-surface-hover)"
2448
+ backgroundColor: "var(--color-accent-bg-hover)"
2765
2449
  }
2766
2450
  },
2767
2451
  ".cm-panel.cm-search": {
@@ -2769,14 +2453,14 @@ var baseTheme = EditorView13.baseTheme({
2769
2453
  borderTop: "1px solid var(--color-separator)"
2770
2454
  }
2771
2455
  });
2772
- var editorGutter = EditorView13.theme({
2456
+ var editorGutter = EditorView11.theme({
2773
2457
  ".cm-gutters": {
2774
2458
  // NOTE: Non-transparent background required to cover content if scrolling horizontally.
2775
2459
  background: "var(--color-base-surface) !important",
2776
2460
  paddingRight: "1rem"
2777
2461
  }
2778
2462
  });
2779
- var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2463
+ var createFontTheme = ({ monospace } = {}) => EditorView11.theme({
2780
2464
  // Main content.
2781
2465
  ".cm-scroller": {
2782
2466
  fontFamily: monospace ? fontMono : fontBody
@@ -2789,9 +2473,9 @@ var createFontTheme = ({ monospace } = {}) => EditorView13.theme({
2789
2473
  });
2790
2474
 
2791
2475
  // src/extensions/focus.ts
2792
- import { StateEffect as StateEffect6, StateField as StateField5 } from "@codemirror/state";
2793
- import { EditorView as EditorView14 } from "@codemirror/view";
2794
- var focusEffect = StateEffect6.define();
2476
+ import { StateEffect as StateEffect4, StateField as StateField5 } from "@codemirror/state";
2477
+ import { EditorView as EditorView12 } from "@codemirror/view";
2478
+ var focusEffect = StateEffect4.define();
2795
2479
  var focusField = StateField5.define({
2796
2480
  create: () => false,
2797
2481
  update: (value, tr) => {
@@ -2800,38 +2484,422 @@ var focusField = StateField5.define({
2800
2484
  return effect.value;
2801
2485
  }
2802
2486
  }
2803
- return value;
2487
+ return value;
2488
+ }
2489
+ });
2490
+ var focus = [
2491
+ focusField,
2492
+ EditorView12.domEventHandlers({
2493
+ focus: (_event, view) => {
2494
+ requestAnimationFrame(() => view.dispatch({
2495
+ effects: focusEffect.of(true)
2496
+ }));
2497
+ },
2498
+ blur: (_event, view) => {
2499
+ requestAnimationFrame(() => view.dispatch({
2500
+ effects: focusEffect.of(false)
2501
+ }));
2502
+ }
2503
+ })
2504
+ ];
2505
+
2506
+ // src/extensions/scrolling/auto-scroll.ts
2507
+ import { StateEffect as StateEffect6 } from "@codemirror/state";
2508
+ import { EditorView as EditorView14, ViewPlugin as ViewPlugin10 } from "@codemirror/view";
2509
+ import { addEventListener, combine, throttle as throttle2 } from "@dxos/async";
2510
+ import { Domino } from "@dxos/ui";
2511
+ import { getSize } from "@dxos/ui-theme";
2512
+
2513
+ // src/extensions/scrolling/crawler.ts
2514
+ import { StateEffect as StateEffect5 } from "@codemirror/state";
2515
+ import { EditorView as EditorView13, ViewPlugin as ViewPlugin9 } from "@codemirror/view";
2516
+ import { log as log7 } from "@dxos/log";
2517
+ var __dxlog_file10 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/scrolling/crawler.ts";
2518
+ var crawlerLineEffect = StateEffect5.define();
2519
+ var crawlerActiveEffect = StateEffect5.define();
2520
+ var scrollToLine = (view, options) => {
2521
+ view.dispatch({
2522
+ effects: crawlerLineEffect.of(options)
2523
+ });
2524
+ };
2525
+ var crawler = ({ overScroll = 0 } = {}) => {
2526
+ const crawlerPlugin = ViewPlugin9.fromClass(class CrawlerPlugin {
2527
+ view;
2528
+ crawler;
2529
+ constructor(view) {
2530
+ this.view = view;
2531
+ this.crawler = createCrawler(this.view);
2532
+ }
2533
+ // No-op.
2534
+ destroy() {
2535
+ this.crawler.cancel();
2536
+ }
2537
+ cancel() {
2538
+ this.crawler.cancel();
2539
+ }
2540
+ crawl({ active, instant } = {
2541
+ active: false
2542
+ }) {
2543
+ if (active) {
2544
+ this.crawler.scroll(instant);
2545
+ } else {
2546
+ this.crawler.cancel();
2547
+ }
2548
+ }
2549
+ scroll({ line, offset = 0, position, behavior = "instant" }) {
2550
+ const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
2551
+ const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
2552
+ const doc = this.view.state.doc;
2553
+ let targetScrollTop = scrollHeight - clientHeight + offset;
2554
+ if (line >= 0 && line <= doc.lines - 1) {
2555
+ const lineStart = doc.line(line + 1).from;
2556
+ const coords = this.view.coordsAtPos(lineStart);
2557
+ if (coords) {
2558
+ const currentScrollTop = scrollTop;
2559
+ const maxScrollTop = scrollHeight - clientHeight;
2560
+ if (position === "end") {
2561
+ targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
2562
+ } else {
2563
+ targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
2564
+ }
2565
+ targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
2566
+ }
2567
+ }
2568
+ requestAnimationFrame(() => {
2569
+ this.view.scrollDOM.scrollTo({
2570
+ top: targetScrollTop
2571
+ });
2572
+ });
2573
+ }
2574
+ });
2575
+ return [
2576
+ crawlerPlugin,
2577
+ // Listen for effect.
2578
+ EditorView13.updateListener.of((update2) => {
2579
+ update2.transactions.forEach((transaction) => {
2580
+ try {
2581
+ const plugin = update2.view.plugin(crawlerPlugin);
2582
+ if (plugin) {
2583
+ for (const effect of transaction.effects) {
2584
+ if (effect.is(crawlerActiveEffect)) {
2585
+ plugin.crawl(effect.value);
2586
+ } else if (effect.is(crawlerLineEffect)) {
2587
+ plugin.scroll(effect.value);
2588
+ }
2589
+ }
2590
+ }
2591
+ } catch (err) {
2592
+ log7.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file10, L: 105, S: void 0 });
2593
+ }
2594
+ });
2595
+ }),
2596
+ // Styles.
2597
+ EditorView13.theme({
2598
+ ".cm-scroller": {
2599
+ overflowY: "scroll",
2600
+ // Browser scroll-anchoring: when widgets above the viewport resize (e.g. tool blocks
2601
+ // expanding their TogglePanel), the browser picks a stable element near the viewport
2602
+ // top and adjusts `scrollTop` so the user's view doesn't jump. Auto-scroll's pinning
2603
+ // logic still has the final word when pinned (forces scrollTop to scrollHeight).
2604
+ overflowAnchor: "auto"
2605
+ },
2606
+ ".cm-scroller.cm-hide-scrollbar::-webkit-scrollbar": {
2607
+ display: "none"
2608
+ },
2609
+ ".cm-scroller::-webkit-scrollbar-thumb": {
2610
+ background: "transparent",
2611
+ transition: "background 0.15s"
2612
+ },
2613
+ "&:hover .cm-scroller::-webkit-scrollbar-thumb": {
2614
+ background: "var(--color-scrollbar-thumb)"
2615
+ },
2616
+ // Spacer below the last text line. Implemented as a real block pseudo-element
2617
+ // (rather than `padding-bottom` on `.cm-content`) so it materializes in the
2618
+ // scroller's `scrollHeight` regardless of how `padding` is reset by the base
2619
+ // theme or downstream classes — this is what gives auto-scroll its head-room
2620
+ // so the last line stays `overScroll` px above the viewport bottom.
2621
+ ".cm-content::after": {
2622
+ content: '""',
2623
+ display: "block",
2624
+ height: `${overScroll}px`
2625
+ },
2626
+ ".cm-scroll-button": {
2627
+ position: "absolute",
2628
+ bottom: "0.5rem",
2629
+ right: "1rem"
2630
+ }
2631
+ })
2632
+ ];
2633
+ };
2634
+ function createCrawler(view, omega = 5, snapThreshold = 5, snapVelocity = 50) {
2635
+ const el = view.scrollDOM;
2636
+ let currentTop = 0;
2637
+ let velocity = 0;
2638
+ let rafId = null;
2639
+ let lastTime = 0;
2640
+ let instant = false;
2641
+ function frame(now) {
2642
+ const dt = lastTime === 0 ? 1 / 60 : Math.min(0.1, (now - lastTime) / 1e3);
2643
+ lastTime = now;
2644
+ const targetTop = el.scrollHeight - el.clientHeight;
2645
+ const delta = targetTop - currentTop;
2646
+ if (instant) {
2647
+ el.scrollTop = targetTop;
2648
+ currentTop = targetTop;
2649
+ velocity = 0;
2650
+ if (Math.abs(delta) < snapThreshold) {
2651
+ rafId = null;
2652
+ lastTime = 0;
2653
+ return;
2654
+ }
2655
+ rafId = requestAnimationFrame(frame);
2656
+ return;
2657
+ }
2658
+ if (Math.abs(delta) < snapThreshold && Math.abs(velocity) < snapVelocity) {
2659
+ el.scrollTop = targetTop;
2660
+ currentTop = targetTop;
2661
+ velocity = 0;
2662
+ rafId = null;
2663
+ lastTime = 0;
2664
+ return;
2665
+ }
2666
+ const accel = omega * omega * delta - 2 * omega * velocity;
2667
+ velocity += accel * dt;
2668
+ currentTop += velocity * dt;
2669
+ el.scrollTop = currentTop;
2670
+ rafId = requestAnimationFrame(frame);
2804
2671
  }
2805
- });
2806
- var focus = [
2807
- focusField,
2808
- EditorView14.domEventHandlers({
2809
- focus: (_event, view) => {
2810
- requestAnimationFrame(() => view.dispatch({
2811
- effects: focusEffect.of(true)
2812
- }));
2672
+ return {
2673
+ scroll: (useInstant = false) => {
2674
+ instant = useInstant;
2675
+ if (rafId === null) {
2676
+ currentTop = el.scrollTop;
2677
+ lastTime = 0;
2678
+ rafId = requestAnimationFrame(frame);
2679
+ }
2813
2680
  },
2814
- blur: (_event, view) => {
2815
- requestAnimationFrame(() => view.dispatch({
2816
- effects: focusEffect.of(false)
2817
- }));
2681
+ cancel: () => {
2682
+ if (rafId !== null) {
2683
+ cancelAnimationFrame(rafId);
2684
+ velocity = 0;
2685
+ lastTime = 0;
2686
+ rafId = null;
2687
+ }
2818
2688
  }
2819
- })
2820
- ];
2689
+ };
2690
+ }
2691
+
2692
+ // src/extensions/scrolling/auto-scroll.ts
2693
+ var autoScrollEffect = StateEffect6.define();
2694
+ var autoScroll = ({ scrollOnResize = true } = {}) => {
2695
+ let buttonContainer;
2696
+ let isPinned = true;
2697
+ let jumpPending = false;
2698
+ let enabled = true;
2699
+ let firstUpdate = true;
2700
+ let streamed = false;
2701
+ const setPinned = (pinned) => {
2702
+ buttonContainer?.classList.toggle("opacity-0", pinned);
2703
+ isPinned = pinned;
2704
+ };
2705
+ return [
2706
+ // Update listener for scrolling when content changes.
2707
+ EditorView14.updateListener.of((update2) => {
2708
+ const { view, heightChanged, state, startState } = update2;
2709
+ for (const tr of update2.transactions) {
2710
+ for (const effect of tr.effects) {
2711
+ if (effect.is(autoScrollEffect)) {
2712
+ enabled = effect.value;
2713
+ if (enabled) {
2714
+ setPinned(true);
2715
+ view.dispatch({
2716
+ effects: crawlerActiveEffect.of({
2717
+ active: true
2718
+ })
2719
+ });
2720
+ } else {
2721
+ view.dispatch({
2722
+ effects: crawlerActiveEffect.of({
2723
+ active: false
2724
+ })
2725
+ });
2726
+ }
2727
+ }
2728
+ }
2729
+ }
2730
+ if (!enabled) {
2731
+ return;
2732
+ }
2733
+ if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
2734
+ firstUpdate = false;
2735
+ jumpPending = true;
2736
+ requestAnimationFrame(() => {
2737
+ view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
2738
+ jumpPending = false;
2739
+ });
2740
+ return;
2741
+ }
2742
+ firstUpdate = false;
2743
+ if (jumpPending) {
2744
+ return;
2745
+ }
2746
+ if (heightChanged) {
2747
+ if (isPinned) {
2748
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
2749
+ const delta = scrollHeight - scrollTop - clientHeight;
2750
+ if (update2.docChanged) {
2751
+ streamed = true;
2752
+ }
2753
+ if (delta > 0) {
2754
+ setPinned(true);
2755
+ view.dispatch({
2756
+ effects: crawlerActiveEffect.of({
2757
+ active: true,
2758
+ instant: !streamed
2759
+ })
2760
+ });
2761
+ } else if (delta < -1) {
2762
+ setPinned(false);
2763
+ }
2764
+ } else {
2765
+ if (state.doc.length === 0) {
2766
+ setPinned(true);
2767
+ }
2768
+ }
2769
+ }
2770
+ }),
2771
+ // Re-pin and jump to bottom when the scroll container itself resizes (e.g. sidebar toggle,
2772
+ // window resize). Doc-driven height changes are handled by the updateListener above; this
2773
+ // observer covers the case where the viewport changes while the doc length is unchanged.
2774
+ scrollOnResize ? ViewPlugin10.fromClass(class {
2775
+ observer;
2776
+ firstObservation = true;
2777
+ destroyed = false;
2778
+ constructor(view) {
2779
+ const onResize = throttle2(() => {
2780
+ if (this.destroyed || !enabled) {
2781
+ return;
2782
+ }
2783
+ setPinned(true);
2784
+ requestAnimationFrame(() => {
2785
+ if (this.destroyed) {
2786
+ return;
2787
+ }
2788
+ view.scrollDOM.scrollTo({
2789
+ top: view.scrollDOM.scrollHeight,
2790
+ behavior: "instant"
2791
+ });
2792
+ view.dispatch({
2793
+ effects: crawlerActiveEffect.of({
2794
+ active: false
2795
+ })
2796
+ });
2797
+ });
2798
+ }, 50);
2799
+ this.observer = new ResizeObserver(() => {
2800
+ if (this.firstObservation) {
2801
+ this.firstObservation = false;
2802
+ return;
2803
+ }
2804
+ onResize();
2805
+ });
2806
+ this.observer.observe(view.scrollDOM);
2807
+ }
2808
+ destroy() {
2809
+ this.destroyed = true;
2810
+ this.observer.disconnect();
2811
+ }
2812
+ }) : [],
2813
+ // Detect user scroll and unpin (or re-pin if scrolled to the bottom).
2814
+ ViewPlugin10.fromClass(class {
2815
+ cleanup;
2816
+ constructor(view) {
2817
+ const onUserScroll = throttle2(() => {
2818
+ requestAnimationFrame(() => {
2819
+ const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
2820
+ const delta = scrollHeight - scrollTop - clientHeight;
2821
+ const pinned = Math.abs(delta) <= 1;
2822
+ setPinned(pinned);
2823
+ if (!pinned) {
2824
+ view.dispatch({
2825
+ effects: crawlerActiveEffect.of({
2826
+ active: false
2827
+ })
2828
+ });
2829
+ }
2830
+ });
2831
+ }, 500);
2832
+ this.cleanup = createUserScrollDetector(view.scrollDOM, () => {
2833
+ if (isPinned) {
2834
+ setPinned(false);
2835
+ view.dispatch({
2836
+ effects: crawlerActiveEffect.of({
2837
+ active: false
2838
+ })
2839
+ });
2840
+ }
2841
+ onUserScroll();
2842
+ });
2843
+ }
2844
+ destroy() {
2845
+ this.cleanup();
2846
+ }
2847
+ }),
2848
+ // Scroll button.
2849
+ ViewPlugin10.fromClass(class {
2850
+ constructor(view) {
2851
+ const icon = Domino.of("dx-icon").classNames(getSize(4)).attributes({
2852
+ icon: "ph--arrow-down--regular"
2853
+ });
2854
+ const button = Domino.of("button").classNames("dx-button bg-accent-bg").attributes({
2855
+ "data-density": "md"
2856
+ }).append(icon).on("click", () => {
2857
+ setPinned(true);
2858
+ view.dispatch({
2859
+ effects: [
2860
+ crawlerLineEffect.of({
2861
+ line: -1,
2862
+ position: "end",
2863
+ behavior: "smooth"
2864
+ }),
2865
+ // Re-engage the follower so it keeps tracking the bottom as content continues
2866
+ // to stream after the catch-up jump.
2867
+ crawlerActiveEffect.of({
2868
+ active: true,
2869
+ instant: !streamed
2870
+ })
2871
+ ]
2872
+ });
2873
+ });
2874
+ buttonContainer = Domino.of("div").classNames("cm-scroll-button transition-opacity duration-300 opacity-0 z-1").append(button).root;
2875
+ view.scrollDOM.parentElement.appendChild(buttonContainer);
2876
+ }
2877
+ })
2878
+ ];
2879
+ };
2880
+ function createUserScrollDetector(element, onUserScroll) {
2881
+ return combine(addEventListener(element, "wheel", () => onUserScroll(), {
2882
+ passive: true
2883
+ }), addEventListener(element, "pointerdown", (event) => {
2884
+ if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
2885
+ onUserScroll();
2886
+ }
2887
+ }));
2888
+ }
2821
2889
 
2822
- // src/extensions/scroll-past-end.ts
2890
+ // src/extensions/scrolling/scroll-past-end.ts
2823
2891
  import { EditorView as EditorView15, ViewPlugin as ViewPlugin11 } from "@codemirror/view";
2824
2892
  var scrollPastEndPlugin = ViewPlugin11.fromClass(class {
2825
- height = 1e3;
2826
- attrs = {
2827
- style: "padding-bottom: 1000px"
2893
+ _height = 1e3;
2894
+ _attrs = {
2895
+ style: `padding-bottom: ${this._height}px`
2828
2896
  };
2829
2897
  update({ view }) {
2830
2898
  const lastLineBlock = view.lineBlockAt(view.state.doc.length);
2831
2899
  const height = view.dom.clientHeight - lastLineBlock.height - view.documentPadding.top - 0.5;
2832
- if (height >= 0 && height !== this.height) {
2833
- this.height = height;
2834
- this.attrs = {
2900
+ if (height >= 0 && height !== this._height) {
2901
+ this._height = height;
2902
+ this._attrs = {
2835
2903
  style: `padding-bottom: ${height}px`
2836
2904
  };
2837
2905
  }
@@ -2839,12 +2907,68 @@ var scrollPastEndPlugin = ViewPlugin11.fromClass(class {
2839
2907
  });
2840
2908
  var scrollPastEnd = () => [
2841
2909
  scrollPastEndPlugin,
2842
- EditorView15.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?.attrs ?? null)
2910
+ EditorView15.contentAttributes.of((view) => view.plugin(scrollPastEndPlugin)?._attrs ?? null)
2911
+ ];
2912
+
2913
+ // src/extensions/scrolling/scrollbar-autohide.ts
2914
+ import { EditorView as EditorView16, ViewPlugin as ViewPlugin12 } from "@codemirror/view";
2915
+ var scrollbarAutohide = ({ timeout = 800 } = {}) => [
2916
+ ViewPlugin12.fromClass(
2917
+ // NOTE: Uses TS `private`/plain fields rather than ES `#private`. CodeMirror's plugin lifecycle
2918
+ // (and the source/prebundled double-load in dev) can invoke `destroy` with a `this` that the
2919
+ // WeakMap-based `#private` transpilation rejects ("private field on non-instance"), crashing
2920
+ // editor teardown. Plain fields avoid the membership check.
2921
+ class {
2922
+ _scroller;
2923
+ _timer;
2924
+ constructor(view) {
2925
+ this._scroller = view.scrollDOM;
2926
+ this._scroller.addEventListener("scroll", this._handleScroll, {
2927
+ passive: true
2928
+ });
2929
+ }
2930
+ destroy() {
2931
+ this._scroller.removeEventListener("scroll", this._handleScroll);
2932
+ clearTimeout(this._timer);
2933
+ this._scroller.classList.remove("cm-scrolling");
2934
+ }
2935
+ // Show the thumb while scrolling; remove the class once scrolling has been idle for `timeout`.
2936
+ _handleScroll = () => {
2937
+ this._scroller.classList.add("cm-scrolling");
2938
+ clearTimeout(this._timer);
2939
+ this._timer = setTimeout(() => this._scroller.classList.remove("cm-scrolling"), timeout);
2940
+ };
2941
+ }
2942
+ ),
2943
+ EditorView16.theme({
2944
+ // Reveal the thumb only while actively scrolling.
2945
+ ".cm-scroller.cm-scrolling::-webkit-scrollbar-thumb": {
2946
+ background: "var(--color-scrollbar-thumb)"
2947
+ },
2948
+ // Suppress the base theme's hover-reveal (higher specificity via `:not(.cm-scrolling)`), so a
2949
+ // hovered/focused-but-idle editor keeps the thumb hidden.
2950
+ "&:hover .cm-scroller:not(.cm-scrolling)::-webkit-scrollbar-thumb": {
2951
+ background: "transparent"
2952
+ }
2953
+ })
2843
2954
  ];
2844
2955
 
2956
+ // src/extensions/scrolling/scroller.ts
2957
+ import { isTruthy as isTruthy2 } from "@dxos/util";
2958
+ var scroller = ({ overScroll, scrollOnResize, autoScroll: autoScroll2 = true } = {}) => {
2959
+ return [
2960
+ crawler({
2961
+ overScroll
2962
+ }),
2963
+ autoScroll2 && autoScroll({
2964
+ scrollOnResize
2965
+ })
2966
+ ].filter(isTruthy2);
2967
+ };
2968
+
2845
2969
  // src/extensions/factories.ts
2846
2970
  var __dxlog_file11 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/factories.ts";
2847
- var tabbable = EditorView16.contentAttributes.of({
2971
+ var tabbable = EditorView17.contentAttributes.of({
2848
2972
  tabindex: "0"
2849
2973
  });
2850
2974
  var filterChars = (chars) => {
@@ -2897,7 +3021,7 @@ var createBasicExtensions = (propsProp) => {
2897
3021
  const props = defaultsDeep2({}, propsProp, defaultBasicOptions);
2898
3022
  return [
2899
3023
  // NOTE: Doesn't catch errors in keymap functions.
2900
- EditorView16.exceptionSink.of((err) => {
3024
+ EditorView17.exceptionSink.of((err) => {
2901
3025
  log8.catch(err, void 0, { "~LogMeta": "~LogMeta", F: __dxlog_file11, L: 79, S: void 0 });
2902
3026
  }),
2903
3027
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
@@ -2907,7 +3031,7 @@ var createBasicExtensions = (propsProp) => {
2907
3031
  props.drawSelection && drawSelection({
2908
3032
  cursorBlinkRate: 1200
2909
3033
  }),
2910
- props.editable !== void 0 && EditorView16.editable.of(props.editable),
3034
+ props.editable !== void 0 && EditorView17.editable.of(props.editable),
2911
3035
  props.focus && focus,
2912
3036
  props.highlightActiveLine && highlightActiveLine(),
2913
3037
  props.history && history(),
@@ -2915,7 +3039,7 @@ var createBasicExtensions = (propsProp) => {
2915
3039
  lineNumbers(),
2916
3040
  editorGutter
2917
3041
  ],
2918
- props.lineWrapping && EditorView16.lineWrapping,
3042
+ props.lineWrapping && EditorView17.lineWrapping,
2919
3043
  props.placeholder && placeholder2(props.placeholder),
2920
3044
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2921
3045
  // `EditorState.readOnly` is advisory — CodeMirror doesn't auto-reject doc-changing
@@ -2949,8 +3073,8 @@ var createBasicExtensions = (propsProp) => {
2949
3073
  preventDefault: true,
2950
3074
  run: () => true
2951
3075
  }
2952
- ].filter(isTruthy2))
2953
- ].filter(isTruthy2);
3076
+ ].filter(isTruthy3))
3077
+ ].filter(isTruthy3);
2954
3078
  };
2955
3079
  var grow = {
2956
3080
  editor: {
@@ -2971,18 +3095,18 @@ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, synta
2971
3095
  const slots = defaultsDeep2({}, slotsProp, defaultThemeSlots);
2972
3096
  return [
2973
3097
  baseTheme,
2974
- EditorView16.darkTheme.of(themeMode === "dark"),
3098
+ EditorView17.darkTheme.of(themeMode === "dark"),
2975
3099
  createFontTheme({
2976
3100
  monospace
2977
3101
  }),
2978
3102
  syntaxHighlightingProp && syntaxHighlighting(HighlightStyle.define(themeMode === "dark" ? defaultStyles.dark : defaultStyles.light)),
2979
- slots.editor?.className && EditorView16.editorAttributes.of({
3103
+ slots.editor?.className && EditorView17.editorAttributes.of({
2980
3104
  class: slots.editor.className
2981
3105
  }),
2982
- slots.content?.className && EditorView16.contentAttributes.of({
3106
+ slots.content?.className && EditorView17.contentAttributes.of({
2983
3107
  class: slots.content.className
2984
3108
  }),
2985
- (slots.scroller?.className || scrollbarThin) && ViewPlugin12.fromClass(class {
3109
+ (slots.scroller?.className || scrollbarThin) && ViewPlugin13.fromClass(class {
2986
3110
  constructor(view) {
2987
3111
  if (slots.scroller?.className) {
2988
3112
  view.scrollDOM.classList.add(...slots.scroller.className.split(/\s+/));
@@ -2992,7 +3116,7 @@ var createThemeExtensions = ({ monospace, scrollbarThin, slots: slotsProp, synta
2992
3116
  }
2993
3117
  }
2994
3118
  })
2995
- ].filter(isTruthy2);
3119
+ ].filter(isTruthy3);
2996
3120
  };
2997
3121
  var createDataExtensions = ({ id, text, messenger, identity }) => {
2998
3122
  const extensions = [];
@@ -3018,7 +3142,7 @@ var createDataExtensions = ({ id, text, messenger, identity }) => {
3018
3142
 
3019
3143
  // src/extensions/folding.ts
3020
3144
  import { codeFolding, foldGutter } from "@codemirror/language";
3021
- import { EditorView as EditorView17 } from "@codemirror/view";
3145
+ import { EditorView as EditorView18 } from "@codemirror/view";
3022
3146
  import { Domino as Domino2, mx as mx4 } from "@dxos/ui";
3023
3147
  var folding = () => {
3024
3148
  return [
@@ -3033,7 +3157,7 @@ var folding = () => {
3033
3157
  }))).root;
3034
3158
  }
3035
3159
  }),
3036
- EditorView17.theme({
3160
+ EditorView18.theme({
3037
3161
  ".cm-foldGutter": {
3038
3162
  opacity: 0.3,
3039
3163
  transition: "opacity 0.3s",
@@ -3047,7 +3171,7 @@ var folding = () => {
3047
3171
  };
3048
3172
 
3049
3173
  // src/extensions/hashtag.ts
3050
- import { Decoration as Decoration8, EditorView as EditorView18, MatchDecorator, ViewPlugin as ViewPlugin13, WidgetType as WidgetType4 } from "@codemirror/view";
3174
+ import { Decoration as Decoration8, EditorView as EditorView19, MatchDecorator, ViewPlugin as ViewPlugin14, WidgetType as WidgetType4 } from "@codemirror/view";
3051
3175
  import { getHashStyles, mx as mx5 } from "@dxos/ui-theme";
3052
3176
  var TagWidget = class extends WidgetType4 {
3053
3177
  _text;
@@ -3068,7 +3192,7 @@ var tagMatcher = new MatchDecorator({
3068
3192
  })
3069
3193
  });
3070
3194
  var hashtag = () => [
3071
- ViewPlugin13.fromClass(class {
3195
+ ViewPlugin14.fromClass(class {
3072
3196
  tags;
3073
3197
  constructor(view) {
3074
3198
  this.tags = tagMatcher.createDeco(view);
@@ -3078,11 +3202,11 @@ var hashtag = () => [
3078
3202
  }
3079
3203
  }, {
3080
3204
  decorations: (instance) => instance.tags,
3081
- provide: (plugin) => EditorView18.atomicRanges.of((view) => {
3205
+ provide: (plugin) => EditorView19.atomicRanges.of((view) => {
3082
3206
  return view.plugin(plugin)?.tags || Decoration8.none;
3083
3207
  })
3084
3208
  }),
3085
- EditorView18.theme({
3209
+ EditorView19.theme({
3086
3210
  ".cm-tag": {
3087
3211
  borderRadius: "4px",
3088
3212
  marginRight: "6px",
@@ -3137,18 +3261,18 @@ var schemaLinter = (validate) => (view) => {
3137
3261
  };
3138
3262
 
3139
3263
  // src/extensions/listener.ts
3140
- import { EditorView as EditorView19 } from "@codemirror/view";
3264
+ import { EditorView as EditorView20 } from "@codemirror/view";
3141
3265
  import { isNonNullable as isNonNullable2 } from "@dxos/util";
3142
3266
  var listener = ({ onFocus, onChange }) => {
3143
3267
  return [
3144
- onFocus && EditorView19.focusChangeEffect.of((state, focusing) => {
3268
+ onFocus && EditorView20.focusChangeEffect.of((state, focusing) => {
3145
3269
  onFocus({
3146
3270
  id: state.facet(documentId),
3147
3271
  focusing
3148
3272
  });
3149
3273
  return null;
3150
3274
  }),
3151
- onChange && EditorView19.updateListener.of(({ state, docChanged }) => {
3275
+ onChange && EditorView20.updateListener.of(({ state, docChanged }) => {
3152
3276
  if (docChanged) {
3153
3277
  onChange({
3154
3278
  id: state.facet(documentId),
@@ -3163,7 +3287,7 @@ var listener = ({ onFocus, onChange }) => {
3163
3287
  import { snippet } from "@codemirror/autocomplete";
3164
3288
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3165
3289
  import { EditorSelection as EditorSelection2 } from "@codemirror/state";
3166
- import { EditorView as EditorView20, keymap as keymap8 } from "@codemirror/view";
3290
+ import { EditorView as EditorView21, keymap as keymap8 } from "@codemirror/view";
3167
3291
  import { debounceAndThrottle } from "@dxos/async";
3168
3292
  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;
3169
3293
  var Inline = /* @__PURE__ */ (function(Inline2) {
@@ -4252,7 +4376,7 @@ var getFormatting = (state) => {
4252
4376
  };
4253
4377
  };
4254
4378
  var formattingListener = (onStateChange, delay = 100) => {
4255
- return EditorView20.updateListener.of(debounceAndThrottle((update2) => {
4379
+ return EditorView21.updateListener.of(debounceAndThrottle((update2) => {
4256
4380
  if (update2.docChanged || update2.selectionSet) {
4257
4381
  onStateChange(getFormatting(update2.state));
4258
4382
  }
@@ -4316,7 +4440,7 @@ import { markdown, markdownLanguage as markdownLanguage2 } from "@codemirror/lan
4316
4440
  import { foldNodeProp, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
4317
4441
  import { languages } from "@codemirror/language-data";
4318
4442
  import { keymap as keymap9 } from "@codemirror/view";
4319
- import { isTruthy as isTruthy3 } from "@dxos/util";
4443
+ import { isTruthy as isTruthy4 } from "@dxos/util";
4320
4444
 
4321
4445
  // src/extensions/markdown/highlight.ts
4322
4446
  import { markdownLanguage } from "@codemirror/lang-markdown";
@@ -4548,7 +4672,7 @@ var createMarkdownExtensions = (options = {}) => {
4548
4672
  ...defaultKeymap2,
4549
4673
  // TODO(burdon): Remove?
4550
4674
  ...completionKeymap
4551
- ].filter(isTruthy3))
4675
+ ].filter(isTruthy4))
4552
4676
  ];
4553
4677
  };
4554
4678
  var noFencedCodeFolding = {
@@ -4598,16 +4722,16 @@ var convertTreeToJson = (state) => {
4598
4722
 
4599
4723
  // src/extensions/markdown/decorate.ts
4600
4724
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4601
- import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect7 } from "@codemirror/state";
4602
- import { Decoration as Decoration11, EditorView as EditorView24, ViewPlugin as ViewPlugin15, WidgetType as WidgetType7 } from "@codemirror/view";
4725
+ import { Prec as Prec4, RangeSetBuilder as RangeSetBuilder5, StateEffect as StateEffect8 } from "@codemirror/state";
4726
+ import { Decoration as Decoration11, EditorView as EditorView25, ViewPlugin as ViewPlugin17, WidgetType as WidgetType7 } from "@codemirror/view";
4603
4727
  import { invariant as invariant4 } from "@dxos/invariant";
4604
4728
 
4605
4729
  // src/extensions/markdown/changes.ts
4606
4730
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
4607
4731
  import { Transaction as Transaction4 } from "@codemirror/state";
4608
- import { ViewPlugin as ViewPlugin14 } from "@codemirror/view";
4732
+ import { ViewPlugin as ViewPlugin15 } from "@codemirror/view";
4609
4733
  var adjustChanges = () => {
4610
- return ViewPlugin14.fromClass(class {
4734
+ return ViewPlugin15.fromClass(class {
4611
4735
  update(update2) {
4612
4736
  const tree = syntaxTree4(update2.state);
4613
4737
  const adjustments = [];
@@ -4748,15 +4872,19 @@ var getValidUrl = (str) => {
4748
4872
 
4749
4873
  // src/extensions/markdown/image.ts
4750
4874
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4751
- import { StateField as StateField7 } from "@codemirror/state";
4752
- import { Decoration as Decoration9, EditorView as EditorView21, WidgetType as WidgetType5 } from "@codemirror/view";
4753
- var image = (_options = {}) => {
4875
+ import { StateEffect as StateEffect7, StateField as StateField7 } from "@codemirror/state";
4876
+ import { Decoration as Decoration9, EditorView as EditorView22, ViewPlugin as ViewPlugin16, WidgetType as WidgetType5 } from "@codemirror/view";
4877
+ var rebuildEffect = StateEffect7.define();
4878
+ var image = (options = {}) => {
4754
4879
  return [
4755
4880
  StateField7.define({
4756
4881
  create: (state) => {
4757
- return Decoration9.set(buildDecorations(state, 0, state.doc.length));
4882
+ return Decoration9.set(buildDecorations(state, 0, state.doc.length, options));
4758
4883
  },
4759
4884
  update: (value, tr) => {
4885
+ if (tr.effects.some((effect) => effect.is(rebuildEffect))) {
4886
+ return Decoration9.set(buildDecorations(tr.state, 0, tr.state.doc.length, options));
4887
+ }
4760
4888
  if (!tr.docChanged && !tr.selection) {
4761
4889
  return value;
4762
4890
  }
@@ -4774,14 +4902,26 @@ var image = (_options = {}) => {
4774
4902
  filterFrom: from,
4775
4903
  filterTo: to,
4776
4904
  filter: () => false,
4777
- add: buildDecorations(tr.state, from, to)
4905
+ add: buildDecorations(tr.state, from, to, options)
4778
4906
  });
4779
4907
  },
4780
- provide: (field) => EditorView21.decorations.from(field)
4781
- })
4908
+ provide: (field) => EditorView22.decorations.from(field)
4909
+ }),
4910
+ // Block-replace decorations have to live in a state field, but viewport changes are only
4911
+ // observable from a view plugin. Bridge the two by dispatching a rebuild effect whenever
4912
+ // the viewport extends so newly-parsed image nodes get widgetized without requiring focus.
4913
+ ViewPlugin16.define((view) => ({
4914
+ update: (update2) => {
4915
+ if (update2.viewportChanged) {
4916
+ queueMicrotask(() => view.dispatch({
4917
+ effects: rebuildEffect.of(void 0)
4918
+ }));
4919
+ }
4920
+ }
4921
+ }))
4782
4922
  ];
4783
4923
  };
4784
- var buildDecorations = (state, from, to) => {
4924
+ var buildDecorations = (state, from, to, options = {}) => {
4785
4925
  const decorations2 = [];
4786
4926
  const cursor = state.selection.main.head;
4787
4927
  syntaxTree5(state).iterate({
@@ -4794,6 +4934,12 @@ var buildDecorations = (state, from, to) => {
4794
4934
  if (url.match(/^https?:\/\//) === null && url.match(/^file?:\/\//) === null) {
4795
4935
  return;
4796
4936
  }
4937
+ if (options.skip?.({
4938
+ name: "Image",
4939
+ url
4940
+ })) {
4941
+ return;
4942
+ }
4797
4943
  preloadImage(url);
4798
4944
  decorations2.push(Decoration9.replace({
4799
4945
  block: true,
@@ -4827,20 +4973,34 @@ var ImageWidget = class extends WidgetType5 {
4827
4973
  const img = document.createElement("img");
4828
4974
  img.setAttribute("src", this._url);
4829
4975
  img.setAttribute("class", "cm-image");
4830
- if (view.state.field(focusField)) {
4831
- img.onload = () => img.classList.add("cm-loaded-image");
4976
+ const focused = view.state.field(focusField);
4977
+ if (focused) {
4978
+ img.onload = () => {
4979
+ img.classList.add("cm-loaded-image");
4980
+ collapseIfTrackingPixel(img);
4981
+ };
4832
4982
  } else {
4833
4983
  img.classList.add("cm-loaded-image");
4984
+ img.onload = () => collapseIfTrackingPixel(img);
4834
4985
  }
4986
+ img.onerror = () => collapseLine(img);
4835
4987
  return img;
4836
4988
  }
4837
4989
  };
4990
+ var collapseIfTrackingPixel = (img) => {
4991
+ if (img.naturalWidth <= 1 && img.naturalHeight <= 1) {
4992
+ collapseLine(img);
4993
+ }
4994
+ };
4995
+ var collapseLine = (img) => {
4996
+ img.style.display = "none";
4997
+ };
4838
4998
 
4839
4999
  // src/extensions/markdown/styles.ts
4840
- import { EditorView as EditorView22 } from "@codemirror/view";
5000
+ import { EditorView as EditorView23 } from "@codemirror/view";
4841
5001
  var bulletListIndentationWidth = 24;
4842
5002
  var orderedListIndentationWidth = 36;
4843
- var formattingStyles = EditorView22.theme({
5003
+ var formattingStyles = EditorView23.theme({
4844
5004
  /**
4845
5005
  * Horizontal rule.
4846
5006
  */
@@ -4995,12 +5155,12 @@ var formattingStyles = EditorView22.theme({
4995
5155
  // src/extensions/markdown/table.ts
4996
5156
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4997
5157
  import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField8 } from "@codemirror/state";
4998
- import { Decoration as Decoration10, EditorView as EditorView23, WidgetType as WidgetType6 } from "@codemirror/view";
5158
+ import { Decoration as Decoration10, EditorView as EditorView24, WidgetType as WidgetType6 } from "@codemirror/view";
4999
5159
  var table = (options = {}) => {
5000
5160
  return StateField8.define({
5001
5161
  create: (state) => update(state, options),
5002
5162
  update: (_, tr) => update(tr.state, options),
5003
- provide: (field) => EditorView23.decorations.from(field)
5163
+ provide: (field) => EditorView24.decorations.from(field)
5004
5164
  });
5005
5165
  };
5006
5166
  var update = (state, _options) => {
@@ -5591,10 +5751,10 @@ var buildDecorations2 = (view, options, focus2) => {
5591
5751
  atomicDeco: atomicDeco.finish()
5592
5752
  };
5593
5753
  };
5594
- var forceUpdate = StateEffect7.define();
5754
+ var forceUpdate = StateEffect8.define();
5595
5755
  var decorateMarkdown = (options = {}) => {
5596
5756
  return [
5597
- ViewPlugin15.fromClass(class {
5757
+ ViewPlugin17.fromClass(class {
5598
5758
  deco;
5599
5759
  atomicDeco;
5600
5760
  pendingUpdate;
@@ -5629,12 +5789,14 @@ var decorateMarkdown = (options = {}) => {
5629
5789
  }
5630
5790
  }, {
5631
5791
  provide: (plugin) => [
5632
- Prec4.low(EditorView24.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5633
- EditorView24.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5634
- EditorView24.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5792
+ Prec4.low(EditorView25.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration11.none)),
5793
+ EditorView25.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none),
5794
+ EditorView25.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration11.none)
5635
5795
  ]
5636
5796
  }),
5637
- image(),
5797
+ image({
5798
+ skip: options.skip ? (node) => !!options.skip?.(node) : void 0
5799
+ }),
5638
5800
  table(),
5639
5801
  adjustChanges(),
5640
5802
  formattingStyles
@@ -5644,7 +5806,10 @@ var decorateMarkdown = (options = {}) => {
5644
5806
  // src/extensions/markdown/link.ts
5645
5807
  import { syntaxTree as syntaxTree8 } from "@codemirror/language";
5646
5808
  import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
5647
- import { tooltipContent } from "@dxos/ui-theme";
5809
+ import { mx as mx6, surfaceShadow } from "@dxos/ui-theme";
5810
+ var tooltipClassName = mx6("inline-flex items-center p-1 max-w-64 text-sm bg-inverse-surface text-inverse-fg rounded-sm", surfaceShadow({
5811
+ elevation: "positioned"
5812
+ }));
5648
5813
  var linkTooltip = (renderTooltip) => {
5649
5814
  return hoverTooltip2((view, pos, side) => {
5650
5815
  const syntax = syntaxTree8(view.state).resolveInner(pos, side);
@@ -5666,7 +5831,7 @@ var linkTooltip = (renderTooltip) => {
5666
5831
  above: true,
5667
5832
  create: () => {
5668
5833
  const el = document.createElement("div");
5669
- el.className = tooltipContent({});
5834
+ el.className = tooltipClassName;
5670
5835
  renderTooltip(el, {
5671
5836
  url: urlText
5672
5837
  }, view);
@@ -5716,8 +5881,8 @@ var mention = ({ debug, onSearch }) => {
5716
5881
  };
5717
5882
 
5718
5883
  // src/extensions/modal.ts
5719
- import { StateEffect as StateEffect8, StateField as StateField9 } from "@codemirror/state";
5720
- var modalStateEffect = StateEffect8.define();
5884
+ import { StateEffect as StateEffect9, StateField as StateField9 } from "@codemirror/state";
5885
+ var modalStateEffect = StateEffect9.define();
5721
5886
  var modalStateField = StateField9.define({
5722
5887
  create: () => false,
5723
5888
  update: (value, tr) => {
@@ -6285,17 +6450,17 @@ var commands = () => keymap11.of([
6285
6450
 
6286
6451
  // src/extensions/outliner/outliner.ts
6287
6452
  import { Prec as Prec5 } from "@codemirror/state";
6288
- import { Decoration as Decoration12, EditorView as EditorView26, ViewPlugin as ViewPlugin18 } from "@codemirror/view";
6289
- import { mx as mx6 } from "@dxos/ui-theme";
6453
+ import { Decoration as Decoration12, EditorView as EditorView27, ViewPlugin as ViewPlugin20 } from "@codemirror/view";
6454
+ import { mx as mx7 } from "@dxos/ui-theme";
6290
6455
 
6291
6456
  // src/extensions/outliner/editor.ts
6292
6457
  import { EditorSelection as EditorSelection4, EditorState as EditorState2 } from "@codemirror/state";
6293
- import { ViewPlugin as ViewPlugin16 } from "@codemirror/view";
6458
+ import { ViewPlugin as ViewPlugin18 } from "@codemirror/view";
6294
6459
  import { log as log10 } from "@dxos/log";
6295
6460
  var __dxlog_file15 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/outliner/editor.ts";
6296
6461
  var LIST_ITEM_REGEX = /^\s*- (\[ \]|\[x\])? /;
6297
6462
  var initialize = () => {
6298
- return ViewPlugin16.fromClass(class {
6463
+ return ViewPlugin18.fromClass(class {
6299
6464
  constructor(view) {
6300
6465
  const first = view.state.doc.lineAt(0);
6301
6466
  const text = view.state.sliceDoc(first.from, first.to);
@@ -6463,10 +6628,10 @@ var editor = () => [
6463
6628
  ];
6464
6629
 
6465
6630
  // src/extensions/outliner/menu.ts
6466
- import { EditorView as EditorView25, ViewPlugin as ViewPlugin17 } from "@codemirror/view";
6631
+ import { EditorView as EditorView26, ViewPlugin as ViewPlugin19 } from "@codemirror/view";
6467
6632
  import { addEventListener as addEventListener2 } from "@dxos/async";
6468
6633
  var menu = (options = {}) => [
6469
- ViewPlugin17.fromClass(class {
6634
+ ViewPlugin19.fromClass(class {
6470
6635
  view;
6471
6636
  tag;
6472
6637
  rafId;
@@ -6528,7 +6693,7 @@ var menu = (options = {}) => [
6528
6693
  this.rafId = requestAnimationFrame(this.updateButtonPosition.bind(this));
6529
6694
  }
6530
6695
  }),
6531
- EditorView25.theme({
6696
+ EditorView26.theme({
6532
6697
  ".cm-popover-trigger": {
6533
6698
  position: "fixed",
6534
6699
  padding: "0",
@@ -6564,12 +6729,12 @@ var outliner = (_options = {}) => [
6564
6729
  listPaddingLeft: 8
6565
6730
  }),
6566
6731
  // Researve space for menu.
6567
- EditorView26.contentAttributes.of({
6732
+ EditorView27.contentAttributes.of({
6568
6733
  class: "w-full !mr-[3rem]"
6569
6734
  })
6570
6735
  ];
6571
6736
  var decorations = () => [
6572
- ViewPlugin18.fromClass(class {
6737
+ ViewPlugin20.fromClass(class {
6573
6738
  decorations = Decoration12.none;
6574
6739
  constructor(view) {
6575
6740
  this.updateDecorations(view.state, view);
@@ -6594,7 +6759,7 @@ var decorations = () => [
6594
6759
  const lineTo = doc.lineAt(item.contentRange.to);
6595
6760
  const isSelected = selection.includes(item.index) || item === current;
6596
6761
  decorations2.push(Decoration12.line({
6597
- 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"))
6762
+ 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"))
6598
6763
  }).range(line.from, line.from));
6599
6764
  }
6600
6765
  }
@@ -6604,7 +6769,7 @@ var decorations = () => [
6604
6769
  decorations: (v) => v.decorations
6605
6770
  }),
6606
6771
  // Theme.
6607
- EditorView26.theme(Object.assign({
6772
+ EditorView27.theme(Object.assign({
6608
6773
  ".cm-list-item": {
6609
6774
  borderLeftWidth: "1px",
6610
6775
  borderRightWidth: "1px",
@@ -6629,7 +6794,7 @@ var decorations = () => [
6629
6794
  marginBottom: "2px"
6630
6795
  },
6631
6796
  ".cm-list-item-focused": {
6632
- borderColor: "var(--color-neutral-focus-indicator)"
6797
+ borderColor: "var(--color-focus-ring-subtle)"
6633
6798
  },
6634
6799
  "&:focus-within .cm-list-item-selected": {
6635
6800
  borderColor: "var(--color-separator)"
@@ -6639,10 +6804,11 @@ var decorations = () => [
6639
6804
 
6640
6805
  // src/extensions/preview/preview.ts
6641
6806
  import { syntaxTree as syntaxTree10 } from "@codemirror/language";
6642
- import { RangeSetBuilder as RangeSetBuilder6, StateEffect as StateEffect9, StateField as StateField11 } from "@codemirror/state";
6643
- import { Decoration as Decoration13, EditorView as EditorView27, ViewPlugin as ViewPlugin19, WidgetType as WidgetType8 } from "@codemirror/view";
6644
- import { DXN, Entity } from "@dxos/echo";
6645
- var labelResolvedEffect = StateEffect9.define();
6807
+ import { RangeSetBuilder as RangeSetBuilder6, StateEffect as StateEffect10, StateField as StateField11 } from "@codemirror/state";
6808
+ import { Decoration as Decoration13, EditorView as EditorView28, ViewPlugin as ViewPlugin21, WidgetType as WidgetType8 } from "@codemirror/view";
6809
+ import { Entity } from "@dxos/echo";
6810
+ import { EID, URI } from "@dxos/keys";
6811
+ var labelResolvedEffect = StateEffect10.define();
6646
6812
  var preview = (options = {}) => {
6647
6813
  const viewRef = {
6648
6814
  current: void 0
@@ -6659,11 +6825,11 @@ var preview = (options = {}) => {
6659
6825
  return decorations2.map(tr.changes);
6660
6826
  },
6661
6827
  provide: (field) => [
6662
- EditorView27.decorations.from(field),
6663
- EditorView27.atomicRanges.of((view) => view.state.field(field))
6828
+ EditorView28.decorations.from(field),
6829
+ EditorView28.atomicRanges.of((view) => view.state.field(field))
6664
6830
  ]
6665
6831
  }),
6666
- ViewPlugin19.define((view) => {
6832
+ ViewPlugin21.define((view) => {
6667
6833
  viewRef.current = view;
6668
6834
  return {
6669
6835
  destroy() {
@@ -6674,11 +6840,12 @@ var preview = (options = {}) => {
6674
6840
  ];
6675
6841
  };
6676
6842
  var resolveLabel = (db, dxnStr, viewRef) => {
6677
- const dxn = DXN.tryParse(dxnStr);
6678
- if (!dxn) {
6843
+ const echoUri = EID.tryParse(dxnStr);
6844
+ const dxnRef = echoUri ?? (dxnStr.startsWith("dxn:") ? URI.make(dxnStr) : void 0);
6845
+ if (!dxnRef) {
6679
6846
  return;
6680
6847
  }
6681
- const ref = db.makeRef(dxn);
6848
+ const ref = db.makeRef(dxnRef);
6682
6849
  const target = ref.target;
6683
6850
  if (target) {
6684
6851
  return Entity.getLabel(target);
@@ -6696,7 +6863,7 @@ var buildDecorations3 = (state, options, viewRef) => {
6696
6863
  switch (node.name) {
6697
6864
  //
6698
6865
  // Inline widget.
6699
- // [Label](dxn:echo:123)
6866
+ // [Label](echo:/123)
6700
6867
  //
6701
6868
  case "Link": {
6702
6869
  const link = getLinkRef(state, node.node);
@@ -6715,7 +6882,7 @@ var buildDecorations3 = (state, options, viewRef) => {
6715
6882
  }
6716
6883
  //
6717
6884
  // Block widget (transclusion).
6718
- // ![Label](dxn:echo:123)
6885
+ // ![Label](echo:/123)
6719
6886
  //
6720
6887
  case "Image": {
6721
6888
  if (options.addBlockContainer && options.removeBlockContainer) {
@@ -6739,7 +6906,7 @@ var getLinkRef = (state, node) => {
6739
6906
  const urlNode = node.getChild("URL");
6740
6907
  if (mark && urlNode) {
6741
6908
  const dxn = state.sliceDoc(urlNode.from, urlNode.to);
6742
- if (dxn.startsWith("dxn:")) {
6909
+ if (dxn.startsWith("dxn:") || dxn.startsWith("echo:")) {
6743
6910
  const label = state.sliceDoc(mark[0].to, mark[1].from);
6744
6911
  return {
6745
6912
  block: state.sliceDoc(mark[0].from, mark[0].from + 1) === "!",
@@ -6783,7 +6950,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6783
6950
  }
6784
6951
  toDOM(_view) {
6785
6952
  const root = document.createElement("div");
6786
- root.classList.add("cm-preview-block", "dx-density-fine");
6953
+ root.classList.add("cm-preview-block", "dx-density-md");
6787
6954
  this._options.addBlockContainer?.({
6788
6955
  link: this._link,
6789
6956
  el: root
@@ -6799,7 +6966,7 @@ var PreviewBlockWidget = class extends WidgetType8 {
6799
6966
  };
6800
6967
 
6801
6968
  // src/extensions/replacer.ts
6802
- import { EditorView as EditorView28 } from "@codemirror/view";
6969
+ import { EditorView as EditorView29 } from "@codemirror/view";
6803
6970
  var defaultReplacements = [
6804
6971
  {
6805
6972
  input: "--",
@@ -6862,7 +7029,7 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6862
7029
  const sortedReplacements = [
6863
7030
  ...replacements
6864
7031
  ].sort((a, b) => b.input.length - a.input.length);
6865
- return EditorView28.inputHandler.of((view, from, to, insert) => {
7032
+ return EditorView29.inputHandler.of((view, from, to, insert) => {
6866
7033
  if (insert.length !== 1) {
6867
7034
  return false;
6868
7035
  }
@@ -6896,6 +7063,17 @@ var replacer = ({ replacements = defaultReplacements } = {}) => {
6896
7063
  });
6897
7064
  };
6898
7065
 
7066
+ // src/extensions/spacing.ts
7067
+ import { EditorView as EditorView30 } from "@codemirror/view";
7068
+ function lineSpacing(verticalPadding = 2) {
7069
+ return EditorView30.theme({
7070
+ ".cm-line": {
7071
+ paddingTop: `${verticalPadding}px`,
7072
+ paddingBottom: `${verticalPadding}px`
7073
+ }
7074
+ });
7075
+ }
7076
+
6899
7077
  // src/extensions/snippets.ts
6900
7078
  import { keymap as keymap12 } from "@codemirror/view";
6901
7079
  var defaultItems = [
@@ -7108,8 +7286,8 @@ var mixedParser = (registry) => {
7108
7286
  };
7109
7287
 
7110
7288
  // src/extensions/tags/fader.ts
7111
- import { StateEffect as StateEffect10, StateField as StateField12 } from "@codemirror/state";
7112
- import { Decoration as Decoration14, EditorView as EditorView29, ViewPlugin as ViewPlugin20 } from "@codemirror/view";
7289
+ import { StateEffect as StateEffect11, StateField as StateField12 } from "@codemirror/state";
7290
+ import { Decoration as Decoration14, EditorView as EditorView31, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7113
7291
  var DEFAULT_REMOVAL_DELAY = 5e3;
7114
7292
  var DEFAULT_COALESCE_WINDOW = 100;
7115
7293
  var CLEANUP_INTERVAL = 1e3;
@@ -7122,7 +7300,7 @@ var fader = (options = {}) => {
7122
7300
  lastCount = expiries.length;
7123
7301
  }
7124
7302
  };
7125
- const dequeue = StateEffect10.define();
7303
+ const dequeue = StateEffect11.define();
7126
7304
  const fadeField = StateField12.define({
7127
7305
  create: () => ({
7128
7306
  decorations: Decoration14.none,
@@ -7233,9 +7411,9 @@ var fader = (options = {}) => {
7233
7411
  batchStart
7234
7412
  };
7235
7413
  },
7236
- provide: (f) => EditorView29.decorations.from(f, (value) => value.decorations)
7414
+ provide: (f) => EditorView31.decorations.from(f, (value) => value.decorations)
7237
7415
  });
7238
- const cleanup = ViewPlugin20.fromClass(class {
7416
+ const cleanup = ViewPlugin22.fromClass(class {
7239
7417
  view;
7240
7418
  #timer;
7241
7419
  constructor(view) {
@@ -7270,7 +7448,7 @@ var fader = (options = {}) => {
7270
7448
  return [
7271
7449
  fadeField,
7272
7450
  cleanup,
7273
- EditorView29.theme({
7451
+ EditorView31.theme({
7274
7452
  ".cm-fader": {
7275
7453
  animation: "fader 1s ease-out forwards"
7276
7454
  },
@@ -7285,11 +7463,11 @@ var fader = (options = {}) => {
7285
7463
  };
7286
7464
 
7287
7465
  // src/extensions/tags/typewriter.ts
7288
- import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect11, StateField as StateField13 } from "@codemirror/state";
7289
- import { Decoration as Decoration15, EditorView as EditorView30, ViewPlugin as ViewPlugin21, WidgetType as WidgetType9 } from "@codemirror/view";
7466
+ import { Annotation as Annotation3, ChangeSet as ChangeSet2, EditorState as EditorState3, StateEffect as StateEffect12, StateField as StateField13 } from "@codemirror/state";
7467
+ import { Decoration as Decoration15, EditorView as EditorView32, ViewPlugin as ViewPlugin23, WidgetType as WidgetType9 } from "@codemirror/view";
7290
7468
  import { Domino as Domino3 } from "@dxos/ui";
7291
7469
  var typewriterBypass = Annotation3.define();
7292
- var typewriterDrainingEffect = StateEffect11.define();
7470
+ var typewriterDrainingEffect = StateEffect12.define();
7293
7471
  var CURSOR_LINGER = 3e3;
7294
7472
  var FRAME_BUDGET_MS = 4;
7295
7473
  var CHARS_PER_FRAME = 5;
@@ -7300,8 +7478,8 @@ var typewriter = (options = {}) => {
7300
7478
  const flushThreshold = options.flushThreshold ?? FLUSH_THRESHOLD;
7301
7479
  const frameBudgetMs = options.frameBudgetMs ?? FRAME_BUDGET_MS;
7302
7480
  const charsPerFrame = options.charsPerFrame ?? CHARS_PER_FRAME;
7303
- const suppressAppend = StateEffect11.define();
7304
- const insertChunk = StateEffect11.define();
7481
+ const suppressAppend = StateEffect12.define();
7482
+ const insertChunk = StateEffect12.define();
7305
7483
  const bufferField = StateField13.define({
7306
7484
  create: () => ({
7307
7485
  text: "",
@@ -7384,7 +7562,7 @@ var typewriter = (options = {}) => {
7384
7562
  })
7385
7563
  };
7386
7564
  });
7387
- const drainPlugin = ViewPlugin21.fromClass(class {
7565
+ const drainPlugin = ViewPlugin23.fromClass(class {
7388
7566
  view;
7389
7567
  _raf;
7390
7568
  _activeStreamTag = null;
@@ -7489,7 +7667,7 @@ var typewriter = (options = {}) => {
7489
7667
  ].filter(Boolean);
7490
7668
  };
7491
7669
  var typewriterCursor = (bufferField) => {
7492
- const hideCursor = StateEffect11.define();
7670
+ const hideCursor = StateEffect12.define();
7493
7671
  const visibilityField = StateField13.define({
7494
7672
  create: () => ({
7495
7673
  visible: false,
@@ -7543,9 +7721,9 @@ var typewriterCursor = (bufferField) => {
7543
7721
  }).range(pos)
7544
7722
  ]);
7545
7723
  },
7546
- provide: (field) => EditorView30.decorations.from(field)
7724
+ provide: (field) => EditorView32.decorations.from(field)
7547
7725
  });
7548
- const timerPlugin = ViewPlugin21.fromClass(class {
7726
+ const timerPlugin = ViewPlugin23.fromClass(class {
7549
7727
  view;
7550
7728
  _timer;
7551
7729
  constructor(view) {
@@ -7717,7 +7895,7 @@ var linkLength = (buffer, start, bracketAt) => {
7717
7895
 
7718
7896
  // src/extensions/tags/xml-block-decoration.ts
7719
7897
  import { xmlLanguage as xmlLanguage2 } from "@codemirror/lang-xml";
7720
- import { Decoration as Decoration16, ViewPlugin as ViewPlugin22 } from "@codemirror/view";
7898
+ import { Decoration as Decoration16, ViewPlugin as ViewPlugin24 } from "@codemirror/view";
7721
7899
  var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7722
7900
  const lineDecoration = lineClass ? Decoration16.line({
7723
7901
  class: lineClass
@@ -7773,7 +7951,7 @@ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7773
7951
  });
7774
7952
  return Decoration16.set(ranges, true);
7775
7953
  };
7776
- return ViewPlugin22.fromClass(class {
7954
+ return ViewPlugin24.fromClass(class {
7777
7955
  decorations;
7778
7956
  constructor(view) {
7779
7957
  this.decorations = buildDecorations5(view);
@@ -7790,7 +7968,7 @@ var xmlBlockDecoration = ({ tag, lineClass, contentClass, hideTags }) => {
7790
7968
 
7791
7969
  // src/extensions/tags/xml-formatting.ts
7792
7970
  import { xmlLanguage as xmlLanguage3 } from "@codemirror/lang-xml";
7793
- import { Decoration as Decoration17, EditorView as EditorView31, ViewPlugin as ViewPlugin23 } from "@codemirror/view";
7971
+ import { Decoration as Decoration17, EditorView as EditorView33, ViewPlugin as ViewPlugin25 } from "@codemirror/view";
7794
7972
  var XML_TAG_NODES = /* @__PURE__ */ new Set([
7795
7973
  "OpenTag",
7796
7974
  "CloseTag",
@@ -7853,7 +8031,7 @@ var xmlFormatting = ({ skip } = {}) => {
7853
8031
  return Decoration17.set(ranges, true);
7854
8032
  };
7855
8033
  return [
7856
- ViewPlugin23.fromClass(class {
8034
+ ViewPlugin25.fromClass(class {
7857
8035
  decorations;
7858
8036
  constructor(view) {
7859
8037
  this.decorations = buildDecorations5(view);
@@ -7866,9 +8044,9 @@ var xmlFormatting = ({ skip } = {}) => {
7866
8044
  }, {
7867
8045
  decorations: (instance) => instance.decorations
7868
8046
  }),
7869
- EditorView31.baseTheme({
8047
+ EditorView33.baseTheme({
7870
8048
  ".cm-xml-element": {
7871
- backgroundColor: "var(--color-active-surface)",
8049
+ backgroundColor: "var(--color-current-surface)",
7872
8050
  borderRadius: "0.25rem",
7873
8051
  padding: "0.25rem"
7874
8052
  },
@@ -7883,8 +8061,8 @@ var xmlFormatting = ({ skip } = {}) => {
7883
8061
 
7884
8062
  // src/extensions/tags/xml-tags.ts
7885
8063
  import { syntaxTree as syntaxTree11 } from "@codemirror/language";
7886
- import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect12, StateField as StateField14 } from "@codemirror/state";
7887
- import { Decoration as Decoration18, EditorView as EditorView32, ViewPlugin as ViewPlugin24, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
8064
+ import { Prec as Prec7, RangeSetBuilder as RangeSetBuilder7, StateEffect as StateEffect13, StateField as StateField14 } from "@codemirror/state";
8065
+ import { Decoration as Decoration18, EditorView as EditorView34, ViewPlugin as ViewPlugin26, WidgetType as WidgetType10, keymap as keymap14 } from "@codemirror/view";
7888
8066
  import { invariant as invariant7 } from "@dxos/invariant";
7889
8067
  import { log as log11 } from "@dxos/log";
7890
8068
  import { Domino as Domino4 } from "@dxos/ui";
@@ -7996,17 +8174,17 @@ var decodeXmlEntity = (raw) => {
7996
8174
 
7997
8175
  // src/extensions/tags/xml-tags.ts
7998
8176
  var __dxlog_file17 = "/__w/dxos/dxos/packages/ui/ui-editor/src/extensions/tags/xml-tags.ts";
7999
- var navigatePreviousEffect = StateEffect12.define();
8000
- var navigateNextEffect = StateEffect12.define();
8177
+ var navigatePreviousEffect = StateEffect13.define();
8178
+ var navigateNextEffect = StateEffect13.define();
8001
8179
  var getXmlTextChild = (children) => {
8002
8180
  const child = children?.[0];
8003
8181
  return typeof child === "string" ? child : null;
8004
8182
  };
8005
8183
  var xmlWidgetId = (explicit, fallback) => typeof explicit === "string" && explicit.length > 0 ? explicit : fallback;
8006
8184
  var escapeRegExpSource3 = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8007
- var xmlTagContextEffect = StateEffect12.define();
8008
- var xmlTagResetEffect = StateEffect12.define();
8009
- var xmlTagUpdateEffect = StateEffect12.define();
8185
+ var xmlTagContextEffect = StateEffect13.define();
8186
+ var xmlTagResetEffect = StateEffect13.define();
8187
+ var xmlTagUpdateEffect = StateEffect13.define();
8010
8188
  var widgetContextStateField = StateField14.define({
8011
8189
  create: () => void 0,
8012
8190
  update: (value, tr) => {
@@ -8101,7 +8279,7 @@ var keyHandlers = keymap14.of([
8101
8279
  }
8102
8280
  ]);
8103
8281
  var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
8104
- return EditorView32.updateListener.of((update2) => {
8282
+ return EditorView34.updateListener.of((update2) => {
8105
8283
  update2.transactions.forEach((transaction) => {
8106
8284
  for (const effect of transaction.effects) {
8107
8285
  if (effect.is(navigatePreviousEffect)) {
@@ -8129,7 +8307,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
8129
8307
  anchor: line.from,
8130
8308
  head: line.from
8131
8309
  },
8132
- effects: scrollerLineEffect.of({
8310
+ effects: crawlerLineEffect.of({
8133
8311
  line: line.number - 1,
8134
8312
  offset: -16
8135
8313
  })
@@ -8162,7 +8340,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
8162
8340
  anchor: line.to,
8163
8341
  head: line.to
8164
8342
  },
8165
- effects: scrollerLineEffect.of({
8343
+ effects: crawlerLineEffect.of({
8166
8344
  line: line.number - 1,
8167
8345
  offset: -16
8168
8346
  })
@@ -8174,7 +8352,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
8174
8352
  anchor: line.to,
8175
8353
  head: line.to
8176
8354
  },
8177
- effects: scrollerLineEffect.of({
8355
+ effects: crawlerLineEffect.of({
8178
8356
  line: line.number - 1,
8179
8357
  position: "end"
8180
8358
  })
@@ -8186,7 +8364,7 @@ var createNavigationEffectPlugin = (widgetDecorationsField, bookmarks2) => {
8186
8364
  });
8187
8365
  });
8188
8366
  };
8189
- var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin24.fromClass(class {
8367
+ var createWidgetUpdatePlugin = (widgetDecorationsField, notifier) => ViewPlugin26.fromClass(class {
8190
8368
  update(update2) {
8191
8369
  const widgetStateMap = update2.state.field(widgetStateMapStateField);
8192
8370
  const { decorations: decorations2 } = update2.state.field(widgetDecorationsField);
@@ -8271,8 +8449,8 @@ var createWidgetDecorationsField = (registry = {}, notifier) => StateField14.def
8271
8449
  };
8272
8450
  },
8273
8451
  provide: (field) => [
8274
- EditorView32.decorations.from(field, (v) => v.decorations),
8275
- EditorView32.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
8452
+ EditorView34.decorations.from(field, (v) => v.decorations),
8453
+ EditorView34.atomicRanges.of((view) => view.state.field(field).decorations || Decoration18.none)
8276
8454
  ]
8277
8455
  });
8278
8456
  var buildDecorations4 = (state, range, registry, notifier) => {
@@ -8460,7 +8638,7 @@ var PlaceholderWidget2 = class extends WidgetType10 {
8460
8638
  export {
8461
8639
  Cursor,
8462
8640
  EditorState4 as EditorState,
8463
- EditorView33 as EditorView,
8641
+ EditorView35 as EditorView,
8464
8642
  Inline,
8465
8643
  InputModeExtensions,
8466
8644
  List,
@@ -8493,6 +8671,9 @@ export {
8493
8671
  commentsState,
8494
8672
  compactSlots,
8495
8673
  convertTreeToJson,
8674
+ crawler,
8675
+ crawlerActiveEffect,
8676
+ crawlerLineEffect,
8496
8677
  createBasicExtensions,
8497
8678
  createComment,
8498
8679
  createCrawler,
@@ -8544,9 +8725,11 @@ export {
8544
8725
  insertAtCursor,
8545
8726
  insertAtLineStart,
8546
8727
  insertTable,
8728
+ isRangeVisible,
8547
8729
  itemToJSON,
8548
8730
  join,
8549
8731
  keymap15 as keymap,
8732
+ lineSpacing,
8550
8733
  linkTooltip,
8551
8734
  listItemToString,
8552
8735
  listener,
@@ -8579,9 +8762,8 @@ export {
8579
8762
  scrollPastEnd,
8580
8763
  scrollThreadIntoView,
8581
8764
  scrollToLine,
8765
+ scrollbarAutohide,
8582
8766
  scroller,
8583
- scrollerCrawlEffect,
8584
- scrollerLineEffect,
8585
8767
  selectionState,
8586
8768
  setBlockquote,
8587
8769
  setComments,