@dxos/react-ui-editor 0.3.11-main.cb8120f → 0.3.11-main.ccc0ca3

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 (54) hide show
  1. package/dist/lib/browser/index.mjs +362 -45
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +1 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
  8. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +23 -0
  9. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  10. package/dist/types/src/components/Toolbar/Toolbar.d.ts +23 -7
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  12. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +10 -1
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  14. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  15. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  16. package/dist/types/src/components/index.d.ts +1 -0
  17. package/dist/types/src/components/index.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  19. package/dist/types/src/extensions/basic.d.ts +1 -1
  20. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  21. package/dist/types/src/extensions/comments.d.ts +1 -1
  22. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  23. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  24. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  25. package/dist/types/src/extensions/markdown/formatting.d.ts +2 -0
  26. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  27. package/dist/types/src/hooks/index.d.ts +1 -0
  28. package/dist/types/src/hooks/index.d.ts.map +1 -1
  29. package/dist/types/src/hooks/useActionHandler.d.ts +2 -2
  30. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  31. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  32. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  33. package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
  34. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  35. package/dist/types/src/themes/default.d.ts.map +1 -1
  36. package/package.json +26 -25
  37. package/src/components/TextEditor/MarkdownEditor.stories.tsx +2 -3
  38. package/src/components/TextEditor/TextEditor.tsx +23 -13
  39. package/src/components/TextEditor/comments.stories.tsx +3 -3
  40. package/src/components/TextEditor/hooks.stories.tsx +106 -0
  41. package/src/components/Toolbar/Toolbar.stories.tsx +7 -3
  42. package/src/components/Toolbar/Toolbar.tsx +120 -69
  43. package/src/components/Toolbar/index.ts +5 -0
  44. package/src/components/index.ts +1 -0
  45. package/src/extensions/automerge/update-automerge.ts +7 -7
  46. package/src/extensions/basic.ts +15 -13
  47. package/src/extensions/comments.ts +2 -2
  48. package/src/extensions/markdown/bundle.ts +10 -5
  49. package/src/extensions/markdown/formatting.ts +31 -2
  50. package/src/hooks/index.ts +1 -0
  51. package/src/hooks/useActionHandler.ts +22 -5
  52. package/src/hooks/useEditorView.ts +29 -0
  53. package/src/hooks/useTextEditor.ts +143 -19
  54. package/src/themes/default.ts +3 -1
@@ -8,14 +8,13 @@ import { tags as tags2 } from "@lezer/highlight";
8
8
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
9
  import { EditorState as EditorState2 } from "@codemirror/state";
10
10
  import { EditorView as EditorView13 } from "@codemirror/view";
11
- import { useFocusableGroup } from "@fluentui/react-tabster";
12
11
  import { vim } from "@replit/codemirror-vim";
13
12
  import defaultsDeep2 from "lodash.defaultsdeep";
14
13
  import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
15
14
  import { log as log2 } from "@dxos/log";
16
15
  import { useThemeContext } from "@dxos/react-ui";
17
16
  import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
18
- import { nonNullable as nonNullable2 } from "@dxos/util";
17
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
19
18
 
20
19
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
21
20
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
@@ -125,18 +124,18 @@ var updateAutomerge = (field, handle, transactions, state) => {
125
124
  return void 0;
126
125
  }
127
126
  const newHeads = handle.changeAt(lastHeads, (doc) => {
128
- const t = [];
127
+ const invertedTransactions = [];
129
128
  for (const tr of transactions) {
130
129
  tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
131
- t.push({
132
- fromA,
133
- toA,
130
+ invertedTransactions.push({
131
+ from: fromA,
132
+ del: toA - fromA,
134
133
  insert
135
134
  });
136
135
  });
137
136
  }
138
- t.reverse().forEach(({ fromA, toA, insert }) => {
139
- A2.splice(doc, path.slice(), fromA, toA - fromA, insert.toString());
137
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
138
+ A2.splice(doc, path.slice(), from, del, insert.toString());
140
139
  });
141
140
  });
142
141
  return newHeads ?? void 0;
@@ -637,18 +636,19 @@ import { history } from "@codemirror/commands";
637
636
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
638
637
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
639
638
  import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
640
- var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
641
- lineWrapping ? EditorView3.lineWrapping : [],
639
+ import { isNotFalsy } from "@dxos/util";
640
+ var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => [
641
+ lineWrapping && EditorView3.lineWrapping,
642
642
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
643
643
  bracketMatching(),
644
644
  closeBrackets(),
645
645
  drawSelection(),
646
646
  highlightActiveLine(),
647
647
  history(),
648
- _placeholder ? placeholder(_placeholder) : [],
648
+ _placeholder && placeholder(_placeholder),
649
649
  // https://github.com/codemirror/theme-one-dark
650
650
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
651
- ];
651
+ ].filter(isNotFalsy);
652
652
 
653
653
  // packages/ui/react-ui-editor/src/extensions/blast.ts
654
654
  import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
@@ -1086,7 +1086,7 @@ var commentMark = Decoration2.mark({
1086
1086
  var commentCurrentMark = Decoration2.mark({
1087
1087
  class: "cm-comment-current"
1088
1088
  });
1089
- var setFocus = (view, id, center = true) => {
1089
+ var focusComment = (view, id, center = true) => {
1090
1090
  const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1091
1091
  if (!comment?.comment.cursor) {
1092
1092
  return;
@@ -1293,7 +1293,7 @@ var createComment = (view) => {
1293
1293
  current: id
1294
1294
  }),
1295
1295
  selection: {
1296
- anchor: from
1296
+ anchor: to
1297
1297
  }
1298
1298
  });
1299
1299
  return true;
@@ -1453,6 +1453,7 @@ import { searchKeymap } from "@codemirror/search";
1453
1453
  import { EditorState } from "@codemirror/state";
1454
1454
  import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1455
1455
  import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1456
+ import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
1456
1457
 
1457
1458
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1458
1459
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -1643,11 +1644,11 @@ var markdownHighlightStyle = (readonly) => {
1643
1644
  };
1644
1645
 
1645
1646
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1646
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1647
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1647
1648
  return [
1649
+ lineWrapping && EditorView7.lineWrapping,
1648
1650
  EditorState.allowMultipleSelections.of(true),
1649
1651
  EditorState.tabSize.of(2),
1650
- EditorView7.lineWrapping,
1651
1652
  // https://github.com/codemirror/basic-setup
1652
1653
  bracketMatching2(),
1653
1654
  closeBrackets2(),
@@ -1657,7 +1658,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1657
1658
  highlightActiveLine2(),
1658
1659
  history2(),
1659
1660
  indentOnInput(),
1660
- _placeholder ? placeholder2(_placeholder) : [],
1661
+ _placeholder && placeholder2(_placeholder),
1661
1662
  // Main extension.
1662
1663
  // https://github.com/codemirror/lang-markdown
1663
1664
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1692,7 +1693,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1692
1693
  // https://codemirror.net/docs/ref/#commands.standardKeymap
1693
1694
  ...standardKeymap
1694
1695
  ])
1695
- ];
1696
+ ].filter(isNotFalsy2);
1696
1697
  };
1697
1698
 
1698
1699
  // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
@@ -1810,6 +1811,7 @@ var code2 = () => {
1810
1811
  };
1811
1812
 
1812
1813
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1814
+ import { snippet } from "@codemirror/autocomplete";
1813
1815
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1814
1816
  import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1815
1817
  import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
@@ -1818,8 +1820,8 @@ var setHeading = (level) => (view) => {
1818
1820
  const changes = [];
1819
1821
  for (const range of ranges) {
1820
1822
  const { number } = doc.lineAt(range.anchor);
1821
- const { from } = doc.line(number);
1822
- const line = doc.sliceString(from, from + 7);
1823
+ const { from, to } = doc.line(number);
1824
+ const line = doc.sliceString(from, to);
1823
1825
  const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1824
1826
  const current = marks?.length ?? 0;
1825
1827
  if (level !== current) {
@@ -1858,6 +1860,31 @@ var toggleStyle = (mark2) => (view) => {
1858
1860
  }
1859
1861
  return true;
1860
1862
  };
1863
+ var snippets = {
1864
+ codeblock: snippet([
1865
+ "```#{lang}",
1866
+ " #{}",
1867
+ "```"
1868
+ ].join("\n")),
1869
+ table: snippet([
1870
+ "| #{col1} | #{col2} |",
1871
+ "| ---- | ---- |",
1872
+ "| #{val1} | #{val2} |",
1873
+ "| #{val3} | #{val4} |"
1874
+ ].join("\n"))
1875
+ };
1876
+ var insertCodeblock = (view) => {
1877
+ const { selection: { main }, doc } = view.state;
1878
+ const { number } = doc.lineAt(main.anchor);
1879
+ const { from } = doc.line(number);
1880
+ snippets.codeblock(view, null, from, from);
1881
+ };
1882
+ var insertTable = (view) => {
1883
+ const { selection: { main }, doc } = view.state;
1884
+ const { number } = doc.lineAt(main.anchor);
1885
+ const { from } = doc.line(number);
1886
+ snippets.table(view, null, from, from);
1887
+ };
1861
1888
  var toggleBold = toggleStyle("**");
1862
1889
  var toggleItalic = toggleStyle("_");
1863
1890
  var toggleStrikethrough = toggleStyle("~~");
@@ -2552,8 +2579,9 @@ var defaultTheme = {
2552
2579
  outline: "none"
2553
2580
  },
2554
2581
  ".cm-scroller": {
2555
- overflow: "visible",
2556
- fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
2582
+ // overflow: 'visible',
2583
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
2584
+ lineHeight: 1.4
2557
2585
  },
2558
2586
  ".cm-content": {
2559
2587
  padding: 0,
@@ -2764,9 +2792,6 @@ var EditorModes = [
2764
2792
  "vim"
2765
2793
  ];
2766
2794
  var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2767
- const tabsterDOMAttribute = useFocusableGroup({
2768
- tabBehavior: "limited"
2769
- });
2770
2795
  const { themeMode } = useThemeContext();
2771
2796
  const rootRef = useRef(null);
2772
2797
  const [view, setView] = useState(null);
@@ -2774,7 +2799,15 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2774
2799
  view
2775
2800
  ]);
2776
2801
  useEffect2(() => {
2777
- if (autoFocus) {
2802
+ if (autoFocus && !view?.hasFocus) {
2803
+ if (view?.state.selection.main?.from === 0) {
2804
+ const { to } = view.state.doc.lineAt(0);
2805
+ view?.dispatch({
2806
+ selection: {
2807
+ anchor: to
2808
+ }
2809
+ });
2810
+ }
2778
2811
  view?.focus();
2779
2812
  }
2780
2813
  }, [
@@ -2793,7 +2826,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2793
2826
  EditorView13.exceptionSink.of((err) => {
2794
2827
  log2.catch(err, void 0, {
2795
2828
  F: __dxlog_file3,
2796
- L: 124,
2829
+ L: 134,
2797
2830
  S: void 0,
2798
2831
  C: (f, a) => f(...a)
2799
2832
  });
@@ -2815,11 +2848,11 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2815
2848
  // Storage and replication.
2816
2849
  // NOTE: This must come before user extensions.
2817
2850
  model.extension,
2818
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
2851
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2819
2852
  editorMode === "vim" && vim(),
2820
2853
  // Custom.
2821
2854
  ...extensions
2822
- ].filter(nonNullable2)
2855
+ ].filter(isNotFalsy3)
2823
2856
  });
2824
2857
  const newView = new EditorView13({
2825
2858
  state,
@@ -2856,7 +2889,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2856
2889
  }
2857
2890
  case "Escape": {
2858
2891
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2859
- rootRef.current?.focus();
2892
+ view?.focus();
2860
2893
  }
2861
2894
  break;
2862
2895
  }
@@ -2869,9 +2902,9 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2869
2902
  key: model.id,
2870
2903
  role: "none",
2871
2904
  tabIndex: 0,
2872
- onKeyUp: handleKeyUp,
2873
2905
  ...slots.root,
2874
- ...editorMode !== "vim" && tabsterDOMAttribute,
2906
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
2907
+ onKeyUp: handleKeyUp,
2875
2908
  ref: rootRef
2876
2909
  });
2877
2910
  });
@@ -2882,7 +2915,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2882
2915
  ref: forwardedRef,
2883
2916
  readonly,
2884
2917
  extensions: [
2885
- basicBundle({
2918
+ createBasicBundle({
2886
2919
  themeMode,
2887
2920
  placeholder: placeholder3,
2888
2921
  lineWrapping
@@ -2901,7 +2934,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2901
2934
  ref: forwardedRef,
2902
2935
  readonly,
2903
2936
  extensions: [
2904
- markdownBundle({
2937
+ createMarkdownExtensions({
2905
2938
  themeMode,
2906
2939
  placeholder: placeholder3
2907
2940
  }),
@@ -2928,6 +2961,167 @@ var defaultMarkdownSlots = {
2928
2961
  ...defaultSlots
2929
2962
  };
2930
2963
 
2964
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
2965
+ import { At, CaretRight, ChatText, Code, Image, Link, ListBullets, ListChecks, ListNumbers, TextStrikethrough, Table as Table2, TextB, TextItalic } from "@phosphor-icons/react";
2966
+ import { createContext } from "@radix-ui/react-context";
2967
+ import React2 from "react";
2968
+ import { Button, DensityProvider, Select } from "@dxos/react-ui";
2969
+ import { getSize } from "@dxos/react-ui-theme";
2970
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
2971
+ var ToolbarRoot = ({ children, onAction }) => {
2972
+ return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
2973
+ onAction
2974
+ }, /* @__PURE__ */ React2.createElement(DensityProvider, {
2975
+ density: "fine"
2976
+ }, /* @__PURE__ */ React2.createElement("div", {
2977
+ role: "toolbar",
2978
+ className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
2979
+ }, children)));
2980
+ };
2981
+ var ToolbarButton = ({ Icon, onClick, title }) => {
2982
+ const { onAction } = useToolbarContext("ToolbarButton");
2983
+ const handleClick = () => {
2984
+ const action = onClick();
2985
+ if (action) {
2986
+ onAction?.(action);
2987
+ }
2988
+ };
2989
+ return /* @__PURE__ */ React2.createElement(Button, {
2990
+ variant: "ghost",
2991
+ classNames: "p-2",
2992
+ onClick: handleClick,
2993
+ title
2994
+ }, /* @__PURE__ */ React2.createElement(Icon, {
2995
+ className: getSize(5)
2996
+ }));
2997
+ };
2998
+ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
2999
+ className: "grow"
3000
+ });
3001
+ var MarkdownHeading = () => {
3002
+ const { onAction } = useToolbarContext("MarkdownFormatting");
3003
+ return /* @__PURE__ */ React2.createElement(Select.Root, {
3004
+ defaultValue: "0",
3005
+ onValueChange: (value) => onAction?.({
3006
+ type: "heading",
3007
+ data: parseInt(value)
3008
+ })
3009
+ }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
3010
+ classNames: "w-[8rem]"
3011
+ }), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
3012
+ value: "0"
3013
+ }, "Paragraph"), [
3014
+ 1,
3015
+ 2,
3016
+ 3,
3017
+ 4,
3018
+ 5,
3019
+ 6
3020
+ ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
3021
+ key: level,
3022
+ value: String(level)
3023
+ }, "Heading ", level))))));
3024
+ };
3025
+ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
3026
+ role: "none"
3027
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3028
+ Icon: TextB,
3029
+ title: "Bold",
3030
+ onClick: () => ({
3031
+ type: "bold"
3032
+ })
3033
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3034
+ Icon: TextItalic,
3035
+ title: "Italic",
3036
+ onClick: () => ({
3037
+ type: "italic"
3038
+ })
3039
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3040
+ Icon: TextStrikethrough,
3041
+ title: "Strike-through",
3042
+ onClick: () => ({
3043
+ type: "strikethrough"
3044
+ })
3045
+ }));
3046
+ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
3047
+ role: "none"
3048
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3049
+ Icon: ListBullets,
3050
+ title: "Bullet list",
3051
+ onClick: () => ({
3052
+ type: "list"
3053
+ })
3054
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3055
+ Icon: ListNumbers,
3056
+ title: "Numbered list",
3057
+ onClick: () => ({
3058
+ type: "list-ordered"
3059
+ })
3060
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3061
+ Icon: ListChecks,
3062
+ title: "Task list",
3063
+ onClick: () => ({
3064
+ type: "list-tasks"
3065
+ })
3066
+ }));
3067
+ var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
3068
+ role: "none"
3069
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3070
+ Icon: CaretRight,
3071
+ title: "Block quite",
3072
+ onClick: () => ({
3073
+ type: "blockquote"
3074
+ })
3075
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3076
+ Icon: Code,
3077
+ title: "Code block",
3078
+ onClick: () => ({
3079
+ type: "codeblock"
3080
+ })
3081
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3082
+ Icon: Table2,
3083
+ title: "Table",
3084
+ onClick: () => ({
3085
+ type: "table"
3086
+ })
3087
+ }));
3088
+ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
3089
+ role: "none"
3090
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3091
+ Icon: Link,
3092
+ title: "Link",
3093
+ onClick: () => ({
3094
+ type: "link"
3095
+ })
3096
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3097
+ Icon: At,
3098
+ title: "Mention",
3099
+ onClick: () => ({
3100
+ type: "mention"
3101
+ })
3102
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3103
+ Icon: Image,
3104
+ title: "Image",
3105
+ onClick: () => ({
3106
+ type: "image"
3107
+ })
3108
+ }));
3109
+ var MarkdownStandard = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(MarkdownHeading, null), /* @__PURE__ */ React2.createElement(MarkdownStyles, null), /* @__PURE__ */ React2.createElement(MarkdownLists, null), /* @__PURE__ */ React2.createElement(MarkdownBlocks, null), /* @__PURE__ */ React2.createElement(MarkdownLinks, null));
3110
+ var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
3111
+ Icon: ChatText,
3112
+ title: "Create comment",
3113
+ onClick: () => ({
3114
+ type: "comment"
3115
+ })
3116
+ });
3117
+ var Toolbar = {
3118
+ Root: ToolbarRoot,
3119
+ Button: ToolbarButton,
3120
+ Separator: ToolbarSeparator,
3121
+ Markdown: MarkdownStandard,
3122
+ Extended: MarkdownExtended
3123
+ };
3124
+
2931
3125
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2932
3126
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2933
3127
  import { Context as Context2 } from "@dxos/context";
@@ -3061,19 +3255,28 @@ var useActionHandler = (view) => {
3061
3255
  case "list":
3062
3256
  toggleList(view);
3063
3257
  break;
3258
+ case "codeblock":
3259
+ insertCodeblock(view);
3260
+ break;
3261
+ case "table":
3262
+ insertTable(view);
3263
+ break;
3064
3264
  case "comment":
3065
3265
  createComment(view);
3066
3266
  break;
3067
3267
  }
3068
3268
  setTimeout(() => {
3069
3269
  view.focus();
3270
+ view.dispatch({
3271
+ selection: view.state.selection
3272
+ });
3070
3273
  });
3071
3274
  };
3072
3275
  };
3073
3276
 
3074
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3277
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
3075
3278
  import { useState as useState2 } from "react";
3076
- var useTextEditor = () => {
3279
+ var useEditorView = () => {
3077
3280
  const [view, setView] = useState2(null);
3078
3281
  return [
3079
3282
  (ref) => {
@@ -3083,18 +3286,125 @@ var useTextEditor = () => {
3083
3286
  ];
3084
3287
  };
3085
3288
 
3289
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3290
+ import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
3291
+ import { EditorView as EditorView14 } from "@codemirror/view";
3292
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
3293
+ import { log as log4 } from "@dxos/log";
3294
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
3295
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
3296
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
3297
+ const onUpdate = useRef2();
3298
+ const [view, setView] = useState3();
3299
+ const parentRef = useRef2(null);
3300
+ useEffect3(() => {
3301
+ if (parentRef.current) {
3302
+ const state = EditorState3.create({
3303
+ doc,
3304
+ selection,
3305
+ extensions: [
3306
+ // TODO(burdon): Doesn't catch errors in keymap functions.
3307
+ EditorView14.exceptionSink.of((err) => {
3308
+ log4.catch(err, void 0, {
3309
+ F: __dxlog_file5,
3310
+ L: 57,
3311
+ S: void 0,
3312
+ C: (f, a) => f(...a)
3313
+ });
3314
+ }),
3315
+ extensions,
3316
+ EditorView14.updateListener.of(() => {
3317
+ onUpdate.current?.();
3318
+ })
3319
+ ].filter(isNotFalsy4)
3320
+ });
3321
+ const view2 = new EditorView14({
3322
+ parent: parentRef.current,
3323
+ scrollTo,
3324
+ state,
3325
+ // NOTE: Uncomment to debug/monitor all transactions.
3326
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
3327
+ dispatchTransactions: (trs, view3) => {
3328
+ if (debug) {
3329
+ logChanges(trs);
3330
+ }
3331
+ view3.update(trs);
3332
+ }
3333
+ });
3334
+ setView(view2);
3335
+ }
3336
+ return () => {
3337
+ view?.destroy();
3338
+ };
3339
+ }, [
3340
+ parentRef
3341
+ ]);
3342
+ useEffect3(() => {
3343
+ if (view) {
3344
+ if (!selection && !view.state.selection.main.anchor) {
3345
+ selection = EditorSelection.single(view.state.doc.line(1).to);
3346
+ }
3347
+ if (selection || scrollTo) {
3348
+ onUpdate.current = () => {
3349
+ onUpdate.current = void 0;
3350
+ view.dispatch({
3351
+ selection,
3352
+ effects: scrollTo && [
3353
+ scrollTo
3354
+ ],
3355
+ scrollIntoView: !scrollTo
3356
+ });
3357
+ };
3358
+ }
3359
+ if (autoFocus) {
3360
+ view.focus();
3361
+ }
3362
+ }
3363
+ }, [
3364
+ view,
3365
+ autoFocus,
3366
+ selection,
3367
+ scrollTo
3368
+ ]);
3369
+ return {
3370
+ parentRef,
3371
+ view
3372
+ };
3373
+ };
3374
+ var createDataExtensions = ({ readonly = false } = {}) => {
3375
+ return [
3376
+ //
3377
+ EditorState3.readOnly.of(readonly),
3378
+ EditorView14.editable.of(!readonly)
3379
+ ];
3380
+ };
3381
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3382
+ return [
3383
+ //
3384
+ EditorView14.baseTheme(defaultTheme),
3385
+ theme && EditorView14.theme(theme),
3386
+ EditorView14.darkTheme.of(themeMode === "dark"),
3387
+ EditorView14.editorAttributes.of({
3388
+ class: slots?.editor?.className ?? ""
3389
+ }),
3390
+ EditorView14.contentAttributes.of({
3391
+ class: slots?.content?.className ?? ""
3392
+ })
3393
+ ].filter(isNotFalsy4);
3394
+ };
3395
+
3086
3396
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
3087
3397
  import get4 from "lodash.get";
3088
- import { useEffect as useEffect3, useState as useState3 } from "react";
3398
+ import { useEffect as useEffect4, useState as useState4 } from "react";
3089
3399
  import { generateName } from "@dxos/display-name";
3090
3400
  import { getRawDoc } from "@dxos/echo-schema";
3091
3401
  import { invariant as invariant3 } from "@dxos/invariant";
3092
3402
  import { isAutomergeObject } from "@dxos/react-client/echo";
3093
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
3403
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
3094
3404
  var useTextModel = (props) => {
3095
3405
  const { identity, space, text } = props;
3096
- const [model, setModel] = useState3();
3097
- useEffect3(() => setModel(createModel(props)), [
3406
+ const [model, setModel] = useState4();
3407
+ useEffect4(() => setModel(createModel(props)), [
3098
3408
  identity,
3099
3409
  space,
3100
3410
  text
@@ -3103,7 +3413,7 @@ var useTextModel = (props) => {
3103
3413
  };
3104
3414
  var createModel = ({ space, identity, text }) => {
3105
3415
  invariant3(isAutomergeObject(text), void 0, {
3106
- F: __dxlog_file5,
3416
+ F: __dxlog_file6,
3107
3417
  L: 34,
3108
3418
  S: void 0,
3109
3419
  A: [
@@ -3158,33 +3468,39 @@ export {
3158
3468
  SpaceAwarenessProvider,
3159
3469
  TextEditor,
3160
3470
  TextKind,
3471
+ Toolbar,
3161
3472
  YText,
3162
3473
  YXmlFragment,
3163
3474
  autocomplete,
3164
3475
  automerge,
3165
3476
  awareness,
3166
3477
  awarenessProvider,
3167
- basicBundle,
3168
3478
  blast,
3169
3479
  callbackWrapper,
3170
3480
  code2 as code,
3171
3481
  codeTheme,
3172
3482
  comments,
3483
+ createBasicBundle,
3173
3484
  createComment,
3485
+ createDataExtensions,
3486
+ createMarkdownExtensions,
3487
+ createThemeExtensions,
3174
3488
  defaultMarkdownSlots,
3175
3489
  defaultOptions,
3176
3490
  defaultSlots,
3177
3491
  defaultTextSlots,
3178
3492
  defaultTheme,
3493
+ focusComment,
3179
3494
  formatting,
3180
3495
  getToken,
3181
3496
  heading2 as heading,
3182
3497
  hr,
3183
3498
  image,
3499
+ insertCodeblock,
3500
+ insertTable,
3184
3501
  link,
3185
3502
  listener,
3186
3503
  logChanges,
3187
- markdownBundle,
3188
3504
  markdownHighlightStyle,
3189
3505
  markdownTags,
3190
3506
  markdownTagsExtensions,
@@ -3193,7 +3509,6 @@ export {
3193
3509
  modelState,
3194
3510
  outliner,
3195
3511
  setComments,
3196
- setFocus,
3197
3512
  setHeading,
3198
3513
  setSelection,
3199
3514
  table,
@@ -3208,7 +3523,9 @@ export {
3208
3523
  typewriter,
3209
3524
  useActionHandler,
3210
3525
  useComments,
3526
+ useEditorView,
3211
3527
  useTextEditor,
3212
- useTextModel
3528
+ useTextModel,
3529
+ useToolbarContext
3213
3530
  };
3214
3531
  //# sourceMappingURL=index.mjs.map