@dxos/react-ui-editor 0.12.0-next-73daef452f1464b62efdbca75fb0067c1066f264 → 0.13.0

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 (148) hide show
  1. package/dist/lib/index.mjs +148 -133
  2. package/dist/lib/index.mjs.map +1 -1
  3. package/dist/lib/translations.mjs.map +1 -1
  4. package/dist/types/src/components/Editor/Editor.d.ts +8 -8
  5. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
  6. package/dist/types/src/components/Editor/Editor.stories.d.ts +1 -1
  7. package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/Editor/EditorContext.d.ts +6 -6
  9. package/dist/types/src/components/Editor/EditorContext.d.ts.map +1 -1
  10. package/dist/types/src/components/Editor/EditorView.d.ts +5 -4
  11. package/dist/types/src/components/Editor/EditorView.d.ts.map +1 -1
  12. package/dist/types/src/components/Editor/index.d.ts +4 -3
  13. package/dist/types/src/components/Editor/index.d.ts.map +1 -1
  14. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts +2 -2
  15. package/dist/types/src/components/EditorMenuProvider/EditorMenuProvider.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorMenuProvider/index.d.ts +5 -5
  17. package/dist/types/src/components/EditorMenuProvider/index.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts +1 -1
  19. package/dist/types/src/components/EditorMenuProvider/menu-presets.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorMenuProvider/menu.d.ts +4 -4
  21. package/dist/types/src/components/EditorMenuProvider/menu.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts +3 -3
  23. package/dist/types/src/components/EditorMenuProvider/useEditorMenu.d.ts.map +1 -1
  24. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewContext.d.ts +1 -1
  25. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewContext.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorPreviewProvider/EditorPreviewProvider.d.ts.map +1 -1
  27. package/dist/types/src/components/EditorPreviewProvider/index.d.ts +2 -2
  28. package/dist/types/src/components/EditorPreviewProvider/index.d.ts.map +1 -1
  29. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +5 -5
  30. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  31. package/dist/types/src/components/EditorToolbar/blocks.d.ts +2 -2
  32. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  33. package/dist/types/src/components/EditorToolbar/formatting.d.ts +2 -2
  34. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  35. package/dist/types/src/components/EditorToolbar/headings.d.ts +2 -2
  36. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  37. package/dist/types/src/components/EditorToolbar/image.d.ts +1 -1
  38. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -1
  39. package/dist/types/src/components/EditorToolbar/index.d.ts +3 -3
  40. package/dist/types/src/components/EditorToolbar/index.d.ts.map +1 -1
  41. package/dist/types/src/components/EditorToolbar/lists.d.ts +2 -2
  42. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  43. package/dist/types/src/components/EditorToolbar/search.d.ts +1 -1
  44. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -1
  45. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +2 -2
  46. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  47. package/dist/types/src/components/index.d.ts +4 -4
  48. package/dist/types/src/components/index.d.ts.map +1 -1
  49. package/dist/types/src/hooks/index.d.ts +2 -2
  50. package/dist/types/src/hooks/index.d.ts.map +1 -1
  51. package/dist/types/src/hooks/useBasicMarkdownExtensions.d.ts +1 -1
  52. package/dist/types/src/hooks/useTextEditor.d.ts +1 -0
  53. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  54. package/dist/types/src/hooks/useTextEditor.test.d.ts +2 -0
  55. package/dist/types/src/hooks/useTextEditor.test.d.ts.map +1 -0
  56. package/dist/types/src/index.d.ts +2 -2
  57. package/dist/types/src/index.d.ts.map +1 -1
  58. package/dist/types/src/stories/Assistant.stories.d.ts +1 -1
  59. package/dist/types/src/stories/Assistant.stories.d.ts.map +1 -1
  60. package/dist/types/src/stories/Automerge.stories.d.ts.map +1 -1
  61. package/dist/types/src/stories/Blocks.stories.d.ts +1 -1
  62. package/dist/types/src/stories/Blocks.stories.d.ts.map +1 -1
  63. package/dist/types/src/stories/Comments.stories.d.ts.map +1 -1
  64. package/dist/types/src/stories/Diff.stories.d.ts +3 -3
  65. package/dist/types/src/stories/EditorToolbar.stories.d.ts +1 -1
  66. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  67. package/dist/types/src/stories/Experimental.stories.d.ts +3 -3
  68. package/dist/types/src/stories/Folding.stories.d.ts.map +1 -1
  69. package/dist/types/src/stories/Markdown.stories.d.ts +7 -5
  70. package/dist/types/src/stories/Markdown.stories.d.ts.map +1 -1
  71. package/dist/types/src/stories/Outliner.stories.d.ts +3 -1
  72. package/dist/types/src/stories/Outliner.stories.d.ts.map +1 -1
  73. package/dist/types/src/stories/Popover.stories.d.ts +1 -1
  74. package/dist/types/src/stories/Popover.stories.d.ts.map +1 -1
  75. package/dist/types/src/stories/Suggest.stories.d.ts +1 -1
  76. package/dist/types/src/stories/Suggest.stories.d.ts.map +1 -1
  77. package/dist/types/src/stories/TextEditor.stories.d.ts +3 -3
  78. package/dist/types/src/stories/TrackChanges.stories.d.ts +1 -1
  79. package/dist/types/src/stories/TrackChanges.stories.d.ts.map +1 -1
  80. package/dist/types/src/stories/Widgets.stories.d.ts +38 -6
  81. package/dist/types/src/stories/Widgets.stories.d.ts.map +1 -1
  82. package/dist/types/src/stories/{components → testing}/EditorStory.d.ts +2 -2
  83. package/dist/types/src/stories/testing/EditorStory.d.ts.map +1 -0
  84. package/dist/types/src/stories/testing/index.d.ts +3 -0
  85. package/dist/types/src/stories/testing/index.d.ts.map +1 -0
  86. package/dist/types/src/stories/testing/util.d.ts.map +1 -0
  87. package/dist/types/src/translations.d.ts.map +1 -1
  88. package/dist/types/src/util/index.d.ts +1 -1
  89. package/dist/types/src/util/index.d.ts.map +1 -1
  90. package/dist/types/src/util/react.d.ts.map +1 -1
  91. package/dist/types/tsconfig.tsbuildinfo +1 -1
  92. package/package.json +42 -40
  93. package/src/components/Editor/Editor.stories.tsx +4 -4
  94. package/src/components/Editor/Editor.tsx +26 -12
  95. package/src/components/Editor/EditorContext.ts +6 -6
  96. package/src/components/Editor/EditorView.tsx +16 -8
  97. package/src/components/Editor/index.ts +7 -3
  98. package/src/components/EditorMenuProvider/EditorMenuProvider.tsx +68 -79
  99. package/src/components/EditorMenuProvider/index.ts +5 -5
  100. package/src/components/EditorMenuProvider/menu-presets.ts +2 -2
  101. package/src/components/EditorMenuProvider/menu.ts +4 -4
  102. package/src/components/EditorMenuProvider/useEditorMenu.ts +4 -4
  103. package/src/components/EditorPreviewProvider/EditorPreviewContext.ts +1 -2
  104. package/src/components/EditorPreviewProvider/EditorPreviewProvider.tsx +44 -11
  105. package/src/components/EditorPreviewProvider/index.ts +2 -2
  106. package/src/components/EditorToolbar/EditorToolbar.tsx +19 -21
  107. package/src/components/EditorToolbar/blocks.ts +2 -2
  108. package/src/components/EditorToolbar/formatting.ts +31 -22
  109. package/src/components/EditorToolbar/headings.ts +2 -2
  110. package/src/components/EditorToolbar/image.ts +1 -1
  111. package/src/components/EditorToolbar/index.ts +3 -3
  112. package/src/components/EditorToolbar/lists.ts +2 -2
  113. package/src/components/EditorToolbar/search.ts +1 -1
  114. package/src/components/EditorToolbar/view-mode.ts +3 -2
  115. package/src/components/index.ts +4 -4
  116. package/src/hooks/index.ts +2 -2
  117. package/src/hooks/useBasicMarkdownExtensions.ts +3 -3
  118. package/src/hooks/useTextEditor.test.tsx +49 -0
  119. package/src/hooks/useTextEditor.ts +17 -8
  120. package/src/index.ts +2 -2
  121. package/src/stories/Assistant.stories.tsx +6 -6
  122. package/src/stories/Automerge.stories.tsx +63 -17
  123. package/src/stories/Blocks.stories.tsx +4 -4
  124. package/src/stories/Comments.stories.tsx +17 -13
  125. package/src/stories/Diff.stories.tsx +1 -1
  126. package/src/stories/EditorToolbar.stories.tsx +6 -7
  127. package/src/stories/Experimental.stories.tsx +2 -2
  128. package/src/stories/Folding.stories.tsx +13 -10
  129. package/src/stories/Markdown.stories.tsx +26 -1
  130. package/src/stories/Outliner.stories.tsx +24 -2
  131. package/src/stories/Popover.stories.tsx +2 -2
  132. package/src/stories/Suggest.stories.tsx +4 -4
  133. package/src/stories/TextEditor.stories.tsx +1 -1
  134. package/src/stories/Theme.stories.tsx +3 -3
  135. package/src/stories/TrackChanges.stories.tsx +4 -4
  136. package/src/stories/Widgets.stories.tsx +186 -176
  137. package/src/stories/{components → testing}/EditorStory.tsx +7 -7
  138. package/src/stories/testing/index.ts +6 -0
  139. package/src/stories/{components → testing}/util.tsx +2 -2
  140. package/src/translations.ts +2 -2
  141. package/src/util/index.ts +1 -1
  142. package/src/util/react.tsx +4 -7
  143. package/dist/types/src/stories/components/EditorStory.d.ts.map +0 -1
  144. package/dist/types/src/stories/components/index.d.ts +0 -3
  145. package/dist/types/src/stories/components/index.d.ts.map +0 -1
  146. package/dist/types/src/stories/components/util.d.ts.map +0 -1
  147. package/src/stories/components/index.ts +0 -6
  148. /package/dist/types/src/stories/{components → testing}/util.d.ts +0 -0
@@ -1,19 +1,27 @@
1
- import { createContext } from "@radix-ui/react-context";
2
- import * as Atom from "effect/unstable/reactivity/Atom";
3
- import { Fragment, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
1
+ import { createContext, useControllableState } from "@dxos/react-hooks";
2
+ import * as Atom from "effect/reactivity/Atom";
3
+ import { Fragment, Suspense, forwardRef, memo, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from "react";
4
4
  import { createPortal } from "react-dom";
5
5
  import { invariant } from "@dxos/invariant";
6
6
  import { mx } from "@dxos/ui-theme";
7
7
  import { getProviderValue, isNonNullable, isTruthy } from "@dxos/util";
8
- import { Inline, List, addBlockquote, addCodeblock, addLink, createBasicExtensions, createEditorStateTransaction, createMarkdownExtensions, createThemeExtensions, debugDispatcher, decorateMarkdown, documentId, initialSync, insertAtCursor, insertAtLineStart, insertTable, modalStateEffect, modalStateField, placeholder, removeBlockquote, removeCodeblock, removeLink, setHeading, setStyle, toggleList } from "@dxos/ui-editor";
8
+ import { Inline, List, addBlockquote, addCodeblock, addLink, computeDocChanges, createBasicExtensions, createMarkdownExtensions, createThemeExtensions, debugDispatcher, decorateMarkdown, documentId, initialSync, insertAtCursor, insertAtLineStart, insertTable, modalStateEffect, modalStateField, placeholder, removeBlockquote, removeCodeblock, removeLink, restoreEditorState, setHeading, setStyle, toggleList } from "@dxos/ui-editor";
9
9
  import { EditorState, Prec, RangeSetBuilder, StateEffect, StateField, Transaction } from "@codemirror/state";
10
- import { Decoration, EditorView, ViewPlugin, keymap } from "@codemirror/view";
11
- import { useControllableState } from "@radix-ui/react-use-controllable-state";
10
+ import { Decoration, EditorView as EditorView$1, ViewPlugin, keymap } from "@codemirror/view";
12
11
  import { addEventListener } from "@dxos/async";
13
- import { DX_ANCHOR_ACTIVATE, ElevationProvider, Icon, Input, Popover, ScrollArea, toLocalizedString, useDynamicRef, useThemeContext, useTranslation } from "@dxos/react-ui";
12
+ import * as Field from "@dxos/react-ui/Field";
13
+ import * as Hooks from "@dxos/react-ui/Hooks";
14
+ import * as Icon from "@dxos/react-ui/Icon";
15
+ import * as Input from "@dxos/react-ui/Input";
16
+ import * as Layout from "@dxos/react-ui/Layout";
17
+ import * as Popover from "@dxos/react-ui/Popover";
18
+ import * as Theme from "@dxos/react-ui/Theme";
19
+ import * as Typography from "@dxos/react-ui/Typography";
20
+ import * as VirtualAnchor from "@dxos/react-ui/VirtualAnchor";
21
+ import { DX_ANCHOR_ACTIVATE } from "@dxos/ui-types";
14
22
  import { translationKey } from "#translations";
15
23
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
16
- import { Menu, MenuBuilder, useMenuActions } from "@dxos/react-ui-menu";
24
+ import { ActionToolbar, MenuBuilder, useMenuActions } from "@dxos/react-ui-menu";
17
25
  import { openSearchPanel } from "@codemirror/search";
18
26
  import { log } from "@dxos/log";
19
27
  //#region src/components/Editor/controller.ts
@@ -117,7 +125,7 @@ var popover = (options = {}) => {
117
125
  /**
118
126
  * Listen for selection and document changes.
119
127
  */
120
- var popoverTriggerListener = (options) => EditorView.updateListener.of(({ view, docChanged }) => {
128
+ var popoverTriggerListener = (options) => EditorView$1.updateListener.of(({ view, docChanged }) => {
121
129
  const { range: activeRange, trigger } = view.state.field(popoverStateField) ?? {};
122
130
  if (!activeRange) return;
123
131
  const text = view.state.doc.sliceString(activeRange.from, activeRange.to);
@@ -137,7 +145,7 @@ var popoverTriggerListener = (options) => EditorView.updateListener.of(({ view,
137
145
  * Open the popover while typing a word, without requiring a trigger character. Once a range is
138
146
  * active, {@link popoverTriggerListener} keeps it in sync and closes it (whitespace, cursor moves).
139
147
  */
140
- var popoverAutoActivate = (options) => EditorView.updateListener.of(({ view, docChanged, transactions }) => {
148
+ var popoverAutoActivate = (options) => EditorView$1.updateListener.of(({ view, docChanged, transactions }) => {
141
149
  if (!docChanged || !transactions.some((tr) => tr.isUserEvent("input")) || view.state.field(popoverStateField)) return;
142
150
  const selection = view.state.selection.main;
143
151
  if (!selection.empty) return;
@@ -418,13 +426,13 @@ var linkSlashCommands = {
418
426
  var __dxlog_file$3 = "/__w/dxos/dxos/packages/ui/react-ui-editor/src/components/EditorMenuProvider/EditorMenuProvider.tsx";
419
427
  /**
420
428
  * Implements the Popover and listens for the `dx-anchor-activate` event from the `popover` extension's decoration.
421
- * NOTE: We don't use DropdownMenu because the command menu needs to manage focus explicitly.
429
+ * NOTE: We don't use Menu because the command menu needs to manage focus explicitly.
422
430
  * I.e., focus must remain in the editor while displaying the menu (for type-ahead).
423
431
  */
424
432
  var EditorMenuProvider = ({ children, getView, groups, currentItem, open: openProp, defaultOpen, numItems = 8, search, query = "", searchPlaceholder, onOpenChange, onActivate, onSelect, onCancel, onQueryChange, onNavigate }) => {
425
- const { t } = useTranslation(translationKey);
433
+ const { t } = Hooks.useTranslation(translationKey);
426
434
  const triggerRef = useRef(null);
427
- const getViewRef = useDynamicRef(getView);
435
+ const getViewRef = Hooks.useDynamicRef(getView);
428
436
  const [open, setOpen] = useControllableState({
429
437
  prop: openProp,
430
438
  defaultProp: defaultOpen,
@@ -433,7 +441,7 @@ var EditorMenuProvider = ({ children, getView, groups, currentItem, open: openPr
433
441
  invariant(view, void 0, {
434
442
  "~LogMeta": "~LogMeta",
435
443
  F: __dxlog_file$3,
436
- L: 90,
444
+ L: 88,
437
445
  S: void 0,
438
446
  A: ["view", ""]
439
447
  });
@@ -447,8 +455,8 @@ var EditorMenuProvider = ({ children, getView, groups, currentItem, open: openPr
447
455
  useEffect(() => {
448
456
  if (!root) return;
449
457
  return addEventListener(root, DX_ANCHOR_ACTIVATE, (event) => {
450
- const { trigger, dxn } = event;
451
- if (!dxn) {
458
+ const { trigger, eid } = event;
459
+ if (!eid) {
452
460
  triggerRef.current = trigger;
453
461
  if (onActivate) {
454
462
  const view = getViewRef.current?.();
@@ -468,7 +476,7 @@ var EditorMenuProvider = ({ children, getView, groups, currentItem, open: openPr
468
476
  invariant(view, void 0, {
469
477
  "~LogMeta": "~LogMeta",
470
478
  F: __dxlog_file$3,
471
- L: 129,
479
+ L: 127,
472
480
  S: void 0,
473
481
  A: ["view", ""]
474
482
  });
@@ -519,69 +527,54 @@ var EditorMenuProvider = ({ children, getView, groups, currentItem, open: openPr
519
527
  onNavigate
520
528
  ]);
521
529
  return /* @__PURE__ */ jsxs(Popover.Root, {
522
- modal: false,
523
530
  open,
524
- onOpenChange: setOpen,
525
- children: [
526
- /* @__PURE__ */ jsx(Popover.VirtualTrigger, { virtualRef: triggerRef }),
527
- /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, {
528
- align: "start",
529
- classNames: ["flex flex-col", !search && !menuGroups.length && "hidden"],
530
- style: { maxBlockSize: 36 * numItems + 10 + (search ? 36 : 0) },
531
- onEscapeKeyDown: () => {
532
- const currentView = getViewRef.current?.();
533
- if (currentView) onCancel?.({ view: currentView });
534
- },
535
- onOpenAutoFocus: search ? void 0 : (event) => event.preventDefault(),
536
- children: [
537
- search && /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
538
- ref: searchInputRef,
539
- density: "sm",
540
- variant: "subdued",
541
- classNames: "shrink-0 mb-1",
542
- value: query,
543
- placeholder: searchPlaceholder,
544
- "aria-label": searchPlaceholder ?? t("search.label"),
545
- onChange: (event) => onQueryChange?.(event.target.value),
546
- onKeyDown: handleSearchKeyDown
547
- }) }),
548
- /* @__PURE__ */ jsx(Popover.Viewport, {
549
- asChild: true,
550
- classNames: "dx-container",
551
- children: /* @__PURE__ */ jsx(ScrollArea.Root, {
552
- thin: true,
553
- children: /* @__PURE__ */ jsx(ScrollArea.Viewport, { children: /* @__PURE__ */ jsx(Menu$1, {
554
- groups: menuGroups,
555
- currentItem,
556
- onSelect: handleSelect
557
- }) })
558
- })
559
- }),
560
- /* @__PURE__ */ jsx(Popover.Arrow, {})
561
- ]
562
- }) }),
563
- /* @__PURE__ */ jsx("div", {
564
- className: "contents",
565
- ref: setRoot,
566
- children
567
- })
568
- ]
531
+ onOpenChange: ({ open }) => setOpen(open),
532
+ positioning: {
533
+ ...VirtualAnchor.virtualAnchor(triggerRef),
534
+ placement: "bottom-start"
535
+ },
536
+ autoFocus: !!search,
537
+ onEscapeKeyDown: () => {
538
+ const currentView = getViewRef.current?.();
539
+ if (currentView) onCancel?.({ view: currentView });
540
+ },
541
+ children: [/* @__PURE__ */ jsxs(Popover.Content, {
542
+ classNames: !search && !menuGroups.length ? "hidden" : void 0,
543
+ style: { maxBlockSize: 36 * numItems + 10 + (search ? 36 : 0) },
544
+ children: [search && /* @__PURE__ */ jsx(Field.Root, { children: /* @__PURE__ */ jsx(Input.Root, {
545
+ ref: searchInputRef,
546
+ variant: "subdued",
547
+ classNames: "shrink-0 mb-1",
548
+ value: query,
549
+ placeholder: searchPlaceholder,
550
+ "aria-label": searchPlaceholder ?? t("search.label"),
551
+ onChange: (event) => onQueryChange?.(event.target.value),
552
+ onKeyDown: handleSearchKeyDown
553
+ }) }), /* @__PURE__ */ jsx(Popover.Body, { children: /* @__PURE__ */ jsx(Menu, {
554
+ groups: menuGroups,
555
+ currentItem,
556
+ onSelect: handleSelect
557
+ }) })]
558
+ }), /* @__PURE__ */ jsx("div", {
559
+ className: "contents",
560
+ ref: setRoot,
561
+ children
562
+ })]
569
563
  });
570
564
  };
571
- var Menu$1 = ({ groups, currentItem, onSelect }) => {
572
- const { tx } = useThemeContext();
565
+ var Menu = ({ groups, currentItem, onSelect }) => {
573
566
  return /* @__PURE__ */ jsx("ul", { children: groups.map((group, index) => /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(MenuGroup, {
574
567
  group,
575
568
  currentItem,
576
569
  onSelect
577
- }), index < groups.length - 1 && /* @__PURE__ */ jsx("div", { className: tx("menu.separator", {}) })] }, group.id)) });
570
+ }), index < groups.length - 1 && /* @__PURE__ */ jsx(Layout.Separator, {})] }, group.id)) });
578
571
  };
579
572
  var MenuGroup = ({ group, currentItem, onSelect }) => {
580
- const { tx } = useThemeContext();
581
- const { t } = useTranslation();
582
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [group.label && /* @__PURE__ */ jsx("div", {
583
- className: tx("menu.groupLabel", {}),
584
- children: /* @__PURE__ */ jsx("span", { children: toLocalizedString(group.label, t) })
573
+ const { t } = Hooks.useTranslation();
574
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [group.label && /* @__PURE__ */ jsx(Typography.Text, {
575
+ tone: "muted",
576
+ classNames: "px-2",
577
+ children: Theme.toLocalizedString(group.label, t)
585
578
  }), group.items.map((item) => /* @__PURE__ */ jsx(MenuItem, {
586
579
  item,
587
580
  current: currentItem === item.id,
@@ -589,8 +582,7 @@ var MenuGroup = ({ group, currentItem, onSelect }) => {
589
582
  }, item.id))] });
590
583
  };
591
584
  var MenuItem = ({ item, current, onSelect }) => {
592
- const { tx } = useThemeContext();
593
- const { t } = useTranslation();
585
+ const { t } = Hooks.useTranslation();
594
586
  const listRef = useRef(null);
595
587
  useEffect(() => {
596
588
  if (current && listRef.current) listRef.current.scrollIntoView({
@@ -601,11 +593,12 @@ var MenuItem = ({ item, current, onSelect }) => {
601
593
  const handleSelect = useCallback(() => onSelect?.(item), [item, onSelect]);
602
594
  return /* @__PURE__ */ jsxs("li", {
603
595
  ref: listRef,
604
- className: tx("menu.item", {}, [current && "bg-hover-surface"]),
596
+ className: "dx-menu-item",
597
+ "data-highlighted": current ? "" : void 0,
605
598
  onClick: handleSelect,
606
- children: [item.icon && /* @__PURE__ */ jsx(Icon, { icon: item.icon }), /* @__PURE__ */ jsx("span", {
607
- className: "grow truncate",
608
- children: toLocalizedString(item.label, t)
599
+ children: [item.icon && /* @__PURE__ */ jsx(Icon.Icon, { icon: item.icon }), /* @__PURE__ */ jsx("span", {
600
+ className: "dx-menu-item-text",
601
+ children: Theme.toLocalizedString(item.label, t)
609
602
  })]
610
603
  });
611
604
  };
@@ -865,6 +858,7 @@ var addFormatting = (state, getView) => (builder) => {
865
858
  const checked = !!formatting[type];
866
859
  group.action(type, {
867
860
  label: [`formatting.${type}.label`, { ns: translationKey }],
861
+ testId: `editor.toolbar.${type}`,
868
862
  checked,
869
863
  icon
870
864
  }, () => {
@@ -1001,6 +995,7 @@ var addViewMode = (state, onViewModeChange, items = defaultViewModeItems) => (bu
1001
995
  const value = state.viewMode ?? "source";
1002
996
  builder.group("viewMode", {
1003
997
  label: ["view-mode.label", { ns: translationKey }],
998
+ testId: "editor.toolbar.viewMode",
1004
999
  icon: "ph--eye--regular",
1005
1000
  iconOnly: true,
1006
1001
  variant: "dropdownMenu",
@@ -1020,20 +1015,13 @@ var addViewMode = (state, onViewModeChange, items = defaultViewModeItems) => (bu
1020
1015
  };
1021
1016
  //#endregion
1022
1017
  //#region src/components/EditorToolbar/EditorToolbar.tsx
1023
- var SECTION_ROLE = "org.dxos.role.section";
1024
1018
  var EditorToolbar$1 = memo(({ classNames, role, attendableId, onAction, ...props }) => {
1025
1019
  const menuActions = useMarkdownMenuActions(props);
1026
- return /* @__PURE__ */ jsx(ElevationProvider, {
1027
- elevation: role === SECTION_ROLE ? "positioned" : "base",
1028
- children: /* @__PURE__ */ jsx(Menu.Root, {
1029
- ...menuActions,
1030
- attendableId,
1031
- onAction,
1032
- children: /* @__PURE__ */ jsx(Menu.Toolbar, {
1033
- classNames,
1034
- children: /* @__PURE__ */ jsx(Menu.Items, {})
1035
- })
1036
- })
1020
+ return /* @__PURE__ */ jsx(ActionToolbar, {
1021
+ ...menuActions,
1022
+ attendableId,
1023
+ onAction,
1024
+ classNames
1037
1025
  });
1038
1026
  });
1039
1027
  var useMarkdownMenuActions = ({ state, getView, customActions, ...features }) => {
@@ -1067,7 +1055,7 @@ var createMarkdownActions = ({ state, getView, customActions, ...features }) =>
1067
1055
  //#region src/hooks/useBasicMarkdownExtensions.ts
1068
1056
  /**
1069
1057
  * Returns the standard CodeMirror extension stack for an inline markdown editor:
1070
- * basic editor behaviors, themed syntax highlighting (theme mode read from {@link useThemeContext}),
1058
+ * basic editor behaviors, themed syntax highlighting (theme mode read from {@link useThemeMode}),
1071
1059
  * markdown parsing, and visual decorations.
1072
1060
  *
1073
1061
  * Used by surfaces such as `AgentProperties` and `MagazineProperties` to render the
@@ -1077,7 +1065,7 @@ var createMarkdownActions = ({ state, getView, customActions, ...features }) =>
1077
1065
  * so callers passing fresh option literals each render still hit the cache.
1078
1066
  */
1079
1067
  var useBasicMarkdownExtensions = ({ placeholder, decorate = true, readonly = false, extensions } = {}) => {
1080
- const { themeMode } = useThemeContext();
1068
+ const themeMode = Hooks.useThemeMode();
1081
1069
  return useMemo(() => [
1082
1070
  createBasicExtensions({
1083
1071
  placeholder,
@@ -1106,11 +1094,11 @@ var instanceCount = 0;
1106
1094
  * Creates codemirror text editor.
1107
1095
  */
1108
1096
  var useTextEditor = (props = {}, deps = []) => {
1109
- const { id, doc, initialValue, extensions, autoFocus, scrollTo, selection, selectionEnd, debug } = useMemo(() => getProviderValue(props), deps ?? []);
1097
+ const { id, doc, initialValue, extensions, autoFocus, scrollTo, scrollOffset, selection, selectionEnd, debug } = useMemo(() => getProviderValue(props), deps ?? []);
1110
1098
  const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
1111
1099
  const [view, setView] = useState(null);
1112
1100
  const parentRef = useRef(null);
1113
- useEffect(() => {
1101
+ useLayoutEffect(() => {
1114
1102
  let view = null;
1115
1103
  if (parentRef.current) {
1116
1104
  log("create", {
@@ -1120,7 +1108,7 @@ var useTextEditor = (props = {}, deps = []) => {
1120
1108
  }, {
1121
1109
  "~LogMeta": "~LogMeta",
1122
1110
  F: __dxlog_file$1,
1123
- L: 75,
1111
+ L: 82,
1124
1112
  S: void 0
1125
1113
  });
1126
1114
  const length = (doc ?? initialValue)?.length ?? 0;
@@ -1133,20 +1121,23 @@ var useTextEditor = (props = {}, deps = []) => {
1133
1121
  extensions: [
1134
1122
  id && documentId.of(id),
1135
1123
  extensions,
1136
- EditorView.exceptionSink.of((err) => {
1124
+ EditorView$1.exceptionSink.of((err) => {
1137
1125
  log.catch(err, void 0, {
1138
1126
  "~LogMeta": "~LogMeta",
1139
1127
  F: __dxlog_file$1,
1140
- L: 96,
1128
+ L: 103,
1141
1129
  S: void 0
1142
1130
  });
1143
1131
  })
1144
1132
  ].filter(isTruthy)
1145
1133
  });
1146
- view = new EditorView({
1134
+ view = new EditorView$1({
1147
1135
  parent: parentRef.current,
1148
1136
  state,
1149
- scrollTo: scrollTo ? EditorView.scrollIntoView(scrollTo, { yMargin: 96 }) : void 0,
1137
+ scrollTo: scrollTo != null ? EditorView$1.scrollIntoView(scrollTo, {
1138
+ y: "start",
1139
+ yMargin: 0
1140
+ }) : void 0,
1150
1141
  dispatchTransactions: debug ? debugDispatcher : void 0
1151
1142
  });
1152
1143
  if (selectionEnd && !initialSelection) {
@@ -1159,7 +1150,7 @@ var useTextEditor = (props = {}, deps = []) => {
1159
1150
  log("destroy", { id }, {
1160
1151
  "~LogMeta": "~LogMeta",
1161
1152
  F: __dxlog_file$1,
1162
- L: 119,
1153
+ L: 127,
1163
1154
  S: void 0
1164
1155
  });
1165
1156
  view?.destroy();
@@ -1167,7 +1158,7 @@ var useTextEditor = (props = {}, deps = []) => {
1167
1158
  }, deps);
1168
1159
  useEffect(() => {
1169
1160
  if (view) {
1170
- if (scrollTo || selection) {
1161
+ if (scrollTo != null || selection) {
1171
1162
  if (selection && selection.anchor > view.state.doc.length) {
1172
1163
  log.warn("invalid selection", {
1173
1164
  length: view.state.doc.length,
@@ -1176,24 +1167,26 @@ var useTextEditor = (props = {}, deps = []) => {
1176
1167
  }, {
1177
1168
  "~LogMeta": "~LogMeta",
1178
1169
  F: __dxlog_file$1,
1179
- L: 128,
1170
+ L: 136,
1180
1171
  S: void 0
1181
1172
  });
1182
1173
  return;
1183
1174
  }
1184
- view.dispatch(createEditorStateTransaction({
1175
+ restoreEditorState(view, {
1185
1176
  scrollTo,
1177
+ scrollOffset,
1186
1178
  selection
1187
- }));
1179
+ });
1188
1180
  }
1189
1181
  }
1190
1182
  }, [
1191
1183
  view,
1192
1184
  scrollTo,
1185
+ scrollOffset,
1193
1186
  selection
1194
1187
  ]);
1195
1188
  useEffect(() => {
1196
- if (view && autoFocus) view.focus();
1189
+ if (view && autoFocus && !view.dom.ownerDocument.activeElement?.closest("[role=\"menu\"], [role=\"listbox\"]")) view.focus();
1197
1190
  }, [autoFocus, view]);
1198
1191
  return {
1199
1192
  parentRef,
@@ -1221,14 +1214,14 @@ var useTextEditor = (props = {}, deps = []) => {
1221
1214
  * Minimal text editor.
1222
1215
  * NOTE: This shouold not be used with the automerge extension.
1223
1216
  */
1224
- var EditorView$2 = forwardRef(({ classNames, id, extensions, selectionEnd, focusable = true, value, onChange, ...props }, forwardedRef) => {
1217
+ var EditorView = forwardRef(({ classNames, id, extensions, selectionEnd, focusable = true, value, onChange, ...props }, forwardedRef) => {
1225
1218
  const onChangeRef = useRef(onChange);
1226
1219
  onChangeRef.current = onChange;
1227
1220
  const { parentRef, focusAttributes, view } = useTextEditor(() => ({
1228
1221
  id,
1229
1222
  initialValue: value,
1230
1223
  selectionEnd,
1231
- extensions: [extensions ?? [], EditorView.updateListener.of(({ view, docChanged, transactions }) => {
1224
+ extensions: [extensions ?? [], EditorView$1.updateListener.of(({ view, docChanged, transactions }) => {
1232
1225
  if (!transactions.some((tr) => tr.annotation(Transaction.userEvent) === initialSync.value) && docChanged) onChangeRef.current?.(view.state.doc.toString());
1233
1226
  })],
1234
1227
  ...props
@@ -1245,14 +1238,13 @@ var EditorView$2 = forwardRef(({ classNames, id, extensions, selectionEnd, focus
1245
1238
  const next = value;
1246
1239
  if (view.state.doc.toString() === next) return;
1247
1240
  requestAnimationFrame(() => {
1248
- if (view.state.doc.toString() === next) return;
1241
+ const current = view.state.doc.toString();
1242
+ if (current === next) return;
1243
+ const changes = computeDocChanges(current, next);
1244
+ if (changes.length === 0) return;
1249
1245
  view.dispatch({
1250
1246
  annotations: initialSync,
1251
- changes: [{
1252
- from: 0,
1253
- to: view.state.doc.length,
1254
- insert: next
1255
- }],
1247
+ changes,
1256
1248
  selection: selectionEnd ? { anchor: next.length } : void 0
1257
1249
  });
1258
1250
  if (selectionEnd) view.focus();
@@ -1315,16 +1307,16 @@ var EDITOR_VIEW_NAME = "Editor.View";
1315
1307
  * View component that renders the actual CodeMirror editor.
1316
1308
  * Automatically registers the editor controller with the context.
1317
1309
  */
1318
- var EditorView$1 = ({ extensions: providedExtensions, ...props }) => {
1310
+ var EditorView$2 = ({ extensions: providedExtensions, ...props }) => {
1319
1311
  const { extensions: additionalExtensions = [], setController } = useEditorContext(EDITOR_VIEW_NAME);
1320
1312
  const extensions = useMemo(() => [additionalExtensions, providedExtensions].filter(isNonNullable).flat(), [providedExtensions, additionalExtensions]);
1321
- return /* @__PURE__ */ jsx(EditorView$2, {
1313
+ return /* @__PURE__ */ jsx(EditorView, {
1322
1314
  ...props,
1323
1315
  extensions,
1324
1316
  ref: setController
1325
1317
  });
1326
1318
  };
1327
- EditorView$1.displayName = EDITOR_VIEW_NAME;
1319
+ EditorView$2.displayName = EDITOR_VIEW_NAME;
1328
1320
  var EDITOR_TOOLBAR_NAME = "Editor.Toolbar";
1329
1321
  /**
1330
1322
  * Toolbar component that provides editor formatting and control actions.
@@ -1336,7 +1328,7 @@ var EditorToolbar = (props) => {
1336
1328
  invariant(controller?.view, void 0, {
1337
1329
  "~LogMeta": "~LogMeta",
1338
1330
  F: __dxlog_file,
1339
- L: 155,
1331
+ L: 159,
1340
1332
  S: void 0,
1341
1333
  A: ["controller?.view", ""]
1342
1334
  });
@@ -1356,7 +1348,13 @@ var EDITOR_BLOCKS_NAME = "Editor.Blocks";
1356
1348
  */
1357
1349
  var EditorBlocks = () => {
1358
1350
  const { widgets = [] } = useEditorContext(EDITOR_BLOCKS_NAME);
1359
- return /* @__PURE__ */ jsx(Fragment$1, { children: widgets.map(({ id, root, Component, props }) => /* @__PURE__ */ jsx("div", { children: createPortal(/* @__PURE__ */ jsx(Component, { ...props }), root) }, id)) });
1351
+ return /* @__PURE__ */ jsx(Fragment$1, { children: widgets.map(({ id, root, Component, props }) => /* @__PURE__ */ jsx("div", {
1352
+ "data-testid": "editor.blocks.portal",
1353
+ children: createPortal(/* @__PURE__ */ jsx(Suspense, {
1354
+ fallback: null,
1355
+ children: /* @__PURE__ */ jsx(Component, { ...props })
1356
+ }), root)
1357
+ }, id)) });
1360
1358
  };
1361
1359
  EditorBlocks.displayName = EDITOR_BLOCKS_NAME;
1362
1360
  var EDITOR_DIAGNOSTICS_NAME = "Editor.Diagnostics";
@@ -1392,11 +1390,11 @@ var EditorDiagnostics = ({ classNames }) => {
1392
1390
  const head = doc.lineAt(main.head);
1393
1391
  const extensionCount = countExtensions(extensions);
1394
1392
  const item = (label, value) => /* @__PURE__ */ jsxs("span", { children: [/* @__PURE__ */ jsxs("span", {
1395
- className: "text-subdued",
1393
+ className: "text-fg-subtle",
1396
1394
  children: [label, " "]
1397
1395
  }), value] });
1398
1396
  return /* @__PURE__ */ jsxs("div", {
1399
- className: mx("flex flex-wrap gap-x-4 gap-y-0.5 border-bs border-separator px-2 py-1 text-xs font-mono text-description", classNames),
1397
+ className: mx("flex flex-wrap gap-x-4 gap-y-0.5 border-bs border-separator px-2 py-1 text-xs font-mono text-fg-muted", classNames),
1400
1398
  "data-testid": "editor.diagnostics",
1401
1399
  children: [
1402
1400
  item("chars", doc.length),
@@ -1414,7 +1412,7 @@ var Editor = {
1414
1412
  Root: EditorRoot,
1415
1413
  Toolbar: EditorToolbar,
1416
1414
  Content: EditorContent,
1417
- View: EditorView$1,
1415
+ View: EditorView$2,
1418
1416
  Blocks: EditorBlocks,
1419
1417
  Diagnostics: EditorDiagnostics
1420
1418
  };
@@ -1432,13 +1430,21 @@ var EditorPreviewProvider = ({ children, onLookup }) => {
1432
1430
  const triggerRef = useRef(null);
1433
1431
  const [value, setValue] = useState({});
1434
1432
  const [open, setOpen] = useState(false);
1433
+ const activationRef = useRef(0);
1435
1434
  const handleActivate = useCallback((event) => {
1436
- const { dxn, label, trigger } = event;
1435
+ if (event.state === false) {
1436
+ if (event.trigger !== triggerRef.current) return;
1437
+ activationRef.current++;
1438
+ setOpen(false);
1439
+ return;
1440
+ }
1441
+ const sequence = ++activationRef.current;
1442
+ const { eid, label, trigger } = event;
1437
1443
  setValue((value) => ({
1438
1444
  ...value,
1439
1445
  link: {
1440
1446
  label,
1441
- dxn
1447
+ eid
1442
1448
  },
1443
1449
  pending: true
1444
1450
  }));
@@ -1446,13 +1452,20 @@ var EditorPreviewProvider = ({ children, onLookup }) => {
1446
1452
  queueMicrotask(() => setOpen(true));
1447
1453
  onLookup?.({
1448
1454
  label,
1449
- dxn
1450
- }).then((target) => setValue((value) => ({
1451
- ...value,
1452
- target: target ?? void 0,
1453
- pending: false
1454
- })));
1455
+ eid
1456
+ }).then((target) => {
1457
+ if (sequence !== activationRef.current) return;
1458
+ setValue((value) => ({
1459
+ ...value,
1460
+ target: target ?? void 0,
1461
+ pending: false
1462
+ }));
1463
+ });
1455
1464
  }, [onLookup]);
1465
+ const handleOpenChange = useCallback((next) => {
1466
+ if (!next) activationRef.current++;
1467
+ setOpen(next);
1468
+ }, []);
1456
1469
  const [root, setRoot] = useState(null);
1457
1470
  useEffect(() => {
1458
1471
  if (!root || !handleActivate) return;
@@ -1465,18 +1478,20 @@ var EditorPreviewProvider = ({ children, onLookup }) => {
1465
1478
  pending: value.pending,
1466
1479
  link: value.link,
1467
1480
  target: value.target,
1468
- children: /* @__PURE__ */ jsxs(Popover.Root, {
1481
+ children: /* @__PURE__ */ jsx(Popover.Root, {
1469
1482
  open,
1470
- onOpenChange: setOpen,
1471
- children: [/* @__PURE__ */ jsx(Popover.VirtualTrigger, { virtualRef: triggerRef }), /* @__PURE__ */ jsx("div", {
1483
+ onOpenChange: ({ open }) => handleOpenChange(open),
1484
+ positioning: VirtualAnchor.virtualAnchor(triggerRef),
1485
+ autoFocus: false,
1486
+ children: /* @__PURE__ */ jsx("div", {
1472
1487
  className: "contents",
1473
1488
  ref: setRoot,
1474
1489
  children
1475
- })]
1490
+ })
1476
1491
  })
1477
1492
  });
1478
1493
  };
1479
1494
  //#endregion
1480
- export { Editor, EditorMenuProvider, EditorPreviewProvider, addViewMode, createEditorController, createMenuGroup, defaultViewModeItems, filterMenuGroups, formattingCommands, getMenuItem, getNextMenuItem, getPreviousMenuItem, linkSlashCommands, noopController, popover, popoverRangeEffect, popoverStateField, useBasicMarkdownExtensions, useEditorContext, useEditorMenu, useEditorPreview, useTextEditor };
1495
+ export { Editor, EditorMenuProvider, EditorPreviewProvider, EditorView as TextEditor, addViewMode, createEditorController, createMenuGroup, defaultViewModeItems, filterMenuGroups, formattingCommands, getMenuItem, getNextMenuItem, getPreviousMenuItem, linkSlashCommands, noopController, popover, popoverRangeEffect, popoverStateField, useBasicMarkdownExtensions, useEditorContext, useEditorMenu, useEditorPreview, useTextEditor };
1481
1496
 
1482
1497
  //# sourceMappingURL=index.mjs.map