@dxos/react-ui-editor 0.4.7-main.85d9ee7 → 0.4.7-main.aa6dc1f

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 (66) hide show
  1. package/dist/lib/browser/index.mjs +536 -526
  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 +3 -0
  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 +0 -9
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  10. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  11. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  13. package/dist/types/src/{components/TextEditor → extensions/automerge}/automerge.stories.d.ts +5 -2
  14. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -0
  15. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  16. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  17. package/dist/types/src/extensions/command/state.d.ts +1 -1
  18. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  19. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +1 -1
  20. package/dist/types/src/extensions/index.d.ts +0 -1
  21. package/dist/types/src/extensions/index.d.ts.map +1 -1
  22. package/dist/types/src/extensions/listener.d.ts +1 -1
  23. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  24. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  25. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  26. package/dist/types/src/extensions/markdown/decorate.d.ts +3 -0
  27. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  28. package/dist/types/src/hooks/defs.d.ts +5 -11
  29. package/dist/types/src/hooks/defs.d.ts.map +1 -1
  30. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -1
  31. package/dist/types/src/hooks/useTextEditor.d.ts +20 -5
  32. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  33. package/dist/types/src/{components/TextEditor/hooks.stories.d.ts → hooks/useTextEditor.stories.d.ts} +2 -3
  34. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -0
  35. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  36. package/dist/types/src/index.d.ts +1 -0
  37. package/dist/types/src/index.d.ts.map +1 -1
  38. package/dist/types/src/themes/default.d.ts.map +1 -1
  39. package/package.json +31 -31
  40. package/src/components/TextEditor/MarkdownEditor.stories.tsx +25 -6
  41. package/src/components/TextEditor/TextEditor.stories.tsx +0 -15
  42. package/src/components/TextEditor/TextEditor.tsx +11 -6
  43. package/src/components/TextEditor/index.ts +0 -3
  44. package/src/components/Toolbar/Toolbar.stories.tsx +2 -1
  45. package/src/{components/TextEditor → extensions/automerge}/automerge.stories.tsx +32 -37
  46. package/src/extensions/command/command.ts +4 -7
  47. package/src/extensions/command/hint.ts +1 -0
  48. package/src/extensions/command/state.ts +2 -2
  49. package/src/extensions/cursor-line-margin.ts +2 -1
  50. package/src/extensions/index.ts +0 -1
  51. package/src/extensions/listener.ts +3 -3
  52. package/src/extensions/markdown/bundle.ts +4 -14
  53. package/src/extensions/markdown/decorate.ts +20 -13
  54. package/src/hooks/defs.ts +6 -20
  55. package/src/hooks/useActionHandler.ts +11 -11
  56. package/src/hooks/useEditorView.ts +0 -1
  57. package/src/{components/TextEditor/hooks.stories.tsx → hooks/useTextEditor.stories.tsx} +8 -8
  58. package/src/hooks/useTextEditor.ts +69 -14
  59. package/src/hooks/useTextModel.ts +6 -13
  60. package/src/index.ts +2 -0
  61. package/src/themes/default.ts +9 -0
  62. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +0 -1
  63. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +0 -1
  64. package/dist/types/src/extensions/basic.d.ts +0 -9
  65. package/dist/types/src/extensions/basic.d.ts.map +0 -1
  66. package/src/extensions/basic.ts +0 -41
@@ -24,19 +24,18 @@ var translations_default = [
24
24
 
25
25
  // packages/ui/react-ui-editor/src/index.ts
26
26
  import { keymap as keymap9 } from "@codemirror/view";
27
+ import { tags as tags2 } from "@lezer/highlight";
27
28
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
28
29
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
29
30
 
30
- // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
31
- import { tags as tags2 } from "@lezer/highlight";
32
-
33
31
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
34
- import { EditorState as EditorState2 } from "@codemirror/state";
35
- import { EditorView as EditorView17, scrollPastEnd } from "@codemirror/view";
32
+ import { EditorState as EditorState3 } from "@codemirror/state";
33
+ import { EditorView as EditorView17, scrollPastEnd as scrollPastEnd2 } from "@codemirror/view";
36
34
  import { useFocusableGroup } from "@fluentui/react-tabster";
37
- import defaultsDeep2 from "lodash.defaultsdeep";
38
- import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2, useMemo as useMemo2 } from "react";
39
- import { log as log3 } from "@dxos/log";
35
+ import defaultsDeep3 from "lodash.defaultsdeep";
36
+ import React, { forwardRef, useCallback, useEffect as useEffect3, useImperativeHandle, useRef as useRef3, useState as useState5, useMemo as useMemo3 } from "react";
37
+ import { DocAccessor } from "@dxos/echo-schema";
38
+ import { log as log5 } from "@dxos/log";
40
39
  import { useThemeContext } from "@dxos/react-ui";
41
40
  import { focusRing } from "@dxos/react-ui-theme";
42
41
  import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
@@ -733,28 +732,8 @@ var styles2 = EditorView3.baseTheme({
733
732
  }
734
733
  });
735
734
 
736
- // packages/ui/react-ui-editor/src/extensions/basic.ts
737
- import { closeBrackets } from "@codemirror/autocomplete";
738
- import { history } from "@codemirror/commands";
739
- import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
740
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
741
- import { drawSelection, EditorView as EditorView4, highlightActiveLine, placeholder } from "@codemirror/view";
742
- import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
743
- var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true, bracketMatching: _bracketMatching = true } = {}) => [
744
- lineWrapping && EditorView4.lineWrapping,
745
- // https://codemirror.net/docs/ref/#codemirror.minimalSetup
746
- _bracketMatching && bracketMatching(),
747
- closeBrackets(),
748
- drawSelection(),
749
- highlightActiveLine(),
750
- history(),
751
- _placeholder && placeholder(_placeholder),
752
- // https://github.com/codemirror/theme-one-dark
753
- themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
754
- ].filter(isNotFalsy2);
755
-
756
735
  // packages/ui/react-ui-editor/src/extensions/blast.ts
757
- import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
736
+ import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
758
737
  import defaultsDeep from "lodash.defaultsdeep";
759
738
  import { invariant } from "@dxos/invariant";
760
739
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
@@ -802,7 +781,7 @@ var blast = (options = defaultOptions) => {
802
781
  };
803
782
  return [
804
783
  // Cursor moved.
805
- EditorView5.updateListener.of((update2) => {
784
+ EditorView4.updateListener.of((update2) => {
806
785
  if (blaster?.node !== update2.view.scrollDOM) {
807
786
  if (blaster) {
808
787
  blaster.destroy();
@@ -1064,11 +1043,11 @@ var random = (min, max) => {
1064
1043
  };
1065
1044
 
1066
1045
  // packages/ui/react-ui-editor/src/extensions/command/command.ts
1067
- import { EditorView as EditorView7, keymap as keymap3 } from "@codemirror/view";
1046
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1068
1047
 
1069
1048
  // packages/ui/react-ui-editor/src/extensions/command/hint.ts
1070
1049
  import { RangeSetBuilder } from "@codemirror/state";
1071
- import { Decoration as Decoration3, EditorView as EditorView6, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1050
+ import { Decoration as Decoration3, EditorView as EditorView5, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1072
1051
 
1073
1052
  // packages/ui/react-ui-editor/src/extensions/command/state.ts
1074
1053
  import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField3 } from "@codemirror/state";
@@ -1162,7 +1141,7 @@ var closeCommand = (view) => {
1162
1141
  }
1163
1142
  return false;
1164
1143
  };
1165
- var commandKeymap = [
1144
+ var commandKeyBindings = [
1166
1145
  {
1167
1146
  key: "/",
1168
1147
  run: openCommand
@@ -1263,28 +1242,27 @@ var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
1263
1242
  }
1264
1243
  }, {
1265
1244
  provide: (plugin) => [
1266
- EditorView6.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
1245
+ EditorView5.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
1267
1246
  ]
1268
1247
  });
1269
1248
 
1270
1249
  // packages/ui/react-ui-editor/src/extensions/command/command.ts
1271
1250
  var command = (options) => {
1272
1251
  return [
1252
+ commandConfig.of(options),
1273
1253
  commandState,
1274
- keymap3.of(commandKeymap),
1254
+ keymap3.of(commandKeyBindings),
1275
1255
  hintViewPlugin(options),
1276
- // Close tooltip on focus change.
1277
- EditorView7.focusChangeEffect.of((_, focusing) => {
1256
+ EditorView6.focusChangeEffect.of((_, focusing) => {
1278
1257
  return focusing ? closeEffect.of(null) : null;
1279
- }),
1280
- commandConfig.of(options)
1258
+ })
1281
1259
  ];
1282
1260
  };
1283
1261
 
1284
1262
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1285
1263
  import { invertedEffects } from "@codemirror/commands";
1286
1264
  import { Facet as Facet4, StateEffect as StateEffect3, StateField as StateField4 } from "@codemirror/state";
1287
- import { hoverTooltip, keymap as keymap4, Decoration as Decoration4, EditorView as EditorView8 } from "@codemirror/view";
1265
+ import { hoverTooltip, keymap as keymap4, Decoration as Decoration4, EditorView as EditorView7 } from "@codemirror/view";
1288
1266
  import sortBy from "lodash.sortby";
1289
1267
  import { useEffect } from "react";
1290
1268
  import { debounce } from "@dxos/async";
@@ -1404,7 +1382,7 @@ var commentsState = StateField4.define({
1404
1382
  return value;
1405
1383
  }
1406
1384
  });
1407
- var styles3 = EditorView8.baseTheme({
1385
+ var styles3 = EditorView7.baseTheme({
1408
1386
  "&light .cm-comment, &light .cm-comment-current": {
1409
1387
  mixBlendMode: "darken"
1410
1388
  },
@@ -1438,7 +1416,7 @@ var commentCurrentMark = Decoration4.mark({
1438
1416
  "data-testid": "cm-comment"
1439
1417
  }
1440
1418
  });
1441
- var commentsDecorations = EditorView8.decorations.compute([
1419
+ var commentsDecorations = EditorView7.decorations.compute([
1442
1420
  commentsState
1443
1421
  ], (state) => {
1444
1422
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -1476,7 +1454,7 @@ var trackPastedComments = (onUpdate) => {
1476
1454
  }
1477
1455
  };
1478
1456
  return [
1479
- EditorView8.domEventHandlers({
1457
+ EditorView7.domEventHandlers({
1480
1458
  cut: handleTrack,
1481
1459
  copy: handleTrack
1482
1460
  }),
@@ -1498,7 +1476,7 @@ var trackPastedComments = (onUpdate) => {
1498
1476
  return effects;
1499
1477
  }),
1500
1478
  // Handle paste or the undo of comment deletion.
1501
- EditorView8.updateListener.of((update2) => {
1479
+ EditorView7.updateListener.of((update2) => {
1502
1480
  const restore = [];
1503
1481
  for (let i = 0; i < update2.transactions.length; i++) {
1504
1482
  const tr = update2.transactions[i];
@@ -1649,7 +1627,7 @@ var comments = (options = {}) => {
1649
1627
  //
1650
1628
  // Track deleted ranges and update ranges for decorations.
1651
1629
  //
1652
- EditorView8.updateListener.of(({ view, state, changes }) => {
1630
+ EditorView7.updateListener.of(({ view, state, changes }) => {
1653
1631
  let mod = false;
1654
1632
  const { comments: comments2, ...value } = state.field(commentsState);
1655
1633
  changes.iterChanges((from, to, from2, to2) => {
@@ -1681,7 +1659,7 @@ var comments = (options = {}) => {
1681
1659
  //
1682
1660
  // Track selection/proximity.
1683
1661
  //
1684
- EditorView8.updateListener.of(({ view, state }) => {
1662
+ EditorView7.updateListener.of(({ view, state }) => {
1685
1663
  let min = Infinity;
1686
1664
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
1687
1665
  const { head } = state.selection.main;
@@ -1730,7 +1708,7 @@ var focusComment = (view, id, center = true) => {
1730
1708
  },
1731
1709
  effects: [
1732
1710
  //
1733
- EditorView8.scrollIntoView(range.from, center ? {
1711
+ EditorView7.scrollIntoView(range.from, center ? {
1734
1712
  y: "center"
1735
1713
  } : void 0),
1736
1714
  setSelection.of({
@@ -1759,13 +1737,13 @@ var useComments = (view, id, comments2 = []) => {
1759
1737
 
1760
1738
  // packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts
1761
1739
  import { Facet as Facet5, Transaction } from "@codemirror/state";
1762
- import { EditorView as EditorView9 } from "@codemirror/view";
1740
+ import { EditorView as EditorView8 } from "@codemirror/view";
1763
1741
  var cursorLineMarginFacet = Facet5.define({
1764
1742
  combine: (input) => input[0] ?? 3
1765
1743
  });
1766
1744
  var annotation = "cursorLineMargin";
1767
1745
  var lineAtPos = (s, pos) => s.doc.lineAt(pos).number;
1768
- var cursorLineMargin = EditorView9.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1746
+ var cursorLineMargin = EditorView8.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1769
1747
  if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1770
1748
  return;
1771
1749
  }
@@ -1801,7 +1779,7 @@ var cursorLineMargin = EditorView9.updateListener.of(({ transactions, state, sel
1801
1779
  }
1802
1780
  view.dispatch({
1803
1781
  annotations: Transaction.userEvent.of(annotation),
1804
- effects: EditorView9.scrollIntoView(s.selection.main.head, {
1782
+ effects: EditorView8.scrollIntoView(s.selection.main.head, {
1805
1783
  y: needsScrollTop ? "start" : "end",
1806
1784
  yMargin: view.defaultLineHeight * cursorLineMargin2
1807
1785
  })
@@ -1809,9 +1787,9 @@ var cursorLineMargin = EditorView9.updateListener.of(({ transactions, state, sel
1809
1787
  });
1810
1788
 
1811
1789
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
1812
- import { EditorView as EditorView10 } from "@codemirror/view";
1790
+ import { EditorView as EditorView9 } from "@codemirror/view";
1813
1791
  var dnd = () => {
1814
- return EditorView10.domEventHandlers({
1792
+ return EditorView9.domEventHandlers({
1815
1793
  drop: (event, view) => {
1816
1794
  console.log(event);
1817
1795
  }
@@ -1819,30 +1797,30 @@ var dnd = () => {
1819
1797
  };
1820
1798
 
1821
1799
  // packages/ui/react-ui-editor/src/extensions/listener.ts
1822
- import { EditorView as EditorView11 } from "@codemirror/view";
1800
+ import { EditorView as EditorView10 } from "@codemirror/view";
1823
1801
  var listener = ({ onFocus, onChange }) => {
1824
1802
  const extensions = [];
1825
- onFocus && extensions.push(EditorView11.focusChangeEffect.of((_, focused) => {
1826
- onFocus(focused);
1803
+ onFocus && extensions.push(EditorView10.focusChangeEffect.of((_, focusing) => {
1804
+ onFocus(focusing);
1827
1805
  return null;
1828
1806
  }));
1829
- onChange && extensions.push(EditorView11.updateListener.of((update2) => {
1807
+ onChange && extensions.push(EditorView10.updateListener.of((update2) => {
1830
1808
  onChange(update2.state.doc.toString());
1831
1809
  }));
1832
1810
  return extensions;
1833
1811
  };
1834
1812
 
1835
1813
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1836
- import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1837
- import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1814
+ import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
1815
+ import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1838
1816
  import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1839
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1817
+ import { bracketMatching, defaultHighlightStyle, indentOnInput, syntaxHighlighting } from "@codemirror/language";
1840
1818
  import { languages } from "@codemirror/language-data";
1841
1819
  import { searchKeymap } from "@codemirror/search";
1842
1820
  import { EditorState } from "@codemirror/state";
1843
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1844
- import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView12, highlightActiveLine as highlightActiveLine2, keymap as keymap5, placeholder as placeholder2 } from "@codemirror/view";
1845
- import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
1821
+ import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
1822
+ import { EditorView as EditorView11, drawSelection, dropCursor, keymap as keymap5, placeholder } from "@codemirror/view";
1823
+ import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
1846
1824
 
1847
1825
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1848
1826
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -2035,19 +2013,17 @@ var markdownHighlightStyle = (readonly) => {
2035
2013
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2036
2014
  var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
2037
2015
  return [
2038
- lineWrapping && EditorView12.lineWrapping,
2016
+ // TODO(burdon): Reconcile with createBasicExtensions.
2017
+ lineWrapping && EditorView11.lineWrapping,
2039
2018
  EditorState.allowMultipleSelections.of(true),
2040
2019
  EditorState.tabSize.of(2),
2041
- // https://github.com/codemirror/basic-setup
2042
- bracketMatching2(),
2043
- closeBrackets2(),
2044
- crosshairCursor(),
2020
+ bracketMatching(),
2021
+ closeBrackets(),
2045
2022
  dropCursor(),
2046
- drawSelection2(),
2047
- highlightActiveLine2(),
2048
- history2(),
2023
+ drawSelection(),
2024
+ history(),
2049
2025
  indentOnInput(),
2050
- _placeholder && placeholder2(_placeholder),
2026
+ _placeholder && placeholder(_placeholder),
2051
2027
  // Main extension.
2052
2028
  // https://github.com/codemirror/lang-markdown
2053
2029
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -2067,9 +2043,9 @@ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrap
2067
2043
  ]
2068
2044
  }),
2069
2045
  // https://github.com/codemirror/theme-one-dark
2070
- themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
2046
+ themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
2071
2047
  // Custom styles.
2072
- syntaxHighlighting2(markdownHighlightStyle()),
2048
+ syntaxHighlighting(markdownHighlightStyle()),
2073
2049
  keymap5.of([
2074
2050
  // https://codemirror.net/docs/ref/#commands.indentWithTab
2075
2051
  indentWithTab,
@@ -2082,13 +2058,13 @@ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrap
2082
2058
  // https://codemirror.net/docs/ref/#commands.standardKeymap
2083
2059
  ...standardKeymap
2084
2060
  ])
2085
- ].filter(isNotFalsy3);
2061
+ ].filter(isNotFalsy2);
2086
2062
  };
2087
2063
 
2088
2064
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
2089
2065
  import { syntaxTree } from "@codemirror/language";
2090
2066
  import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
2091
- import { EditorView as EditorView13, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2067
+ import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2092
2068
  import { mx as mx2 } from "@dxos/react-ui-theme";
2093
2069
  var HorizontalRuleWidget = class extends WidgetType3 {
2094
2070
  toDOM() {
@@ -2198,7 +2174,7 @@ var buildDecorations = (view, options, focus) => {
2198
2174
  break;
2199
2175
  }
2200
2176
  const first = block.from <= node.from;
2201
- const last = block.to >= node.to;
2177
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
2202
2178
  deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2203
2179
  if (!editing && (first || last)) {
2204
2180
  atomicDeco.add(block.from, block.to, hide);
@@ -2265,45 +2241,46 @@ var decorateMarkdown = (options = {}) => {
2265
2241
  return [
2266
2242
  ViewPlugin4.fromClass(class {
2267
2243
  constructor(view) {
2268
- this.pendingUpdate = -1;
2269
2244
  ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
2270
2245
  }
2271
2246
  update(update2) {
2272
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate)))) {
2247
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
2273
2248
  ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
2274
2249
  this.clearUpdate();
2275
2250
  } else if (update2.selectionSet) {
2276
2251
  this.scheduleUpdate(update2.view);
2277
2252
  }
2278
2253
  }
2279
- clearUpdate() {
2280
- if (this.pendingUpdate > -1) {
2281
- window.clearTimeout(this.pendingUpdate);
2282
- this.pendingUpdate = -1;
2283
- }
2284
- }
2254
+ // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
2255
+ // the cursor will float in space or be in the wrong position after the decoration is applied.
2285
2256
  scheduleUpdate(view) {
2286
2257
  this.clearUpdate();
2287
- this.pendingUpdate = window.setTimeout(() => {
2258
+ this.pendingUpdate = setTimeout(() => {
2288
2259
  view.dispatch({
2289
2260
  effects: forceUpdate.of(null)
2290
2261
  });
2291
- }, options.selectionChangeDelay ?? 400);
2262
+ }, options.selectionChangeDelay);
2263
+ }
2264
+ clearUpdate() {
2265
+ if (this.pendingUpdate) {
2266
+ clearTimeout(this.pendingUpdate);
2267
+ this.pendingUpdate = void 0;
2268
+ }
2292
2269
  }
2293
2270
  destroy() {
2294
2271
  this.clearUpdate();
2295
2272
  }
2296
2273
  }, {
2297
2274
  provide: (plugin) => [
2298
- EditorView13.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2299
- EditorView13.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none),
2300
- EditorView13.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none)
2275
+ EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2276
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2277
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
2301
2278
  ]
2302
2279
  }),
2303
2280
  formattingStyles
2304
2281
  ];
2305
2282
  };
2306
- var formattingStyles = EditorView13.baseTheme({
2283
+ var formattingStyles = EditorView12.baseTheme({
2307
2284
  "& .cm-code": {
2308
2285
  fontFamily: getToken("fontFamily.mono", []).join(",")
2309
2286
  },
@@ -2356,7 +2333,7 @@ var formattingStyles = EditorView13.baseTheme({
2356
2333
  import { snippet } from "@codemirror/autocomplete";
2357
2334
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2358
2335
  import { EditorSelection } from "@codemirror/state";
2359
- import { EditorView as EditorView14, keymap as keymap6 } from "@codemirror/view";
2336
+ import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
2360
2337
  import { useState, useMemo } from "react";
2361
2338
  var compareFormatting = (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;
2362
2339
  var Inline;
@@ -3426,7 +3403,7 @@ var getFormatting = (state) => {
3426
3403
  };
3427
3404
  var useFormattingState = () => {
3428
3405
  const [state, setState] = useState(null);
3429
- const observer = useMemo(() => EditorView14.updateListener.of((update2) => {
3406
+ const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3430
3407
  if (update2.docChanged || update2.selectionSet) {
3431
3408
  const newState = getFormatting(update2.state);
3432
3409
  if (!state || !compareFormatting(state, newState)) {
@@ -3443,7 +3420,7 @@ var useFormattingState = () => {
3443
3420
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3444
3421
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3445
3422
  import { StateField as StateField5 } from "@codemirror/state";
3446
- import { Decoration as Decoration6, EditorView as EditorView15, WidgetType as WidgetType4 } from "@codemirror/view";
3423
+ import { Decoration as Decoration6, EditorView as EditorView14, WidgetType as WidgetType4 } from "@codemirror/view";
3447
3424
  var image = (options = {}) => {
3448
3425
  return StateField5.define({
3449
3426
  create: (state) => {
@@ -3470,7 +3447,7 @@ var image = (options = {}) => {
3470
3447
  add: buildDecorations2(from, to, tr.state)
3471
3448
  });
3472
3449
  },
3473
- provide: (field) => EditorView15.decorations.from(field)
3450
+ provide: (field) => EditorView14.decorations.from(field)
3474
3451
  });
3475
3452
  };
3476
3453
  var preloaded = /* @__PURE__ */ new Set();
@@ -3558,12 +3535,12 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
3558
3535
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
3559
3536
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3560
3537
  import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
3561
- import { Decoration as Decoration7, EditorView as EditorView16, WidgetType as WidgetType5 } from "@codemirror/view";
3538
+ import { Decoration as Decoration7, EditorView as EditorView15, WidgetType as WidgetType5 } from "@codemirror/view";
3562
3539
  var table = (options = {}) => {
3563
3540
  return StateField6.define({
3564
3541
  create: (state) => update(state, options),
3565
3542
  update: (_, tr) => update(tr.state, options),
3566
- provide: (field) => EditorView16.decorations.from(field)
3543
+ provide: (field) => EditorView15.decorations.from(field)
3567
3544
  });
3568
3545
  };
3569
3546
  var update = (state, options) => {
@@ -3773,6 +3750,189 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
3773
3750
  ];
3774
3751
  };
3775
3752
 
3753
+ // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
3754
+ import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
3755
+ import { Context as Context2 } from "@dxos/context";
3756
+ import { invariant as invariant2 } from "@dxos/invariant";
3757
+ import { log as log3 } from "@dxos/log";
3758
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
3759
+ var DEBOUNCE_INTERVAL = 100;
3760
+ var SpaceAwarenessProvider = class {
3761
+ constructor(params) {
3762
+ this._remoteStates = /* @__PURE__ */ new Map();
3763
+ this.remoteStateChange = new Event2();
3764
+ this._space = params.space;
3765
+ this._channel = params.channel;
3766
+ this._peerId = params.peerId;
3767
+ this._info = params.info;
3768
+ }
3769
+ open() {
3770
+ this._ctx = new Context2();
3771
+ this._postTask = new DeferredTask(this._ctx, async () => {
3772
+ if (this._localState) {
3773
+ await this._space.postMessage(this._channel, {
3774
+ kind: "post",
3775
+ state: this._localState
3776
+ });
3777
+ await sleep(DEBOUNCE_INTERVAL);
3778
+ }
3779
+ });
3780
+ this._ctx.onDispose(this._space.listen(this._channel, (message) => {
3781
+ switch (message.payload.kind) {
3782
+ case "query": {
3783
+ this._handleQueryMessage();
3784
+ break;
3785
+ }
3786
+ case "post": {
3787
+ this._handlePostMessage(message.payload);
3788
+ break;
3789
+ }
3790
+ }
3791
+ }));
3792
+ void this._space.postMessage(this._channel, {
3793
+ kind: "query"
3794
+ }).catch((err) => {
3795
+ log3.debug("failed to query awareness", {
3796
+ err
3797
+ }, {
3798
+ F: __dxlog_file4,
3799
+ L: 89,
3800
+ S: this,
3801
+ C: (f, a) => f(...a)
3802
+ });
3803
+ });
3804
+ }
3805
+ close() {
3806
+ void this._ctx?.dispose();
3807
+ this._ctx = void 0;
3808
+ this._postTask = void 0;
3809
+ }
3810
+ getRemoteStates() {
3811
+ return Array.from(this._remoteStates.values());
3812
+ }
3813
+ update(position) {
3814
+ invariant2(this._postTask, void 0, {
3815
+ F: __dxlog_file4,
3816
+ L: 104,
3817
+ S: this,
3818
+ A: [
3819
+ "this._postTask",
3820
+ ""
3821
+ ]
3822
+ });
3823
+ this._localState = {
3824
+ peerId: this._peerId,
3825
+ position,
3826
+ info: this._info
3827
+ };
3828
+ this._postTask.schedule();
3829
+ }
3830
+ _handleQueryMessage() {
3831
+ invariant2(this._postTask, void 0, {
3832
+ F: __dxlog_file4,
3833
+ L: 115,
3834
+ S: this,
3835
+ A: [
3836
+ "this._postTask",
3837
+ ""
3838
+ ]
3839
+ });
3840
+ this._postTask.schedule();
3841
+ }
3842
+ _handlePostMessage(message) {
3843
+ invariant2(message.kind === "post", void 0, {
3844
+ F: __dxlog_file4,
3845
+ L: 120,
3846
+ S: this,
3847
+ A: [
3848
+ "message.kind === 'post'",
3849
+ ""
3850
+ ]
3851
+ });
3852
+ this._remoteStates.set(message.state.peerId, message.state);
3853
+ this.remoteStateChange.emit();
3854
+ }
3855
+ };
3856
+
3857
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3858
+ var useActionHandler = (view) => {
3859
+ return (action) => {
3860
+ if (!view) {
3861
+ return;
3862
+ }
3863
+ let inlineType, listType;
3864
+ switch (action.type) {
3865
+ case "heading":
3866
+ setHeading(parseInt(action.data))(view);
3867
+ break;
3868
+ case "strong":
3869
+ case "emphasis":
3870
+ case "strikethrough":
3871
+ case "code":
3872
+ inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
3873
+ (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
3874
+ break;
3875
+ case "list-ordered":
3876
+ case "list-bullet":
3877
+ case "list-task":
3878
+ listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
3879
+ (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
3880
+ break;
3881
+ case "blockquote":
3882
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
3883
+ break;
3884
+ case "codeblock":
3885
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
3886
+ break;
3887
+ case "table":
3888
+ insertTable(view);
3889
+ break;
3890
+ case "link":
3891
+ (action.data === false ? removeLink : addLink)(view);
3892
+ break;
3893
+ case "comment":
3894
+ createComment(view);
3895
+ break;
3896
+ }
3897
+ setTimeout(() => {
3898
+ if (!view.hasFocus) {
3899
+ view.focus();
3900
+ }
3901
+ });
3902
+ };
3903
+ };
3904
+
3905
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
3906
+ import { useRef, useState as useState2 } from "react";
3907
+ var useEditorView = (id) => {
3908
+ const editorRef = useRef(null);
3909
+ const [prev, setPrev] = useState2([
3910
+ null,
3911
+ ""
3912
+ ]);
3913
+ if (prev[0] !== editorRef.current) {
3914
+ setPrev([
3915
+ editorRef.current,
3916
+ id
3917
+ ]);
3918
+ }
3919
+ return [
3920
+ editorRef,
3921
+ prev[1] !== id
3922
+ ];
3923
+ };
3924
+
3925
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3926
+ import { closeBrackets as closeBrackets2 } from "@codemirror/autocomplete";
3927
+ import { history as history2 } from "@codemirror/commands";
3928
+ import { bracketMatching as bracketMatching2 } from "@codemirror/language";
3929
+ import { EditorSelection as EditorSelection2, EditorState as EditorState2 } from "@codemirror/state";
3930
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor as dropCursor2, EditorView as EditorView16, highlightActiveLine, lineNumbers, placeholder as placeholder2, scrollPastEnd } from "@codemirror/view";
3931
+ import defaultsDeep2 from "lodash.defaultsdeep";
3932
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
3933
+ import { log as log4 } from "@dxos/log";
3934
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
3935
+
3776
3936
  // packages/ui/react-ui-editor/src/themes/default.ts
3777
3937
  import get4 from "lodash.get";
3778
3938
  var defaultTheme = {
@@ -3896,6 +4056,15 @@ var defaultTheme = {
3896
4056
  ".cm-tooltip": {
3897
4057
  border: "none"
3898
4058
  },
4059
+ "&light .cm-tooltip": {
4060
+ background: get4(tokens, "extend.colors.white")
4061
+ },
4062
+ "&dark .cm-tooltip": {
4063
+ background: get4(tokens, "extend.colors.black")
4064
+ },
4065
+ ".cm-tooltip-hover": {
4066
+ padding: "0 4px"
4067
+ },
3899
4068
  ".cm-tooltip-below": {},
3900
4069
  //
3901
4070
  // autocomplete
@@ -4023,43 +4192,262 @@ var codeTheme = {
4023
4192
  }
4024
4193
  };
4025
4194
 
4026
- // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4027
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4028
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo = EditorView17.scrollIntoView(0), moveToEndOfLine, selection, theme, slots = defaultSlots, extensions = [], scrollPastEnd: _scrollPastEnd, debug }, forwardedRef) => {
4029
- const tabsterDOMAttribute = useFocusableGroup({
4030
- tabBehavior: "limited"
4031
- });
4032
- const { themeMode } = useThemeContext();
4033
- const rootRef = useRef(null);
4034
- const [view, setView] = useState2(null);
4035
- useImperativeHandle(forwardedRef, () => view, [
4036
- view
4037
- ]);
4195
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4196
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4197
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}, deps = []) => {
4198
+ const onUpdate = useRef2();
4199
+ const [view, setView] = useState3();
4200
+ const parentRef = useRef2(null);
4038
4201
  useEffect2(() => {
4039
- if (autoFocus) {
4040
- view?.focus();
4202
+ let view2;
4203
+ if (parentRef.current) {
4204
+ const state = EditorState2.create({
4205
+ doc,
4206
+ selection,
4207
+ extensions: [
4208
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4209
+ EditorView16.exceptionSink.of((err) => {
4210
+ log4.catch(err, void 0, {
4211
+ F: __dxlog_file5,
4212
+ L: 70,
4213
+ S: void 0,
4214
+ C: (f, a) => f(...a)
4215
+ });
4216
+ }),
4217
+ extensions,
4218
+ EditorView16.updateListener.of(() => {
4219
+ onUpdate.current?.();
4220
+ })
4221
+ ].filter(isNotFalsy3)
4222
+ });
4223
+ view2 = new EditorView16({
4224
+ parent: parentRef.current,
4225
+ scrollTo,
4226
+ state,
4227
+ // NOTE: Uncomment to debug/monitor all transactions.
4228
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4229
+ dispatchTransactions: (trs, view3) => {
4230
+ if (debug) {
4231
+ logChanges(trs);
4232
+ }
4233
+ view3.update(trs);
4234
+ }
4235
+ });
4236
+ setView(view2);
4041
4237
  }
4042
- }, [
4043
- view,
4044
- autoFocus
4045
- ]);
4238
+ return () => {
4239
+ view2?.destroy();
4240
+ };
4241
+ }, deps);
4046
4242
  useEffect2(() => {
4047
- if (!model || !rootRef.current) {
4048
- return;
4049
- }
4050
- const state = EditorState2.create({
4051
- doc: model.text(),
4052
- selection,
4053
- extensions: [
4054
- // TODO(burdon): Doesn't catch errors in keymap functions.
4055
- EditorView17.exceptionSink.of((err) => {
4056
- log3.catch(err, void 0, {
4057
- F: __dxlog_file4,
4058
- L: 122,
4059
- S: void 0,
4060
- C: (f, a) => f(...a)
4061
- });
4062
- }),
4243
+ if (view) {
4244
+ if (!selection && !view.state.selection.main.anchor) {
4245
+ selection = EditorSelection2.single(view.state.doc.line(1).to);
4246
+ }
4247
+ if (selection || scrollTo) {
4248
+ onUpdate.current = () => {
4249
+ onUpdate.current = void 0;
4250
+ view.dispatch({
4251
+ selection,
4252
+ effects: scrollTo && [
4253
+ scrollTo
4254
+ ],
4255
+ scrollIntoView: !scrollTo
4256
+ });
4257
+ };
4258
+ }
4259
+ if (autoFocus) {
4260
+ view.focus();
4261
+ }
4262
+ }
4263
+ }, [
4264
+ view,
4265
+ autoFocus,
4266
+ selection,
4267
+ scrollTo
4268
+ ]);
4269
+ return {
4270
+ parentRef,
4271
+ view
4272
+ };
4273
+ };
4274
+ var defaults = {
4275
+ bracketMatching: true,
4276
+ closeBrackets: true,
4277
+ drawSelection: true,
4278
+ editable: true,
4279
+ history: true,
4280
+ lineWrapping: true
4281
+ };
4282
+ var createBasicExtensions = (_props) => {
4283
+ const props = defaultsDeep2({}, _props, defaults);
4284
+ return [
4285
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4286
+ EditorView16.exceptionSink.of((err) => {
4287
+ log4.catch(err, void 0, {
4288
+ F: __dxlog_file5,
4289
+ L: 167,
4290
+ S: void 0,
4291
+ C: (f, a) => f(...a)
4292
+ });
4293
+ }),
4294
+ props.allowMultipleSelections && EditorState2.allowMultipleSelections.of(true),
4295
+ props.bracketMatching && bracketMatching2(),
4296
+ props.closeBrackets && closeBrackets2(),
4297
+ props.crosshairCursor && crosshairCursor(),
4298
+ props.dropCursor && dropCursor2(),
4299
+ props.drawSelection && drawSelection2(),
4300
+ props.highlightActiveLine && highlightActiveLine(),
4301
+ props.history && history2(),
4302
+ props.lineNumbers && lineNumbers(),
4303
+ props.lineWrapping && EditorView16.lineWrapping,
4304
+ props.placeholder && placeholder2(props.placeholder),
4305
+ props.readonly && [
4306
+ EditorState2.readOnly.of(true),
4307
+ EditorView16.editable.of(false)
4308
+ ],
4309
+ props.scrollPastEnd && scrollPastEnd(),
4310
+ props.tabSize && EditorState2.tabSize.of(props.tabSize)
4311
+ ].filter(isNotFalsy3);
4312
+ };
4313
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4314
+ return [
4315
+ EditorView16.baseTheme(defaultTheme),
4316
+ EditorView16.darkTheme.of(themeMode === "dark"),
4317
+ theme && EditorView16.theme(theme),
4318
+ slots?.editor?.className && EditorView16.editorAttributes.of({
4319
+ class: slots.editor.className
4320
+ }),
4321
+ slots?.content?.className && EditorView16.contentAttributes.of({
4322
+ class: slots.content.className
4323
+ })
4324
+ ].filter(isNotFalsy3);
4325
+ };
4326
+
4327
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
4328
+ import get5 from "lodash.get";
4329
+ import { useState as useState4, useMemo as useMemo2 } from "react";
4330
+ import { generateName } from "@dxos/display-name";
4331
+ import { getRawDoc } from "@dxos/echo-schema";
4332
+ import { invariant as invariant3 } from "@dxos/invariant";
4333
+ import { isAutomergeObject } from "@dxos/react-client/echo";
4334
+ import { hueTokens } from "@dxos/react-ui-theme";
4335
+ import { hexToHue } from "@dxos/util";
4336
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
4337
+ var useTextExtensions = ({ id, text, space, identity }) => {
4338
+ const extensions = [
4339
+ automerge(text)
4340
+ ];
4341
+ const peerId = identity?.identityKey.toHex();
4342
+ const { cursorLightValue, cursorDarkValue } = hueTokens[hexToHue(peerId ?? "0")];
4343
+ if (space && identity) {
4344
+ const awarenessProvider2 = new SpaceAwarenessProvider({
4345
+ space,
4346
+ channel: `awareness.${id}`,
4347
+ peerId: identity.identityKey.toHex(),
4348
+ info: {
4349
+ displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
4350
+ color: cursorDarkValue,
4351
+ lightColor: cursorLightValue
4352
+ }
4353
+ });
4354
+ extensions.push(awareness(awarenessProvider2));
4355
+ }
4356
+ return extensions;
4357
+ };
4358
+ var useTextModel = (props) => useMemo2(() => createModel(props), Object.values(props));
4359
+ var useInMemoryTextModel = ({ id, defaultContent }) => {
4360
+ const [content, setContent] = useState4(defaultContent ?? "");
4361
+ return {
4362
+ id,
4363
+ content,
4364
+ text: () => content,
4365
+ setContent
4366
+ };
4367
+ };
4368
+ var createModel = ({ space, identity, text }) => {
4369
+ if (!text) {
4370
+ return void 0;
4371
+ }
4372
+ invariant3(isAutomergeObject(text), void 0, {
4373
+ F: __dxlog_file6,
4374
+ L: 88,
4375
+ S: void 0,
4376
+ A: [
4377
+ "isAutomergeObject(text)",
4378
+ ""
4379
+ ]
4380
+ });
4381
+ const obj = text;
4382
+ const doc = getRawDoc(obj, [
4383
+ obj.field
4384
+ ]);
4385
+ const peerId = identity?.identityKey.toHex();
4386
+ const { cursorLightValue, cursorDarkValue } = hueTokens[hexToHue(peerId ?? "0")];
4387
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
4388
+ space,
4389
+ channel: `automerge.awareness.${obj.id}`,
4390
+ info: {
4391
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
4392
+ color: cursorLightValue,
4393
+ lightColor: cursorDarkValue
4394
+ },
4395
+ peerId: identity?.identityKey.toHex() ?? "Anonymous"
4396
+ });
4397
+ const extensions = [
4398
+ automerge({
4399
+ handle: doc.handle,
4400
+ path: doc.path
4401
+ })
4402
+ ];
4403
+ if (awarenessProvider2) {
4404
+ extensions.push(awareness(awarenessProvider2));
4405
+ }
4406
+ return {
4407
+ id: obj.id,
4408
+ content: doc,
4409
+ text: () => get5(doc.handle.docSync(), doc.path),
4410
+ extension: extensions
4411
+ };
4412
+ };
4413
+
4414
+ // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4415
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4416
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo = EditorView17.scrollIntoView(0), moveToEndOfLine, selection, theme, slots = defaultSlots, extensions = [], scrollPastEnd: _scrollPastEnd, debug }, forwardedRef) => {
4417
+ const tabsterDOMAttribute = useFocusableGroup({
4418
+ tabBehavior: "limited"
4419
+ });
4420
+ const { themeMode } = useThemeContext();
4421
+ const rootRef = useRef3(null);
4422
+ const [view, setView] = useState5(null);
4423
+ useImperativeHandle(forwardedRef, () => view, [
4424
+ view
4425
+ ]);
4426
+ useEffect3(() => {
4427
+ if (autoFocus) {
4428
+ view?.focus();
4429
+ }
4430
+ }, [
4431
+ view,
4432
+ autoFocus
4433
+ ]);
4434
+ useEffect3(() => {
4435
+ if (!model || !rootRef.current) {
4436
+ return;
4437
+ }
4438
+ const state = EditorState3.create({
4439
+ doc: typeof model.content === "string" ? model.content : DocAccessor.getValue(model.content),
4440
+ selection,
4441
+ extensions: [
4442
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4443
+ EditorView17.exceptionSink.of((err) => {
4444
+ log5.catch(err, void 0, {
4445
+ F: __dxlog_file7,
4446
+ L: 123,
4447
+ S: void 0,
4448
+ C: (f, a) => f(...a)
4449
+ });
4450
+ }),
4063
4451
  // Theme.
4064
4452
  // TODO(burdon): Make base theme configurable.
4065
4453
  EditorView17.baseTheme(defaultTheme),
@@ -4072,7 +4460,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
4072
4460
  class: slots.content.className
4073
4461
  }),
4074
4462
  // NOTE: Assumes default line height.
4075
- _scrollPastEnd && scrollPastEnd(),
4463
+ _scrollPastEnd && scrollPastEnd2(),
4076
4464
  // Focus.
4077
4465
  EditorView17.updateListener.of((update2) => {
4078
4466
  update2.transactions.forEach((transaction) => {
@@ -4083,7 +4471,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
4083
4471
  }),
4084
4472
  // State.
4085
4473
  EditorView17.editable.of(!readonly),
4086
- EditorState2.readOnly.of(!!readonly),
4474
+ EditorState3.readOnly.of(!!readonly),
4087
4475
  // Storage and replication.
4088
4476
  // NOTE: This must come before user extensions.
4089
4477
  model.extension,
@@ -4148,14 +4536,16 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
4148
4536
  ...tabsterDOMAttribute
4149
4537
  });
4150
4538
  });
4151
- var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4539
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping = true, theme = textTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4152
4540
  const { themeMode } = useThemeContext();
4153
- const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
4154
- const extensions = useMemo2(() => [
4155
- createBasicBundle({
4156
- themeMode,
4157
- placeholder: placeholder3,
4158
- lineWrapping
4541
+ const updatedSlots = defaultsDeep3({}, slots, defaultTextSlots);
4542
+ const extensions = useMemo3(() => [
4543
+ createBasicExtensions({
4544
+ lineWrapping,
4545
+ placeholder: placeholder3
4546
+ }),
4547
+ createThemeExtensions({
4548
+ themeMode
4159
4549
  }),
4160
4550
  ..._extensions ?? []
4161
4551
  ], [
@@ -4175,8 +4565,8 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
4175
4565
  });
4176
4566
  var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4177
4567
  const { themeMode } = useThemeContext();
4178
- const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
4179
- const extensions = useMemo2(() => [
4568
+ const updatedSlots = defaultsDeep3({}, slots, defaultMarkdownSlots);
4569
+ const extensions = useMemo3(() => [
4180
4570
  createMarkdownExtensions({
4181
4571
  themeMode,
4182
4572
  placeholder: placeholder3
@@ -4214,7 +4604,7 @@ var defaultMarkdownSlots = {
4214
4604
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4215
4605
  import { ChatText, Code, CodeBlock, Link, ListBullets, ListChecks, ListNumbers, Paragraph, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic, Quotes } from "@phosphor-icons/react";
4216
4606
  import { createContext } from "@radix-ui/react-context";
4217
- import React2, { useRef as useRef2, useState as useState3 } from "react";
4607
+ import React2, { useRef as useRef4, useState as useState6 } from "react";
4218
4608
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4219
4609
  import { getSize } from "@dxos/react-ui-theme";
4220
4610
  var tooltipProps = {
@@ -4272,9 +4662,9 @@ var MarkdownHeading = () => {
4272
4662
  const header = blockType && /heading(\d)/.exec(blockType);
4273
4663
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4274
4664
  const HeadingIcon = HeadingIcons[value ?? "0"];
4275
- const suppressNextTooltip = useRef2(false);
4276
- const [tooltipOpen, setTooltipOpen] = useState3(false);
4277
- const [selectOpen, setSelectOpen] = useState3(false);
4665
+ const suppressNextTooltip = useRef4(false);
4666
+ const [tooltipOpen, setTooltipOpen] = useState6(false);
4667
+ const [selectOpen, setSelectOpen] = useState6(false);
4278
4668
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
4279
4669
  open: tooltipOpen,
4280
4670
  onOpenChange: (nextOpen) => {
@@ -4477,384 +4867,6 @@ var Toolbar = {
4477
4867
  Markdown: MarkdownStandard,
4478
4868
  Extended: MarkdownExtended
4479
4869
  };
4480
-
4481
- // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
4482
- import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
4483
- import { Context as Context2 } from "@dxos/context";
4484
- import { invariant as invariant2 } from "@dxos/invariant";
4485
- import { log as log4 } from "@dxos/log";
4486
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
4487
- var DEBOUNCE_INTERVAL = 100;
4488
- var SpaceAwarenessProvider = class {
4489
- constructor(params) {
4490
- this._remoteStates = /* @__PURE__ */ new Map();
4491
- this.remoteStateChange = new Event2();
4492
- this._space = params.space;
4493
- this._channel = params.channel;
4494
- this._peerId = params.peerId;
4495
- this._info = params.info;
4496
- }
4497
- open() {
4498
- this._ctx = new Context2();
4499
- this._postTask = new DeferredTask(this._ctx, async () => {
4500
- if (this._localState) {
4501
- await this._space.postMessage(this._channel, {
4502
- kind: "post",
4503
- state: this._localState
4504
- });
4505
- await sleep(DEBOUNCE_INTERVAL);
4506
- }
4507
- });
4508
- this._ctx.onDispose(this._space.listen(this._channel, (message) => {
4509
- switch (message.payload.kind) {
4510
- case "query": {
4511
- this._handleQueryMessage();
4512
- break;
4513
- }
4514
- case "post": {
4515
- this._handlePostMessage(message.payload);
4516
- break;
4517
- }
4518
- }
4519
- }));
4520
- void this._space.postMessage(this._channel, {
4521
- kind: "query"
4522
- }).catch((err) => {
4523
- log4.debug("failed to query awareness", {
4524
- err
4525
- }, {
4526
- F: __dxlog_file5,
4527
- L: 89,
4528
- S: this,
4529
- C: (f, a) => f(...a)
4530
- });
4531
- });
4532
- }
4533
- close() {
4534
- void this._ctx?.dispose();
4535
- this._ctx = void 0;
4536
- this._postTask = void 0;
4537
- }
4538
- getRemoteStates() {
4539
- return Array.from(this._remoteStates.values());
4540
- }
4541
- update(position) {
4542
- invariant2(this._postTask, void 0, {
4543
- F: __dxlog_file5,
4544
- L: 104,
4545
- S: this,
4546
- A: [
4547
- "this._postTask",
4548
- ""
4549
- ]
4550
- });
4551
- this._localState = {
4552
- peerId: this._peerId,
4553
- position,
4554
- info: this._info
4555
- };
4556
- this._postTask.schedule();
4557
- }
4558
- _handleQueryMessage() {
4559
- invariant2(this._postTask, void 0, {
4560
- F: __dxlog_file5,
4561
- L: 115,
4562
- S: this,
4563
- A: [
4564
- "this._postTask",
4565
- ""
4566
- ]
4567
- });
4568
- this._postTask.schedule();
4569
- }
4570
- _handlePostMessage(message) {
4571
- invariant2(message.kind === "post", void 0, {
4572
- F: __dxlog_file5,
4573
- L: 120,
4574
- S: this,
4575
- A: [
4576
- "message.kind === 'post'",
4577
- ""
4578
- ]
4579
- });
4580
- this._remoteStates.set(message.state.peerId, message.state);
4581
- this.remoteStateChange.emit();
4582
- }
4583
- };
4584
-
4585
- // packages/ui/react-ui-editor/src/hooks/defs.ts
4586
- import { StateField as StateField7 } from "@codemirror/state";
4587
- var modelState = StateField7.define({
4588
- create: () => void 0,
4589
- update: (model) => model
4590
- });
4591
-
4592
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4593
- var useActionHandler = (view) => {
4594
- return (action) => {
4595
- if (!view) {
4596
- return;
4597
- }
4598
- let inlineType, listType;
4599
- switch (action.type) {
4600
- case "heading":
4601
- setHeading(parseInt(action.data))(view);
4602
- break;
4603
- case "strong":
4604
- case "emphasis":
4605
- case "strikethrough":
4606
- case "code":
4607
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4608
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4609
- break;
4610
- case "list-ordered":
4611
- case "list-bullet":
4612
- case "list-task":
4613
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4614
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4615
- break;
4616
- case "blockquote":
4617
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4618
- break;
4619
- case "codeblock":
4620
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4621
- break;
4622
- case "table":
4623
- insertTable(view);
4624
- break;
4625
- case "link":
4626
- (action.data === false ? removeLink : addLink)(view);
4627
- break;
4628
- case "comment":
4629
- createComment(view);
4630
- break;
4631
- }
4632
- setTimeout(() => {
4633
- if (!view.hasFocus) {
4634
- view.focus();
4635
- }
4636
- });
4637
- };
4638
- };
4639
-
4640
- // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
4641
- import { useRef as useRef3, useState as useState4 } from "react";
4642
- var useEditorView = (id) => {
4643
- const editorRef = useRef3(null);
4644
- const [prev, setPrev] = useState4([
4645
- null,
4646
- ""
4647
- ]);
4648
- if (prev[0] !== editorRef.current) {
4649
- setPrev([
4650
- editorRef.current,
4651
- id
4652
- ]);
4653
- }
4654
- return [
4655
- editorRef,
4656
- prev[1] !== id
4657
- ];
4658
- };
4659
-
4660
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4661
- import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4662
- import { EditorView as EditorView18 } from "@codemirror/view";
4663
- import { useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
4664
- import { log as log5 } from "@dxos/log";
4665
- import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
4666
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4667
- var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}, deps) => {
4668
- const onUpdate = useRef4();
4669
- const [view, setView] = useState5();
4670
- const parentRef = useRef4(null);
4671
- useEffect3(() => {
4672
- let view2;
4673
- if (parentRef.current) {
4674
- const state = EditorState3.create({
4675
- doc,
4676
- selection,
4677
- extensions: [
4678
- // TODO(burdon): Doesn't catch errors in keymap functions.
4679
- EditorView18.exceptionSink.of((err) => {
4680
- log5.catch(err, void 0, {
4681
- F: __dxlog_file6,
4682
- L: 57,
4683
- S: void 0,
4684
- C: (f, a) => f(...a)
4685
- });
4686
- }),
4687
- extensions,
4688
- EditorView18.updateListener.of(() => {
4689
- onUpdate.current?.();
4690
- })
4691
- ].filter(isNotFalsy5)
4692
- });
4693
- view2 = new EditorView18({
4694
- parent: parentRef.current,
4695
- scrollTo,
4696
- state,
4697
- // NOTE: Uncomment to debug/monitor all transactions.
4698
- // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4699
- dispatchTransactions: (trs, view3) => {
4700
- if (debug) {
4701
- logChanges(trs);
4702
- }
4703
- view3.update(trs);
4704
- }
4705
- });
4706
- setView(view2);
4707
- }
4708
- return () => {
4709
- view2?.destroy();
4710
- };
4711
- }, deps);
4712
- useEffect3(() => {
4713
- if (view) {
4714
- if (!selection && !view.state.selection.main.anchor) {
4715
- selection = EditorSelection2.single(view.state.doc.line(1).to);
4716
- }
4717
- if (selection || scrollTo) {
4718
- onUpdate.current = () => {
4719
- onUpdate.current = void 0;
4720
- view.dispatch({
4721
- selection,
4722
- effects: scrollTo && [
4723
- scrollTo
4724
- ],
4725
- scrollIntoView: !scrollTo
4726
- });
4727
- };
4728
- }
4729
- if (autoFocus) {
4730
- view.focus();
4731
- }
4732
- }
4733
- }, [
4734
- view,
4735
- autoFocus,
4736
- selection,
4737
- scrollTo
4738
- ]);
4739
- return {
4740
- parentRef,
4741
- view
4742
- };
4743
- };
4744
- var createDataExtensions = ({ readonly = false } = {}) => {
4745
- return [
4746
- //
4747
- EditorState3.readOnly.of(readonly),
4748
- EditorView18.editable.of(!readonly)
4749
- ];
4750
- };
4751
- var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4752
- return [
4753
- //
4754
- EditorView18.baseTheme(defaultTheme),
4755
- theme && EditorView18.theme(theme),
4756
- EditorView18.darkTheme.of(themeMode === "dark"),
4757
- EditorView18.editorAttributes.of({
4758
- class: slots?.editor?.className ?? ""
4759
- }),
4760
- EditorView18.contentAttributes.of({
4761
- class: slots?.content?.className ?? ""
4762
- })
4763
- ].filter(isNotFalsy5);
4764
- };
4765
-
4766
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
4767
- import get5 from "lodash.get";
4768
- import { useState as useState6, useMemo as useMemo3 } from "react";
4769
- import { generateName } from "@dxos/display-name";
4770
- import { getRawDoc } from "@dxos/echo-schema";
4771
- import { invariant as invariant3 } from "@dxos/invariant";
4772
- import { isAutomergeObject } from "@dxos/react-client/echo";
4773
- import { hueTokens } from "@dxos/react-ui-theme";
4774
- import { hexToHue } from "@dxos/util";
4775
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
4776
- var useTextExtensions = ({ id, text, space, identity }) => {
4777
- const extensions = [
4778
- automerge(text)
4779
- ];
4780
- const peerId = identity?.identityKey.toHex();
4781
- const { cursorLightValue, cursorDarkValue } = hueTokens[hexToHue(peerId ?? "0")];
4782
- if (space && identity) {
4783
- const awarenessProvider2 = new SpaceAwarenessProvider({
4784
- space,
4785
- channel: `awareness.${id}`,
4786
- peerId: identity.identityKey.toHex(),
4787
- info: {
4788
- displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
4789
- color: cursorDarkValue,
4790
- lightColor: cursorLightValue
4791
- }
4792
- });
4793
- extensions.push(awareness(awarenessProvider2));
4794
- }
4795
- return extensions;
4796
- };
4797
- var useTextModel = (props) => useMemo3(() => createModel(props), Object.values(props));
4798
- var useInMemoryTextModel = ({ id, defaultContent }) => {
4799
- const [content, setContent] = useState6(defaultContent ?? "");
4800
- return {
4801
- id,
4802
- content,
4803
- setContent,
4804
- text: () => content
4805
- };
4806
- };
4807
- var createModel = ({ space, identity, text }) => {
4808
- if (!text) {
4809
- return void 0;
4810
- }
4811
- invariant3(isAutomergeObject(text), void 0, {
4812
- F: __dxlog_file7,
4813
- L: 87,
4814
- S: void 0,
4815
- A: [
4816
- "isAutomergeObject(text)",
4817
- ""
4818
- ]
4819
- });
4820
- const obj = text;
4821
- const doc = getRawDoc(obj, [
4822
- obj.field
4823
- ]);
4824
- const peerId = identity?.identityKey.toHex();
4825
- const { cursorLightValue, cursorDarkValue } = hueTokens[hexToHue(peerId ?? "0")];
4826
- const awarenessProvider2 = space && new SpaceAwarenessProvider({
4827
- space,
4828
- channel: `automerge.awareness.${obj.id}`,
4829
- info: {
4830
- displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
4831
- color: cursorLightValue,
4832
- lightColor: cursorDarkValue
4833
- },
4834
- peerId: identity?.identityKey.toHex() ?? "Anonymous"
4835
- });
4836
- const extensions = [
4837
- modelState.init(() => model),
4838
- automerge({
4839
- handle: doc.handle,
4840
- path: doc.path
4841
- })
4842
- ];
4843
- if (awarenessProvider2) {
4844
- extensions.push(awareness(awarenessProvider2));
4845
- }
4846
- const model = {
4847
- id: obj.id,
4848
- content: doc,
4849
- text: () => get5(doc.handle.docSync(), doc.path),
4850
- extension: extensions,
4851
- peer: identity ? {
4852
- id: identity.identityKey.toHex(),
4853
- name: identity.profile?.displayName
4854
- } : void 0
4855
- };
4856
- return model;
4857
- };
4858
4870
  export {
4859
4871
  BaseTextEditor,
4860
4872
  Cursor,
@@ -4886,9 +4898,8 @@ export {
4886
4898
  command,
4887
4899
  comments,
4888
4900
  compareFormatting,
4889
- createBasicBundle,
4901
+ createBasicExtensions,
4890
4902
  createComment,
4891
- createDataExtensions,
4892
4903
  createMarkdownExtensions,
4893
4904
  createThemeExtensions,
4894
4905
  cursorLineMargin,
@@ -4919,7 +4930,6 @@ export {
4919
4930
  markdownTagsExtensions,
4920
4931
  markdownTheme,
4921
4932
  mention,
4922
- modelState,
4923
4933
  outliner,
4924
4934
  removeBlockquote,
4925
4935
  removeCodeblock,