@dxos/react-ui-editor 0.4.8-main.97e86fe → 0.4.8-main.a4c603b

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 (108) hide show
  1. package/dist/lib/browser/index.mjs +941 -776
  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 +103 -0
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -0
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +34 -18
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +5 -79
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/extensions/automerge/automerge.d.ts +1 -1
  11. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -1
  13. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  14. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  15. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  16. package/dist/types/src/extensions/automerge/defs.d.ts +15 -1
  17. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/index.d.ts +1 -0
  19. package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/sync.d.ts +1 -2
  21. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -2
  23. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  24. package/dist/types/src/extensions/awareness.d.ts.map +1 -0
  25. package/dist/types/src/extensions/comments.d.ts +2 -2
  26. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  27. package/dist/types/src/extensions/cursor-line-margin.d.ts +7 -6
  28. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +1 -1
  29. package/dist/types/src/extensions/cursor.d.ts +1 -1
  30. package/dist/types/src/extensions/cursor.d.ts.map +1 -1
  31. package/dist/types/src/extensions/index.d.ts +1 -3
  32. package/dist/types/src/extensions/index.d.ts.map +1 -1
  33. package/dist/types/src/extensions/markdown/bundle.d.ts +4 -5
  34. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  35. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  37. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  38. package/dist/types/src/extensions/markdown/table.d.ts +2 -2
  39. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  40. package/dist/types/src/extensions/outliner.d.ts +4 -0
  41. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  42. package/dist/types/src/{extensions/awareness → hooks}/awareness-provider.d.ts +1 -1
  43. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
  44. package/dist/types/src/hooks/defs.d.ts +20 -0
  45. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  46. package/dist/types/src/hooks/index.d.ts +4 -1
  47. package/dist/types/src/hooks/index.d.ts.map +1 -1
  48. package/dist/types/src/hooks/useEditorView.d.ts +20 -0
  49. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  50. package/dist/types/src/hooks/useTextEditor.d.ts +44 -3
  51. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  52. package/dist/types/src/hooks/useTextEditor.stories.d.ts +0 -6
  53. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -1
  54. package/dist/types/src/hooks/useTextModel.d.ts +33 -0
  55. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -0
  56. package/dist/types/src/themes/default.d.ts +3 -0
  57. package/dist/types/src/themes/default.d.ts.map +1 -1
  58. package/package.json +24 -24
  59. package/src/components/TextEditor/MarkdownEditor.stories.tsx +524 -0
  60. package/src/components/TextEditor/TextEditor.stories.tsx +23 -508
  61. package/src/components/TextEditor/TextEditor.tsx +149 -37
  62. package/src/components/Toolbar/Toolbar.stories.tsx +42 -42
  63. package/src/extensions/automerge/automerge.stories.tsx +17 -16
  64. package/src/extensions/automerge/automerge.ts +1 -2
  65. package/src/extensions/automerge/cursor.ts +1 -1
  66. package/src/extensions/automerge/defs.ts +22 -1
  67. package/src/extensions/automerge/index.ts +1 -0
  68. package/src/extensions/automerge/sync.ts +9 -2
  69. package/src/extensions/automerge/update-automerge.ts +1 -2
  70. package/src/extensions/{awareness/awareness.ts → awareness.ts} +1 -1
  71. package/src/extensions/command/state.ts +1 -1
  72. package/src/extensions/comments.ts +5 -9
  73. package/src/extensions/cursor-line-margin.ts +18 -17
  74. package/src/extensions/cursor.ts +1 -1
  75. package/src/extensions/index.ts +1 -3
  76. package/src/extensions/markdown/bundle.ts +28 -11
  77. package/src/extensions/markdown/decorate.ts +4 -2
  78. package/src/extensions/markdown/formatting.ts +10 -15
  79. package/src/extensions/markdown/image.ts +0 -1
  80. package/src/extensions/markdown/table.ts +3 -11
  81. package/src/extensions/outliner.ts +12 -0
  82. package/src/{extensions/awareness → hooks}/awareness-provider.ts +1 -1
  83. package/src/hooks/defs.ts +30 -0
  84. package/src/hooks/index.ts +5 -1
  85. package/src/hooks/useEditorView.ts +32 -0
  86. package/src/hooks/useTextEditor.stories.tsx +34 -39
  87. package/src/hooks/useTextEditor.ts +120 -11
  88. package/src/hooks/useTextModel.ts +121 -0
  89. package/src/index.ts +1 -1
  90. package/src/themes/default.ts +28 -1
  91. package/dist/types/src/extensions/awareness/awareness-provider.d.ts.map +0 -1
  92. package/dist/types/src/extensions/awareness/awareness.d.ts.map +0 -1
  93. package/dist/types/src/extensions/awareness/index.d.ts +0 -3
  94. package/dist/types/src/extensions/awareness/index.d.ts.map +0 -1
  95. package/dist/types/src/extensions/doc.d.ts +0 -3
  96. package/dist/types/src/extensions/doc.d.ts.map +0 -1
  97. package/dist/types/src/extensions/factories.d.ts +0 -49
  98. package/dist/types/src/extensions/factories.d.ts.map +0 -1
  99. package/dist/types/src/extensions/types.d.ts +0 -9
  100. package/dist/types/src/extensions/types.d.ts.map +0 -1
  101. package/dist/types/src/hooks/useDocAccessor.d.ts +0 -7
  102. package/dist/types/src/hooks/useDocAccessor.d.ts.map +0 -1
  103. package/src/extensions/awareness/index.ts +0 -6
  104. package/src/extensions/doc.ts +0 -14
  105. package/src/extensions/factories.ts +0 -167
  106. package/src/extensions/types.ts +0 -16
  107. package/src/hooks/useDocAccessor.ts +0 -21
  108. /package/dist/types/src/extensions/{awareness/awareness.d.ts → awareness.d.ts} +0 -0
@@ -29,12 +29,16 @@ import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
29
29
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
30
30
 
31
31
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
32
- import { EditorState as EditorState2 } from "@codemirror/state";
33
- import { EditorView as EditorView16 } from "@codemirror/view";
32
+ import { EditorState as EditorState3 } from "@codemirror/state";
33
+ import { EditorView as EditorView17, scrollPastEnd as scrollPastEnd2 } from "@codemirror/view";
34
34
  import { useFocusableGroup } from "@fluentui/react-tabster";
35
- import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2 } from "react";
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";
36
38
  import { log as log9 } from "@dxos/log";
37
- import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
39
+ import { useThemeContext } from "@dxos/react-ui";
40
+ import { focusRing } from "@dxos/react-ui-theme";
41
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
38
42
 
39
43
  // packages/ui/react-ui-editor/src/extensions/annotations.ts
40
44
  import { StateField } from "@codemirror/state";
@@ -239,6 +243,7 @@ var cursorConverter = ({ handle, path }) => ({
239
243
 
240
244
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
241
245
  import { Annotation, StateEffect } from "@codemirror/state";
246
+ import get2 from "lodash.get";
242
247
  var getPath = (state, field) => state.field(field).path;
243
248
  var getLastHeads = (state, field) => state.field(field).lastHeads;
244
249
  var updateHeadsEffect = StateEffect.define({});
@@ -492,7 +497,7 @@ var automerge = (accessor) => {
492
497
  ];
493
498
  };
494
499
 
495
- // packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts
500
+ // packages/ui/react-ui-editor/src/extensions/awareness.ts
496
501
  import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
497
502
  import { Decoration as Decoration2, EditorView as EditorView3, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
498
503
  import { Event } from "@dxos/async";
@@ -727,115 +732,11 @@ var styles2 = EditorView3.baseTheme({
727
732
  }
728
733
  });
729
734
 
730
- // packages/ui/react-ui-editor/src/extensions/awareness/awareness-provider.ts
731
- import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
732
- import { Context as Context2 } from "@dxos/context";
733
- import { invariant } from "@dxos/invariant";
734
- import { log as log2 } from "@dxos/log";
735
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/awareness/awareness-provider.ts";
736
- var DEBOUNCE_INTERVAL = 100;
737
- var SpaceAwarenessProvider = class {
738
- constructor(params) {
739
- this._remoteStates = /* @__PURE__ */ new Map();
740
- this.remoteStateChange = new Event2();
741
- this._space = params.space;
742
- this._channel = params.channel;
743
- this._peerId = params.peerId;
744
- this._info = params.info;
745
- }
746
- open() {
747
- this._ctx = new Context2();
748
- this._postTask = new DeferredTask(this._ctx, async () => {
749
- if (this._localState) {
750
- await this._space.postMessage(this._channel, {
751
- kind: "post",
752
- state: this._localState
753
- });
754
- await sleep(DEBOUNCE_INTERVAL);
755
- }
756
- });
757
- this._ctx.onDispose(this._space.listen(this._channel, (message) => {
758
- switch (message.payload.kind) {
759
- case "query": {
760
- this._handleQueryMessage();
761
- break;
762
- }
763
- case "post": {
764
- this._handlePostMessage(message.payload);
765
- break;
766
- }
767
- }
768
- }));
769
- void this._space.postMessage(this._channel, {
770
- kind: "query"
771
- }).catch((err) => {
772
- log2.debug("failed to query awareness", {
773
- err
774
- }, {
775
- F: __dxlog_file2,
776
- L: 89,
777
- S: this,
778
- C: (f, a) => f(...a)
779
- });
780
- });
781
- }
782
- close() {
783
- void this._ctx?.dispose();
784
- this._ctx = void 0;
785
- this._postTask = void 0;
786
- }
787
- getRemoteStates() {
788
- return Array.from(this._remoteStates.values());
789
- }
790
- update(position) {
791
- invariant(this._postTask, void 0, {
792
- F: __dxlog_file2,
793
- L: 104,
794
- S: this,
795
- A: [
796
- "this._postTask",
797
- ""
798
- ]
799
- });
800
- this._localState = {
801
- peerId: this._peerId,
802
- position,
803
- info: this._info
804
- };
805
- this._postTask.schedule();
806
- }
807
- _handleQueryMessage() {
808
- invariant(this._postTask, void 0, {
809
- F: __dxlog_file2,
810
- L: 115,
811
- S: this,
812
- A: [
813
- "this._postTask",
814
- ""
815
- ]
816
- });
817
- this._postTask.schedule();
818
- }
819
- _handlePostMessage(message) {
820
- invariant(message.kind === "post", void 0, {
821
- F: __dxlog_file2,
822
- L: 120,
823
- S: this,
824
- A: [
825
- "message.kind === 'post'",
826
- ""
827
- ]
828
- });
829
- this._remoteStates.set(message.state.peerId, message.state);
830
- this.remoteStateChange.emit();
831
- }
832
- };
833
-
834
735
  // packages/ui/react-ui-editor/src/extensions/blast.ts
835
736
  import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
836
737
  import defaultsDeep from "lodash.defaultsdeep";
837
- import { invariant as invariant2 } from "@dxos/invariant";
838
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
738
+ import { invariant } from "@dxos/invariant";
739
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
839
740
  var defaultOptions = {
840
741
  effect: 2,
841
742
  maxParticles: 200,
@@ -961,8 +862,8 @@ var Blaster = class {
961
862
  return this._node;
962
863
  }
963
864
  initialize() {
964
- invariant2(!this._canvas && !this._ctx, void 0, {
965
- F: __dxlog_file3,
865
+ invariant(!this._canvas && !this._ctx, void 0, {
866
+ F: __dxlog_file2,
966
867
  L: 141,
967
868
  S: this,
968
869
  A: [
@@ -998,8 +899,8 @@ var Blaster = class {
998
899
  }
999
900
  }
1000
901
  start() {
1001
- invariant2(this._canvas && this._ctx, void 0, {
1002
- F: __dxlog_file3,
902
+ invariant(this._canvas && this._ctx, void 0, {
903
+ F: __dxlog_file2,
1003
904
  L: 180,
1004
905
  S: this,
1005
906
  A: [
@@ -1209,8 +1110,8 @@ var commandState = StateField3.define({
1209
1110
  }
1210
1111
  return state;
1211
1112
  },
1212
- provide: (field) => [
1213
- showTooltip.from(field, (value) => value.tooltip ?? null)
1113
+ provide: (f) => [
1114
+ showTooltip.from(f, (value) => value.tooltip ?? null)
1214
1115
  ]
1215
1116
  });
1216
1117
  var openEffect = StateEffect2.define();
@@ -1365,7 +1266,7 @@ import { hoverTooltip, keymap as keymap4, Decoration as Decoration4, EditorView
1365
1266
  import sortBy from "lodash.sortby";
1366
1267
  import { useEffect } from "react";
1367
1268
  import { debounce } from "@dxos/async";
1368
- import { log as log4 } from "@dxos/log";
1269
+ import { log as log3 } from "@dxos/log";
1369
1270
  import { nonNullable } from "@dxos/util";
1370
1271
 
1371
1272
  // packages/ui/react-ui-editor/src/styles/markdown.ts
@@ -1392,10 +1293,10 @@ var inlineUrl = mx(code, "px-1");
1392
1293
  var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
1393
1294
 
1394
1295
  // packages/ui/react-ui-editor/src/styles/tokens.ts
1395
- import get2 from "lodash.get";
1296
+ import get3 from "lodash.get";
1396
1297
  import { tailwindConfig } from "@dxos/react-ui-theme";
1397
1298
  var tokens = tailwindConfig({}).theme;
1398
- var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
1299
+ var getToken = (path, defaultValue = void 0) => get3(tokens, path, defaultValue);
1399
1300
 
1400
1301
  // packages/ui/react-ui-editor/src/styles/layout.ts
1401
1302
  var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
@@ -1403,14 +1304,14 @@ var editorFillLayoutRoot = "bs-full relative";
1403
1304
  var editorFillLayoutEditor = "!absolute inset-0";
1404
1305
 
1405
1306
  // packages/ui/react-ui-editor/src/util.ts
1406
- import { log as log3 } from "@dxos/log";
1407
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1307
+ import { log as log2 } from "@dxos/log";
1308
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1408
1309
  var callbackWrapper = (fn) => (...args) => {
1409
1310
  try {
1410
1311
  return fn(...args);
1411
1312
  } catch (err) {
1412
- log3.catch(err, void 0, {
1413
- F: __dxlog_file4,
1313
+ log2.catch(err, void 0, {
1314
+ F: __dxlog_file3,
1414
1315
  L: 18,
1415
1316
  S: void 0,
1416
1317
  C: (f, a) => f(...a)
@@ -1433,10 +1334,10 @@ var logChanges = (trs) => {
1433
1334
  return changes2;
1434
1335
  }).filter(Boolean);
1435
1336
  if (changes.length) {
1436
- log3.info("changes", {
1337
+ log2.info("changes", {
1437
1338
  changes
1438
1339
  }, {
1439
- F: __dxlog_file4,
1340
+ F: __dxlog_file3,
1440
1341
  L: 39,
1441
1342
  S: void 0,
1442
1343
  C: (f, a) => f(...a)
@@ -1445,7 +1346,7 @@ var logChanges = (trs) => {
1445
1346
  };
1446
1347
 
1447
1348
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1448
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1349
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1449
1350
  var documentId = Facet4.define({
1450
1351
  combine: (values) => values[0]
1451
1352
  });
@@ -1531,8 +1432,8 @@ var commentsDecorations = EditorView7.decorations.compute([
1531
1432
  const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1532
1433
  const range = comment.range;
1533
1434
  if (!range || range.from === range.to) {
1534
- log4.warn("Invalid range:", range, {
1535
- F: __dxlog_file5,
1435
+ log3.warn("Invalid range:", range, {
1436
+ F: __dxlog_file4,
1536
1437
  L: 132,
1537
1438
  S: void 0,
1538
1439
  C: (f, a) => f(...a)
@@ -1834,14 +1735,12 @@ var focusComment = (view, id, center = true) => {
1834
1735
  var useComments = (view, id, comments2 = []) => {
1835
1736
  useEffect(() => {
1836
1737
  if (view) {
1837
- if (id === view.state.facet(documentId)) {
1838
- view.dispatch({
1839
- effects: setComments.of({
1840
- id,
1841
- comments: comments2
1842
- })
1843
- });
1844
- }
1738
+ view.dispatch({
1739
+ effects: setComments.of({
1740
+ id,
1741
+ comments: comments2
1742
+ })
1743
+ });
1845
1744
  }
1846
1745
  }, [
1847
1746
  id,
@@ -1851,97 +1750,74 @@ var useComments = (view, id, comments2 = []) => {
1851
1750
  };
1852
1751
 
1853
1752
  // packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts
1854
- import { Transaction } from "@codemirror/state";
1753
+ import { Facet as Facet5, Transaction } from "@codemirror/state";
1855
1754
  import { EditorView as EditorView8 } from "@codemirror/view";
1856
- import { log as log5 } from "@dxos/log";
1857
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts";
1755
+ import { log as log4 } from "@dxos/log";
1756
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts";
1757
+ var cursorLineMarginFacet = Facet5.define({
1758
+ combine: (input) => input[0] ?? 3
1759
+ });
1858
1760
  var annotation = "cursorLineMargin";
1859
1761
  var lineAtPos = (s, pos) => s.doc.lineAt(pos).number;
1860
- var cursorLineMargin = (options = {
1861
- lines: 3
1862
- }) => {
1863
- const { lines } = options;
1864
- if (!lines || lines <= 0) {
1865
- return [];
1762
+ var cursorLineMargin = EditorView8.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1763
+ if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1764
+ return;
1866
1765
  }
1867
- return EditorView8.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1868
- if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1869
- return;
1870
- }
1871
- const s = state;
1872
- const { main } = s.selection;
1873
- const rect = view.dom.getBoundingClientRect();
1874
- const viewportTop = rect.top - view.documentTop;
1875
- const viewportBottom = rect.bottom - view.documentTop;
1876
- const mainLine = lineAtPos(s, main.head);
1877
- const _visTopLine = lineAtPos(s, view.lineBlockAtHeight(viewportTop).from);
1878
- const botLineBlk = view.lineBlockAtHeight(viewportBottom);
1879
- const visBotLineDoc = lineAtPos(s, Math.min(botLineBlk.to, s.doc.length));
1880
- const visBotLine = botLineBlk.bottom >= viewportBottom ? visBotLineDoc : visBotLineDoc + Math.floor((viewportBottom - botLineBlk.bottom) / view.defaultLineHeight);
1881
- const needsScrollTop = false;
1882
- const needsScrollBot = mainLine >= visBotLine - lines;
1883
- if (needsScrollTop && needsScrollBot) {
1884
- log5.warn("is cursorScrollMargin too large for the editor size?", void 0, {
1885
- F: __dxlog_file6,
1886
- L: 65,
1887
- S: void 0,
1888
- C: (f, a) => f(...a)
1889
- });
1766
+ const s = state;
1767
+ const { main } = s.selection;
1768
+ const cursorLineMargin2 = s.facet(cursorLineMarginFacet);
1769
+ const rect = view.dom.getBoundingClientRect();
1770
+ const viewportTop = rect.top - view.documentTop;
1771
+ const viewportBottom = rect.bottom - view.documentTop;
1772
+ const mainLine = lineAtPos(s, main.head);
1773
+ const _visTopLine = lineAtPos(s, view.lineBlockAtHeight(viewportTop).from);
1774
+ const botLineBlk = view.lineBlockAtHeight(viewportBottom);
1775
+ const visBotLineDoc = lineAtPos(s, Math.min(botLineBlk.to, s.doc.length));
1776
+ const visBotLine = botLineBlk.bottom >= viewportBottom ? visBotLineDoc : visBotLineDoc + Math.floor((viewportBottom - botLineBlk.bottom) / view.defaultLineHeight);
1777
+ const needsScrollTop = false;
1778
+ const needsScrollBot = mainLine >= visBotLine - cursorLineMargin2;
1779
+ if (needsScrollTop && needsScrollBot) {
1780
+ log4.warn("is cursorScrollMargin too large for the editor size?", void 0, {
1781
+ F: __dxlog_file5,
1782
+ L: 66,
1783
+ S: void 0,
1784
+ C: (f, a) => f(...a)
1785
+ });
1786
+ return;
1787
+ }
1788
+ if (!needsScrollTop && !needsScrollBot) {
1789
+ return;
1790
+ }
1791
+ if (selectionSet) {
1792
+ const { head } = startState.selection.main;
1793
+ const oldMainLine = lineAtPos(startState, head);
1794
+ if (needsScrollTop && oldMainLine < mainLine) {
1890
1795
  return;
1891
1796
  }
1892
- if (!needsScrollTop && !needsScrollBot) {
1797
+ if (needsScrollBot && oldMainLine > mainLine) {
1893
1798
  return;
1894
1799
  }
1895
- if (selectionSet) {
1896
- const { head } = startState.selection.main;
1897
- const oldMainLine = lineAtPos(startState, head);
1898
- if (needsScrollTop && oldMainLine < mainLine) {
1899
- return;
1900
- }
1901
- if (needsScrollBot && oldMainLine > mainLine) {
1902
- return;
1903
- }
1904
- }
1905
- view.dispatch({
1906
- annotations: Transaction.userEvent.of(annotation),
1907
- effects: EditorView8.scrollIntoView(s.selection.main.head, {
1908
- y: needsScrollTop ? "start" : "end",
1909
- yMargin: view.defaultLineHeight * lines
1910
- })
1911
- });
1912
- });
1913
- };
1914
-
1915
- // packages/ui/react-ui-editor/src/extensions/doc.ts
1916
- import { Facet as Facet5 } from "@codemirror/state";
1917
- import { invariant as invariant3 } from "@dxos/invariant";
1918
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/doc.ts";
1919
- var documentId2 = Facet5.define({
1920
- combine: (providers) => {
1921
- invariant3(providers.length <= 1, void 0, {
1922
- F: __dxlog_file7,
1923
- L: 11,
1924
- S: void 0,
1925
- A: [
1926
- "providers.length <= 1",
1927
- ""
1928
- ]
1929
- });
1930
- return providers[0];
1931
1800
  }
1801
+ view.dispatch({
1802
+ annotations: Transaction.userEvent.of(annotation),
1803
+ effects: EditorView8.scrollIntoView(s.selection.main.head, {
1804
+ y: needsScrollTop ? "start" : "end",
1805
+ yMargin: view.defaultLineHeight * cursorLineMargin2
1806
+ })
1807
+ });
1932
1808
  });
1933
1809
 
1934
1810
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
1935
1811
  import { EditorView as EditorView9 } from "@codemirror/view";
1936
- import { log as log6 } from "@dxos/log";
1937
- var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/dnd.ts";
1812
+ import { log as log5 } from "@dxos/log";
1813
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/dnd.ts";
1938
1814
  var dnd = () => {
1939
1815
  return EditorView9.domEventHandlers({
1940
1816
  drop: (event, view) => {
1941
- log6.info("domEventHandlersDrop", {
1817
+ log5.info("domEventHandlersDrop", {
1942
1818
  event
1943
1819
  }, {
1944
- F: __dxlog_file8,
1820
+ F: __dxlog_file6,
1945
1821
  L: 14,
1946
1822
  S: void 0,
1947
1823
  C: (f, a) => f(...a)
@@ -1950,350 +1826,31 @@ var dnd = () => {
1950
1826
  });
1951
1827
  };
1952
1828
 
1953
- // packages/ui/react-ui-editor/src/extensions/factories.ts
1954
- import { closeBrackets } from "@codemirror/autocomplete";
1955
- import { history } from "@codemirror/commands";
1956
- import { bracketMatching } from "@codemirror/language";
1829
+ // packages/ui/react-ui-editor/src/extensions/listener.ts
1830
+ import { EditorView as EditorView10 } from "@codemirror/view";
1831
+ var listener = ({ onFocus, onChange }) => {
1832
+ const extensions = [];
1833
+ onFocus && extensions.push(EditorView10.focusChangeEffect.of((_, focusing) => {
1834
+ onFocus(focusing);
1835
+ return null;
1836
+ }));
1837
+ onChange && extensions.push(EditorView10.updateListener.of((update2) => {
1838
+ onChange(update2.state.doc.toString());
1839
+ }));
1840
+ return extensions;
1841
+ };
1842
+
1843
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1844
+ import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
1845
+ import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1846
+ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1847
+ import { bracketMatching, defaultHighlightStyle, indentOnInput, syntaxHighlighting } from "@codemirror/language";
1848
+ import { languages } from "@codemirror/language-data";
1849
+ import { searchKeymap } from "@codemirror/search";
1957
1850
  import { EditorState } from "@codemirror/state";
1958
- import { EditorView as EditorView10, crosshairCursor, drawSelection, dropCursor, highlightActiveLine, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
1959
- import defaultsDeep2 from "lodash.defaultsdeep";
1960
- import { generateName } from "@dxos/display-name";
1961
- import { log as log7 } from "@dxos/log";
1962
- import { hueTokens } from "@dxos/react-ui-theme";
1963
- import { hexToHue, isNotFalsy as isNotFalsy2 } from "@dxos/util";
1964
-
1965
- // packages/ui/react-ui-editor/src/themes/default.ts
1966
- import get3 from "lodash.get";
1967
- var defaultTheme = {
1968
- //
1969
- // Main layout:
1970
- // https://codemirror.net/examples/styling
1971
- //
1972
- // <div class="cm-editor [cm-focused] [generated classes]">
1973
- // <div class="cm-scroller">
1974
- // <div class="cm-gutters">
1975
- // <div class="cm-gutter [...]">
1976
- // <div class="cm-gutterElement">...</div>
1977
- // </div>
1978
- // </div>
1979
- // <div class="cm-content" role="textbox" contenteditable="true">
1980
- // <div class="cm-line"></div>
1981
- // </div>
1982
- // <div class="cm-selectionLayer">
1983
- // <div class="cm-selectionBackground"></div>
1984
- // </div>
1985
- // <div class="cm-cursorLayer">
1986
- // <div class="cm-cursor"></div>
1987
- // </div>
1988
- // </div>
1989
- // </div>
1990
- //
1991
- "&": {},
1992
- "&.cm-focused": {
1993
- outline: "none"
1994
- },
1995
- // NOTE: See https://codemirror.net/docs/guide (DOM Structure).
1996
- ".cm-scroller": {
1997
- // TODO(burdon): Reconcile with docs: https://codemirror.net/docs/guide
1998
- // Inside of that is the scroller element. If the editor has its own scrollbar, this one should be styled with overflow: auto. But it doesn't have to—the editor also supports growing to accomodate its content, or growing up to a certain max-height and then scrolling.
1999
- overflowY: "auto",
2000
- fontFamily: get3(tokens, "fontFamily.body", []).join(","),
2001
- lineHeight: 1.5
2002
- },
2003
- ".cm-content": {
2004
- // TODO(burdon): Is it possible to remove the padding value from CM's default theme?
2005
- // padding: 'unset',
2006
- // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
2007
- fontSize: "16px"
2008
- },
2009
- "&light .cm-content": {
2010
- color: get3(tokens, "extend.semanticColors.base.fg.light", "black"),
2011
- caretColor: "black"
2012
- },
2013
- "&dark .cm-content": {
2014
- color: get3(tokens, "extend.semanticColors.base.fg.dark", "white"),
2015
- caretColor: "white"
2016
- },
2017
- //
2018
- // Cursor
2019
- //
2020
- "&light .cm-cursor, &light .cm-dropCursor": {
2021
- borderLeft: "2px solid black"
2022
- },
2023
- "&dark .cm-cursor, &dark .cm-dropCursor": {
2024
- borderLeft: "2px solid white"
2025
- },
2026
- "&light .cm-placeholder": {
2027
- color: get3(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
2028
- },
2029
- "&dark .cm-placeholder": {
2030
- color: get3(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
2031
- },
2032
- //
2033
- // line
2034
- //
2035
- ".cm-line": {
2036
- paddingInline: 0
2037
- },
2038
- ".cm-activeLine": {
2039
- background: "transparent"
2040
- },
2041
- //
2042
- // gutter
2043
- //
2044
- ".cm-gutterElement": {
2045
- width: "48px"
2046
- },
2047
- //
2048
- // Selection
2049
- //
2050
- "&light .cm-selectionBackground": {
2051
- background: get3(tokens, "extend.colors.primary.100")
2052
- },
2053
- "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
2054
- background: get3(tokens, "extend.colors.primary.200")
2055
- },
2056
- "&dark .cm-selectionBackground": {
2057
- background: get3(tokens, "extend.colors.primary.700")
2058
- },
2059
- "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
2060
- background: get3(tokens, "extend.colors.primary.600")
2061
- },
2062
- //
2063
- // Search
2064
- //
2065
- "&light .cm-searchMatch": {
2066
- backgroundColor: get3(tokens, "extend.colors.yellow.100")
2067
- },
2068
- "&dark .cm-searchMatch": {
2069
- backgroundColor: get3(tokens, "extend.colors.yellow.700")
2070
- },
2071
- //
2072
- // link
2073
- //
2074
- ".cm-link": {
2075
- color: get3(tokens, "extend.colors.primary.500"),
2076
- textDecorationLine: "underline",
2077
- textDecorationThickness: "1px",
2078
- textUnderlineOffset: "2px",
2079
- borderRadius: ".125rem",
2080
- fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2081
- },
2082
- //
2083
- // tooltip
2084
- //
2085
- ".cm-tooltip": {
2086
- border: "none",
2087
- background: "unset"
2088
- },
2089
- ".cm-tooltip-below": {},
2090
- //
2091
- // autocomplete
2092
- //
2093
- ".cm-tooltip-autocomplete": {
2094
- marginTop: "4px",
2095
- marginLeft: "-3px"
2096
- },
2097
- ".cm-tooltip-autocomplete ul li": {},
2098
- ".cm-tooltip-autocomplete ul li[aria-selected]": {},
2099
- ".cm-completionIcon": {
2100
- display: "none"
2101
- },
2102
- ".cm-completionLabel": {
2103
- fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2104
- },
2105
- ".cm-completionMatchedText": {
2106
- textDecoration: "none"
2107
- },
2108
- //
2109
- // table
2110
- //
2111
- ".cm-table *": {
2112
- fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
2113
- textDecoration: "none !important"
2114
- },
2115
- ".cm-table-head": {
2116
- padding: "2px 16px 2px 0px",
2117
- borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
2118
- fontWeight: 100,
2119
- textAlign: "left",
2120
- color: get3(tokens, "extend.colors.neutral.500")
2121
- },
2122
- ".cm-table-cell": {
2123
- padding: "2px 16px 2px 0px"
2124
- },
2125
- //
2126
- // image
2127
- //
2128
- ".cm-image": {
2129
- display: "block",
2130
- height: "0"
2131
- },
2132
- ".cm-image.cm-loaded-image": {
2133
- height: "auto",
2134
- borderTop: "0.5rem solid transparent",
2135
- borderBottom: "0.5rem solid transparent"
2136
- },
2137
- //
2138
- // font size
2139
- // TODO(thure): This appears to be the best or only way to set selection caret heights,
2140
- // but it's far more verbose than it needs to be.
2141
- //
2142
- // ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
2143
- // const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
2144
- //
2145
- // acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
2146
- // acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
2147
- // acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
2148
- // return acc;
2149
- // }, {}),
2150
- /**
2151
- * Panels
2152
- * TODO(burdon): Needs styling attention (esp. dark mode).
2153
- * https://github.com/codemirror/search/blob/main/src/search.ts#L745
2154
- *
2155
- * Find/replace panel.
2156
- * <div class="cm-announced">...</div>
2157
- * <div class="cm-scroller">...</div>
2158
- * <div class="cm-panels cm-panels-bottom">
2159
- * <div class="cm-search cm-panel">
2160
- * <input class="cm-textfield" />
2161
- * <button class="cm-button">...</button>
2162
- * <label><input type="checkbox" />...</label>
2163
- * </div>
2164
- * </div
2165
- */
2166
- ".cm-panels": {
2167
- border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
2168
- },
2169
- ".cm-panel": {
2170
- background: get3(tokens, "extend.colors.neutral.50"),
2171
- fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2172
- },
2173
- ".cm-button": {
2174
- margin: "4px",
2175
- fontFamily: get3(tokens, "fontFamily.body", []).join(","),
2176
- background: get3(tokens, "extend.colors.neutral.100"),
2177
- backgroundImage: "none",
2178
- border: "none",
2179
- "&:hover": {
2180
- background: get3(tokens, "extend.colors.neutral.200")
2181
- },
2182
- "&:active": {
2183
- background: get3(tokens, "extend.colors.neutral.300"),
2184
- backgroundImage: "none"
2185
- }
2186
- },
2187
- ".cm-panel input[type=checkbox]": {
2188
- marginRight: "0.4rem !important"
2189
- }
2190
- };
2191
-
2192
- // packages/ui/react-ui-editor/src/extensions/factories.ts
2193
- var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
2194
- var defaults = {
2195
- bracketMatching: true,
2196
- closeBrackets: true,
2197
- drawSelection: true,
2198
- editable: true,
2199
- history: true,
2200
- lineWrapping: true
2201
- };
2202
- var createBasicExtensions = (_props) => {
2203
- const props = defaultsDeep2({}, _props, defaults);
2204
- return [
2205
- // TODO(burdon): Doesn't catch errors in keymap functions.
2206
- EditorView10.exceptionSink.of((err) => {
2207
- log7.catch(err, void 0, {
2208
- F: __dxlog_file9,
2209
- L: 78,
2210
- S: void 0,
2211
- C: (f, a) => f(...a)
2212
- });
2213
- }),
2214
- props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2215
- props.bracketMatching && bracketMatching(),
2216
- props.closeBrackets && closeBrackets(),
2217
- props.crosshairCursor && crosshairCursor(),
2218
- props.dropCursor && dropCursor(),
2219
- props.drawSelection && drawSelection(),
2220
- props.highlightActiveLine && highlightActiveLine(),
2221
- props.history && history(),
2222
- props.lineNumbers && lineNumbers(),
2223
- props.lineWrapping && EditorView10.lineWrapping,
2224
- props.placeholder && placeholder(props.placeholder),
2225
- props.readonly && [
2226
- EditorState.readOnly.of(true),
2227
- EditorView10.editable.of(false)
2228
- ],
2229
- props.scrollPastEnd && scrollPastEnd(),
2230
- props.tabSize && EditorState.tabSize.of(props.tabSize)
2231
- ].filter(isNotFalsy2);
2232
- };
2233
- var defaultSlots = {
2234
- editor: {
2235
- className: "w-full bs-full"
2236
- }
2237
- };
2238
- var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2239
- const slots = defaultsDeep2({}, _slots, defaultSlots);
2240
- return [
2241
- EditorView10.baseTheme(defaultTheme),
2242
- EditorView10.darkTheme.of(themeMode === "dark"),
2243
- theme && EditorView10.theme(theme),
2244
- slots.editor?.className && EditorView10.editorAttributes.of({
2245
- class: slots.editor.className
2246
- }),
2247
- slots.content?.className && EditorView10.contentAttributes.of({
2248
- class: slots.content.className
2249
- })
2250
- ].filter(isNotFalsy2);
2251
- };
2252
- var createDataExtensions = ({ id, text, space, identity }) => {
2253
- const extensions = [
2254
- automerge(text)
2255
- ];
2256
- if (space && identity) {
2257
- const peerId = identity?.identityKey.toHex();
2258
- const { cursorLightValue, cursorDarkValue } = hueTokens[identity?.profile?.data?.hue ?? hexToHue(peerId ?? "0")];
2259
- const awarenessProvider2 = new SpaceAwarenessProvider({
2260
- space,
2261
- channel: `awareness.${id}`,
2262
- peerId: identity.identityKey.toHex(),
2263
- info: {
2264
- displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
2265
- color: cursorDarkValue,
2266
- lightColor: cursorLightValue
2267
- }
2268
- });
2269
- extensions.push(awareness(awarenessProvider2));
2270
- }
2271
- return extensions;
2272
- };
2273
-
2274
- // packages/ui/react-ui-editor/src/extensions/listener.ts
2275
- import { EditorView as EditorView11 } from "@codemirror/view";
2276
- var listener = ({ onFocus, onChange }) => {
2277
- const extensions = [];
2278
- onFocus && extensions.push(EditorView11.focusChangeEffect.of((_, focusing) => {
2279
- onFocus(focusing);
2280
- return null;
2281
- }));
2282
- onChange && extensions.push(EditorView11.updateListener.of((update2) => {
2283
- onChange(update2.state.doc.toString());
2284
- }));
2285
- return extensions;
2286
- };
2287
-
2288
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2289
- import { closeBracketsKeymap } from "@codemirror/autocomplete";
2290
- import { historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2291
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
2292
- import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
2293
- import { languages } from "@codemirror/language-data";
2294
- import { searchKeymap } from "@codemirror/search";
2295
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2296
- import { keymap as keymap5 } from "@codemirror/view";
1851
+ import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
1852
+ import { EditorView as EditorView11, drawSelection, dropCursor, keymap as keymap5, placeholder } from "@codemirror/view";
1853
+ import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
2297
1854
 
2298
1855
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
2299
1856
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -2484,8 +2041,19 @@ var markdownHighlightStyle = (readonly) => {
2484
2041
  };
2485
2042
 
2486
2043
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2487
- var createMarkdownExtensions = ({ themeMode } = {}) => {
2044
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
2488
2045
  return [
2046
+ // TODO(burdon): Reconcile with createBasicExtensions.
2047
+ lineWrapping && EditorView11.lineWrapping,
2048
+ EditorState.allowMultipleSelections.of(true),
2049
+ EditorState.tabSize.of(2),
2050
+ bracketMatching(),
2051
+ closeBrackets(),
2052
+ dropCursor(),
2053
+ drawSelection(),
2054
+ history(),
2055
+ indentOnInput(),
2056
+ _placeholder && placeholder(_placeholder),
2489
2057
  // Main extension.
2490
2058
  // https://github.com/codemirror/lang-markdown
2491
2059
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -2520,7 +2088,7 @@ var createMarkdownExtensions = ({ themeMode } = {}) => {
2520
2088
  // https://codemirror.net/docs/ref/#commands.standardKeymap
2521
2089
  ...standardKeymap
2522
2090
  ])
2523
- ];
2091
+ ].filter(isNotFalsy2);
2524
2092
  };
2525
2093
 
2526
2094
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
@@ -2560,7 +2128,7 @@ var CheckboxWidget = class extends WidgetType3 {
2560
2128
  }
2561
2129
  toDOM(view) {
2562
2130
  const input = document.createElement("input");
2563
- input.className = "cm-task-checkbox ch-checkbox ch-focus-ring -mbs-0.5";
2131
+ input.className = "cm-task-checkbox";
2564
2132
  input.type = "checkbox";
2565
2133
  input.checked = this._checked;
2566
2134
  if (view.state.readOnly) {
@@ -3450,8 +3018,8 @@ var removeList = (type) => ({ state, dispatch }) => {
3450
3018
  return true;
3451
3019
  };
3452
3020
  var toggleList = (type) => (target) => {
3453
- const formatting = getFormatting(target.state);
3454
- const active = formatting.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
3021
+ const formatting2 = getFormatting(target.state);
3022
+ const active = formatting2.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
3455
3023
  return (active ? removeList(type) : addList(type))(target);
3456
3024
  };
3457
3025
  var renumberListItems = (item, counter, changes, doc) => {
@@ -3665,7 +3233,7 @@ var removeCodeblock = ({ state, dispatch }) => {
3665
3233
  var toggleCodeblock = (target) => {
3666
3234
  return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3667
3235
  };
3668
- var formattingKeymap = (options = {}) => {
3236
+ var formatting = (options = {}) => {
3669
3237
  return [
3670
3238
  keymap6.of([
3671
3239
  {
@@ -4046,11 +3614,9 @@ var update = (state, options) => {
4046
3614
  });
4047
3615
  tables.forEach((table2) => {
4048
3616
  const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
4049
- if (hide2) {
4050
- builder.add(table2.from, table2.to, Decoration7.replace({
4051
- widget: new TableWidget(table2)
4052
- }));
4053
- }
3617
+ hide2 && builder.add(table2.from, table2.to, Decoration7.replace({
3618
+ widget: new TableWidget(table2)
3619
+ }));
4054
3620
  builder.add(table2.from, table2.to, Decoration7.mark({
4055
3621
  class: "cm-table"
4056
3622
  }));
@@ -4096,8 +3662,8 @@ var TableWidget = class extends WidgetType5 {
4096
3662
 
4097
3663
  // packages/ui/react-ui-editor/src/extensions/mention.ts
4098
3664
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
4099
- import { log as log8 } from "@dxos/log";
4100
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
3665
+ import { log as log6 } from "@dxos/log";
3666
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4101
3667
  var mention = ({ onSearch }) => {
4102
3668
  return autocompletion2({
4103
3669
  // TODO(burdon): Not working.
@@ -4109,10 +3675,10 @@ var mention = ({ onSearch }) => {
4109
3675
  icons: false,
4110
3676
  override: [
4111
3677
  (context) => {
4112
- log8.info("completion context", {
3678
+ log6.info("completion context", {
4113
3679
  context
4114
3680
  }, {
4115
- F: __dxlog_file10,
3681
+ F: __dxlog_file7,
4116
3682
  L: 26,
4117
3683
  S: void 0,
4118
3684
  C: (f, a) => f(...a)
@@ -4161,6 +3727,11 @@ var EditorModes = {
4161
3727
  ]
4162
3728
  };
4163
3729
 
3730
+ // packages/ui/react-ui-editor/src/extensions/outliner.ts
3731
+ var outliner = (options = {}) => {
3732
+ return [];
3733
+ };
3734
+
4164
3735
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
4165
3736
  import { keymap as keymap8 } from "@codemirror/view";
4166
3737
  var defaultItems = [
@@ -4218,63 +3789,728 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4218
3789
  ];
4219
3790
  };
4220
3791
 
4221
- // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4222
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4223
- var instanceCount = 0;
4224
- var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView16.scrollIntoView(0), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4225
- const [instanceId] = useState2(() => `text-editor-${++instanceCount}`);
4226
- const tabsterDOMAttribute = useFocusableGroup({
4227
- tabBehavior: "limited"
4228
- });
4229
- const rootRef = useRef(null);
4230
- const [view, setView] = useState2(null);
4231
- useImperativeHandle(forwardedRef, () => view, [
4232
- view
4233
- ]);
4234
- useEffect2(() => {
4235
- if (autoFocus) {
4236
- view?.focus();
4237
- }
4238
- }, [
4239
- view,
4240
- autoFocus
4241
- ]);
4242
- useEffect2(() => {
4243
- log9("updating", {
4244
- id,
4245
- instanceId
4246
- }, {
4247
- F: __dxlog_file11,
4248
- L: 88,
4249
- S: void 0,
4250
- C: (f, a) => f(...a)
3792
+ // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
3793
+ import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
3794
+ import { Context as Context2 } from "@dxos/context";
3795
+ import { invariant as invariant2 } from "@dxos/invariant";
3796
+ import { log as log7 } from "@dxos/log";
3797
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
3798
+ var DEBOUNCE_INTERVAL = 100;
3799
+ var SpaceAwarenessProvider = class {
3800
+ constructor(params) {
3801
+ this._remoteStates = /* @__PURE__ */ new Map();
3802
+ this.remoteStateChange = new Event2();
3803
+ this._space = params.space;
3804
+ this._channel = params.channel;
3805
+ this._peerId = params.peerId;
3806
+ this._info = params.info;
3807
+ }
3808
+ open() {
3809
+ this._ctx = new Context2();
3810
+ this._postTask = new DeferredTask(this._ctx, async () => {
3811
+ if (this._localState) {
3812
+ await this._space.postMessage(this._channel, {
3813
+ kind: "post",
3814
+ state: this._localState
3815
+ });
3816
+ await sleep(DEBOUNCE_INTERVAL);
3817
+ }
4251
3818
  });
4252
- const state = EditorState2.create({
4253
- doc,
4254
- selection,
4255
- extensions: [
4256
- id && documentId2.of(id),
4257
- // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4258
- EditorView16.exceptionSink.of((err) => {
4259
- log9.catch(err, void 0, {
4260
- F: __dxlog_file11,
4261
- L: 101,
3819
+ this._ctx.onDispose(this._space.listen(this._channel, (message) => {
3820
+ switch (message.payload.kind) {
3821
+ case "query": {
3822
+ this._handleQueryMessage();
3823
+ break;
3824
+ }
3825
+ case "post": {
3826
+ this._handlePostMessage(message.payload);
3827
+ break;
3828
+ }
3829
+ }
3830
+ }));
3831
+ void this._space.postMessage(this._channel, {
3832
+ kind: "query"
3833
+ }).catch((err) => {
3834
+ log7.debug("failed to query awareness", {
3835
+ err
3836
+ }, {
3837
+ F: __dxlog_file8,
3838
+ L: 89,
3839
+ S: this,
3840
+ C: (f, a) => f(...a)
3841
+ });
3842
+ });
3843
+ }
3844
+ close() {
3845
+ void this._ctx?.dispose();
3846
+ this._ctx = void 0;
3847
+ this._postTask = void 0;
3848
+ }
3849
+ getRemoteStates() {
3850
+ return Array.from(this._remoteStates.values());
3851
+ }
3852
+ update(position) {
3853
+ invariant2(this._postTask, void 0, {
3854
+ F: __dxlog_file8,
3855
+ L: 104,
3856
+ S: this,
3857
+ A: [
3858
+ "this._postTask",
3859
+ ""
3860
+ ]
3861
+ });
3862
+ this._localState = {
3863
+ peerId: this._peerId,
3864
+ position,
3865
+ info: this._info
3866
+ };
3867
+ this._postTask.schedule();
3868
+ }
3869
+ _handleQueryMessage() {
3870
+ invariant2(this._postTask, void 0, {
3871
+ F: __dxlog_file8,
3872
+ L: 115,
3873
+ S: this,
3874
+ A: [
3875
+ "this._postTask",
3876
+ ""
3877
+ ]
3878
+ });
3879
+ this._postTask.schedule();
3880
+ }
3881
+ _handlePostMessage(message) {
3882
+ invariant2(message.kind === "post", void 0, {
3883
+ F: __dxlog_file8,
3884
+ L: 120,
3885
+ S: this,
3886
+ A: [
3887
+ "message.kind === 'post'",
3888
+ ""
3889
+ ]
3890
+ });
3891
+ this._remoteStates.set(message.state.peerId, message.state);
3892
+ this.remoteStateChange.emit();
3893
+ }
3894
+ };
3895
+
3896
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
3897
+ var useActionHandler = (view) => {
3898
+ return (action) => {
3899
+ if (!view) {
3900
+ return;
3901
+ }
3902
+ let inlineType, listType;
3903
+ switch (action.type) {
3904
+ case "heading":
3905
+ setHeading(parseInt(action.data))(view);
3906
+ break;
3907
+ case "strong":
3908
+ case "emphasis":
3909
+ case "strikethrough":
3910
+ case "code":
3911
+ inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
3912
+ (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
3913
+ break;
3914
+ case "list-ordered":
3915
+ case "list-bullet":
3916
+ case "list-task":
3917
+ listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
3918
+ (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
3919
+ break;
3920
+ case "blockquote":
3921
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
3922
+ break;
3923
+ case "codeblock":
3924
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
3925
+ break;
3926
+ case "table":
3927
+ insertTable(view);
3928
+ break;
3929
+ case "link":
3930
+ (action.data === false ? removeLink : addLink)(view);
3931
+ break;
3932
+ case "comment":
3933
+ createComment(view);
3934
+ break;
3935
+ }
3936
+ setTimeout(() => {
3937
+ if (!view.hasFocus) {
3938
+ view.focus();
3939
+ }
3940
+ });
3941
+ };
3942
+ };
3943
+
3944
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
3945
+ import { useRef, useState as useState2 } from "react";
3946
+ var useEditorView = (id) => {
3947
+ const editorRef = useRef(null);
3948
+ const [prev, setPrev] = useState2([
3949
+ null,
3950
+ ""
3951
+ ]);
3952
+ if (prev[0] !== editorRef.current) {
3953
+ setPrev([
3954
+ editorRef.current,
3955
+ id
3956
+ ]);
3957
+ }
3958
+ return [
3959
+ editorRef,
3960
+ prev[1] !== id
3961
+ ];
3962
+ };
3963
+
3964
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3965
+ import { closeBrackets as closeBrackets2 } from "@codemirror/autocomplete";
3966
+ import { history as history2 } from "@codemirror/commands";
3967
+ import { bracketMatching as bracketMatching2 } from "@codemirror/language";
3968
+ import { EditorSelection as EditorSelection2, EditorState as EditorState2 } from "@codemirror/state";
3969
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor as dropCursor2, EditorView as EditorView16, highlightActiveLine, lineNumbers, placeholder as placeholder2, scrollPastEnd } from "@codemirror/view";
3970
+ import defaultsDeep2 from "lodash.defaultsdeep";
3971
+ import { useEffect as useEffect2, useRef as useRef2, useState as useState3 } from "react";
3972
+ import { log as log8 } from "@dxos/log";
3973
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
3974
+
3975
+ // packages/ui/react-ui-editor/src/themes/default.ts
3976
+ import get4 from "lodash.get";
3977
+ var defaultTheme = {
3978
+ //
3979
+ // Main layout:
3980
+ // https://codemirror.net/examples/styling
3981
+ //
3982
+ // <div class="cm-editor [cm-focused] [generated classes]">
3983
+ // <div class="cm-scroller">
3984
+ // <div class="cm-gutters">
3985
+ // <div class="cm-gutter [...]">
3986
+ // <div class="cm-gutterElement">...</div>
3987
+ // </div>
3988
+ // </div>
3989
+ // <div class="cm-content" role="textbox" contenteditable="true">
3990
+ // <div class="cm-line"></div>
3991
+ // </div>
3992
+ // <div class="cm-selectionLayer">
3993
+ // <div class="cm-selectionBackground"></div>
3994
+ // </div>
3995
+ // <div class="cm-cursorLayer">
3996
+ // <div class="cm-cursor"></div>
3997
+ // </div>
3998
+ // </div>
3999
+ // </div>
4000
+ //
4001
+ "&": {},
4002
+ "&.cm-focused": {
4003
+ outline: "none"
4004
+ },
4005
+ // NOTE: See https://codemirror.net/docs/guide (DOM Structure).
4006
+ ".cm-scroller": {
4007
+ // TODO(burdon): Reconcile with docs: https://codemirror.net/docs/guide
4008
+ // Inside of that is the scroller element. If the editor has its own scrollbar, this one should be styled with overflow: auto. But it doesn't have to—the editor also supports growing to accomodate its content, or growing up to a certain max-height and then scrolling.
4009
+ overflowY: "auto",
4010
+ fontFamily: get4(tokens, "fontFamily.mono", []).join(","),
4011
+ lineHeight: 1.5
4012
+ },
4013
+ ".cm-content": {
4014
+ // TODO(burdon): Is it possible to remove the padding value from CM's default theme?
4015
+ // padding: 'unset',
4016
+ // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
4017
+ fontSize: "16px"
4018
+ },
4019
+ "&light .cm-content": {
4020
+ color: get4(tokens, "extend.semanticColors.base.fg.light", "black"),
4021
+ caretColor: "black"
4022
+ },
4023
+ "&dark .cm-content": {
4024
+ color: get4(tokens, "extend.semanticColors.base.fg.dark", "white"),
4025
+ caretColor: "white"
4026
+ },
4027
+ //
4028
+ // Cursor
4029
+ //
4030
+ "&light .cm-cursor, &light .cm-dropCursor": {
4031
+ borderLeft: "2px solid black"
4032
+ },
4033
+ "&dark .cm-cursor, &dark .cm-dropCursor": {
4034
+ borderLeft: "2px solid white"
4035
+ },
4036
+ "&light .cm-placeholder": {
4037
+ color: get4(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
4038
+ },
4039
+ "&dark .cm-placeholder": {
4040
+ color: get4(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
4041
+ },
4042
+ //
4043
+ // line
4044
+ //
4045
+ ".cm-line": {
4046
+ paddingInline: 0
4047
+ },
4048
+ ".cm-activeLine": {
4049
+ background: "transparent"
4050
+ },
4051
+ //
4052
+ // gutter
4053
+ //
4054
+ ".cm-gutterElement": {
4055
+ width: "48px"
4056
+ },
4057
+ //
4058
+ // Selection
4059
+ //
4060
+ "&light .cm-selectionBackground": {
4061
+ background: get4(tokens, "extend.colors.primary.100")
4062
+ },
4063
+ "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
4064
+ background: get4(tokens, "extend.colors.primary.200")
4065
+ },
4066
+ "&dark .cm-selectionBackground": {
4067
+ background: get4(tokens, "extend.colors.primary.700")
4068
+ },
4069
+ "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
4070
+ background: get4(tokens, "extend.colors.primary.600")
4071
+ },
4072
+ //
4073
+ // Search
4074
+ //
4075
+ "&light .cm-searchMatch": {
4076
+ backgroundColor: get4(tokens, "extend.colors.yellow.100")
4077
+ },
4078
+ "&dark .cm-searchMatch": {
4079
+ backgroundColor: get4(tokens, "extend.colors.yellow.700")
4080
+ },
4081
+ //
4082
+ // link
4083
+ //
4084
+ ".cm-link": {
4085
+ color: get4(tokens, "extend.colors.primary.500"),
4086
+ textDecorationLine: "underline",
4087
+ textDecorationThickness: "1px",
4088
+ textUnderlineOffset: "2px",
4089
+ borderRadius: ".125rem",
4090
+ fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4091
+ },
4092
+ //
4093
+ // tooltip
4094
+ //
4095
+ ".cm-tooltip": {
4096
+ border: "none",
4097
+ background: "unset"
4098
+ },
4099
+ ".cm-tooltip-below": {},
4100
+ //
4101
+ // autocomplete
4102
+ //
4103
+ ".cm-tooltip-autocomplete": {
4104
+ marginTop: "4px",
4105
+ marginLeft: "-3px"
4106
+ },
4107
+ ".cm-tooltip-autocomplete ul li": {},
4108
+ ".cm-tooltip-autocomplete ul li[aria-selected]": {},
4109
+ ".cm-completionIcon": {
4110
+ display: "none"
4111
+ },
4112
+ ".cm-completionLabel": {
4113
+ fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4114
+ },
4115
+ ".cm-completionMatchedText": {
4116
+ textDecoration: "none"
4117
+ },
4118
+ //
4119
+ // table
4120
+ //
4121
+ ".cm-table *": {
4122
+ fontFamily: `${get4(tokens, "fontFamily.mono", []).join(",")} !important`,
4123
+ textDecoration: "none !important"
4124
+ },
4125
+ ".cm-table-head": {
4126
+ padding: "2px 16px 2px 0px",
4127
+ borderBottom: `1px solid ${get4(tokens, "extend.colors.neutral.500")}`,
4128
+ fontWeight: 100,
4129
+ textAlign: "left",
4130
+ color: get4(tokens, "extend.colors.neutral.500")
4131
+ },
4132
+ ".cm-table-cell": {
4133
+ padding: "2px 16px 2px 0px"
4134
+ },
4135
+ //
4136
+ // image
4137
+ //
4138
+ ".cm-image": {
4139
+ display: "block",
4140
+ height: "0"
4141
+ },
4142
+ ".cm-image.cm-loaded-image": {
4143
+ height: "auto",
4144
+ borderTop: "0.5rem solid transparent",
4145
+ borderBottom: "0.5rem solid transparent"
4146
+ },
4147
+ //
4148
+ // font size
4149
+ // TODO(thure): This appears to be the best or only way to set selection caret heights,
4150
+ // but it's far more verbose than it needs to be.
4151
+ //
4152
+ // ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
4153
+ // const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
4154
+ //
4155
+ // acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
4156
+ // acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
4157
+ // acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
4158
+ // return acc;
4159
+ // }, {}),
4160
+ /**
4161
+ * Panels
4162
+ * TODO(burdon): Needs styling attention (esp. dark mode).
4163
+ * https://github.com/codemirror/search/blob/main/src/search.ts#L745
4164
+ *
4165
+ * Find/replace panel.
4166
+ * <div class="cm-announced">...</div>
4167
+ * <div class="cm-scroller">...</div>
4168
+ * <div class="cm-panels cm-panels-bottom">
4169
+ * <div class="cm-search cm-panel">
4170
+ * <input class="cm-textfield" />
4171
+ * <button class="cm-button">...</button>
4172
+ * <label><input type="checkbox" />...</label>
4173
+ * </div>
4174
+ * </div
4175
+ */
4176
+ ".cm-panels": {
4177
+ border: `1px solid ${get4(tokens, "extend.colors.neutral.200")}`
4178
+ },
4179
+ ".cm-panel": {
4180
+ background: get4(tokens, "extend.colors.neutral.50"),
4181
+ fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4182
+ },
4183
+ ".cm-button": {
4184
+ margin: "4px",
4185
+ fontFamily: get4(tokens, "fontFamily.body", []).join(","),
4186
+ background: get4(tokens, "extend.colors.neutral.100"),
4187
+ backgroundImage: "none",
4188
+ border: "none",
4189
+ "&:hover": {
4190
+ background: get4(tokens, "extend.colors.neutral.200")
4191
+ },
4192
+ "&:active": {
4193
+ background: get4(tokens, "extend.colors.neutral.300"),
4194
+ backgroundImage: "none"
4195
+ }
4196
+ },
4197
+ ".cm-panel input[type=checkbox]": {
4198
+ marginRight: "0.4rem !important"
4199
+ }
4200
+ };
4201
+ var textTheme = {
4202
+ ".cm-scroller": {
4203
+ fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4204
+ },
4205
+ ".cm-placeholder": {
4206
+ fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4207
+ }
4208
+ };
4209
+ var markdownTheme = {
4210
+ ".cm-scroller": {
4211
+ fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4212
+ },
4213
+ ".cm-placeholder": {
4214
+ fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4215
+ }
4216
+ };
4217
+ var codeTheme = {
4218
+ ".cm-scroller": {
4219
+ fontFamily: get4(tokens, "fontFamily.mono", []).join(",")
4220
+ },
4221
+ ".cm-placeholder": {
4222
+ fontFamily: get4(tokens, "fontFamily.mono", []).join(",")
4223
+ }
4224
+ };
4225
+
4226
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4227
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4228
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}, deps = []) => {
4229
+ const onUpdate = useRef2();
4230
+ const [view, setView] = useState3();
4231
+ const parentRef = useRef2(null);
4232
+ useEffect2(() => {
4233
+ let view2;
4234
+ if (parentRef.current) {
4235
+ const state = EditorState2.create({
4236
+ doc,
4237
+ selection,
4238
+ extensions: [
4239
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4240
+ EditorView16.exceptionSink.of((err) => {
4241
+ log8.catch(err, void 0, {
4242
+ F: __dxlog_file9,
4243
+ L: 70,
4244
+ S: void 0,
4245
+ C: (f, a) => f(...a)
4246
+ });
4247
+ }),
4248
+ extensions,
4249
+ EditorView16.updateListener.of(() => {
4250
+ onUpdate.current?.();
4251
+ })
4252
+ ].filter(isNotFalsy3)
4253
+ });
4254
+ view2 = new EditorView16({
4255
+ parent: parentRef.current,
4256
+ scrollTo,
4257
+ state,
4258
+ // NOTE: Uncomment to debug/monitor all transactions.
4259
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4260
+ dispatchTransactions: (trs, view3) => {
4261
+ if (debug) {
4262
+ logChanges(trs);
4263
+ }
4264
+ view3.update(trs);
4265
+ }
4266
+ });
4267
+ setView(view2);
4268
+ }
4269
+ return () => {
4270
+ view2?.destroy();
4271
+ };
4272
+ }, deps);
4273
+ useEffect2(() => {
4274
+ if (view) {
4275
+ if (!selection && !view.state.selection.main.anchor) {
4276
+ selection = EditorSelection2.single(view.state.doc.line(1).to);
4277
+ }
4278
+ if (selection || scrollTo) {
4279
+ onUpdate.current = () => {
4280
+ onUpdate.current = void 0;
4281
+ view.dispatch({
4282
+ selection,
4283
+ effects: scrollTo && [
4284
+ scrollTo
4285
+ ],
4286
+ scrollIntoView: !scrollTo
4287
+ });
4288
+ };
4289
+ }
4290
+ if (autoFocus) {
4291
+ view.focus();
4292
+ }
4293
+ }
4294
+ }, [
4295
+ view,
4296
+ autoFocus,
4297
+ selection,
4298
+ scrollTo
4299
+ ]);
4300
+ return {
4301
+ parentRef,
4302
+ view
4303
+ };
4304
+ };
4305
+ var defaults = {
4306
+ bracketMatching: true,
4307
+ closeBrackets: true,
4308
+ drawSelection: true,
4309
+ editable: true,
4310
+ history: true,
4311
+ lineWrapping: true
4312
+ };
4313
+ var createBasicExtensions = (_props) => {
4314
+ const props = defaultsDeep2({}, _props, defaults);
4315
+ return [
4316
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4317
+ EditorView16.exceptionSink.of((err) => {
4318
+ log8.catch(err, void 0, {
4319
+ F: __dxlog_file9,
4320
+ L: 167,
4321
+ S: void 0,
4322
+ C: (f, a) => f(...a)
4323
+ });
4324
+ }),
4325
+ props.allowMultipleSelections && EditorState2.allowMultipleSelections.of(true),
4326
+ props.bracketMatching && bracketMatching2(),
4327
+ props.closeBrackets && closeBrackets2(),
4328
+ props.crosshairCursor && crosshairCursor(),
4329
+ props.dropCursor && dropCursor2(),
4330
+ props.drawSelection && drawSelection2(),
4331
+ props.highlightActiveLine && highlightActiveLine(),
4332
+ props.history && history2(),
4333
+ props.lineNumbers && lineNumbers(),
4334
+ props.lineWrapping && EditorView16.lineWrapping,
4335
+ props.placeholder && placeholder2(props.placeholder),
4336
+ props.readonly && [
4337
+ EditorState2.readOnly.of(true),
4338
+ EditorView16.editable.of(false)
4339
+ ],
4340
+ props.scrollPastEnd && scrollPastEnd(),
4341
+ props.tabSize && EditorState2.tabSize.of(props.tabSize)
4342
+ ].filter(isNotFalsy3);
4343
+ };
4344
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4345
+ return [
4346
+ EditorView16.baseTheme(defaultTheme),
4347
+ EditorView16.darkTheme.of(themeMode === "dark"),
4348
+ theme && EditorView16.theme(theme),
4349
+ slots?.editor?.className && EditorView16.editorAttributes.of({
4350
+ class: slots.editor.className
4351
+ }),
4352
+ slots?.content?.className && EditorView16.contentAttributes.of({
4353
+ class: slots.content.className
4354
+ })
4355
+ ].filter(isNotFalsy3);
4356
+ };
4357
+
4358
+ // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
4359
+ import get5 from "lodash.get";
4360
+ import { useState as useState4, useMemo as useMemo2 } from "react";
4361
+ import { generateName } from "@dxos/display-name";
4362
+ import { getRawDoc } from "@dxos/echo-schema";
4363
+ import { invariant as invariant3 } from "@dxos/invariant";
4364
+ import { isAutomergeObject } from "@dxos/react-client/echo";
4365
+ import { hueTokens } from "@dxos/react-ui-theme";
4366
+ import { hexToHue } from "@dxos/util";
4367
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
4368
+ var useTextExtensions = ({ id, text, space, identity }) => {
4369
+ const extensions = [
4370
+ automerge(text)
4371
+ ];
4372
+ const peerId = identity?.identityKey.toHex();
4373
+ const { cursorLightValue, cursorDarkValue } = hueTokens[identity?.profile?.data?.hue ?? hexToHue(peerId ?? "0")];
4374
+ if (space && identity) {
4375
+ const awarenessProvider2 = new SpaceAwarenessProvider({
4376
+ space,
4377
+ channel: `awareness.${id}`,
4378
+ peerId: identity.identityKey.toHex(),
4379
+ info: {
4380
+ displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
4381
+ color: cursorDarkValue,
4382
+ lightColor: cursorLightValue
4383
+ }
4384
+ });
4385
+ extensions.push(awareness(awarenessProvider2));
4386
+ }
4387
+ return extensions;
4388
+ };
4389
+ var useTextModel = (props) => useMemo2(() => createModel(props), Object.values(props));
4390
+ var useInMemoryTextModel = ({ id, defaultContent }) => {
4391
+ const [content, setContent] = useState4(defaultContent ?? "");
4392
+ return {
4393
+ id,
4394
+ content,
4395
+ text: () => content,
4396
+ setContent
4397
+ };
4398
+ };
4399
+ var createModel = ({ space, identity, text }) => {
4400
+ if (!text) {
4401
+ return void 0;
4402
+ }
4403
+ invariant3(isAutomergeObject(text), void 0, {
4404
+ F: __dxlog_file10,
4405
+ L: 89,
4406
+ S: void 0,
4407
+ A: [
4408
+ "isAutomergeObject(text)",
4409
+ ""
4410
+ ]
4411
+ });
4412
+ const obj = text;
4413
+ const doc = getRawDoc(obj, [
4414
+ obj.field
4415
+ ]);
4416
+ const peerId = identity?.identityKey.toHex();
4417
+ const { cursorLightValue, cursorDarkValue } = hueTokens[identity?.profile?.data?.hue ?? hexToHue(peerId ?? "0")];
4418
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
4419
+ space,
4420
+ channel: `automerge.awareness.${obj.id}`,
4421
+ info: {
4422
+ displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
4423
+ color: cursorLightValue,
4424
+ lightColor: cursorDarkValue
4425
+ },
4426
+ peerId: identity?.identityKey.toHex() ?? "Anonymous"
4427
+ });
4428
+ const extensions = [
4429
+ automerge({
4430
+ handle: doc.handle,
4431
+ path: doc.path
4432
+ })
4433
+ ];
4434
+ if (awarenessProvider2) {
4435
+ extensions.push(awareness(awarenessProvider2));
4436
+ }
4437
+ return {
4438
+ id: obj.id,
4439
+ content: doc,
4440
+ text: () => get5(doc.handle.docSync(), doc.path),
4441
+ extension: extensions
4442
+ };
4443
+ };
4444
+
4445
+ // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4446
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4447
+ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo = EditorView17.scrollIntoView(0), moveToEndOfLine, selection, theme, slots = defaultSlots, extensions = [], scrollPastEnd: _scrollPastEnd, debug }, forwardedRef) => {
4448
+ const tabsterDOMAttribute = useFocusableGroup({
4449
+ tabBehavior: "limited"
4450
+ });
4451
+ const { themeMode } = useThemeContext();
4452
+ const rootRef = useRef3(null);
4453
+ const [view, setView] = useState5(null);
4454
+ useImperativeHandle(forwardedRef, () => view, [
4455
+ view
4456
+ ]);
4457
+ useEffect3(() => {
4458
+ if (autoFocus) {
4459
+ view?.focus();
4460
+ }
4461
+ }, [
4462
+ view,
4463
+ autoFocus
4464
+ ]);
4465
+ useEffect3(() => {
4466
+ if (!model || !rootRef.current) {
4467
+ return;
4468
+ }
4469
+ const state = EditorState3.create({
4470
+ doc: typeof model.content === "string" ? model.content : DocAccessor.getValue(model.content),
4471
+ selection,
4472
+ extensions: [
4473
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4474
+ EditorView17.exceptionSink.of((err) => {
4475
+ log9.catch(err, void 0, {
4476
+ F: __dxlog_file11,
4477
+ L: 123,
4262
4478
  S: void 0,
4263
4479
  C: (f, a) => f(...a)
4264
4480
  });
4265
4481
  }),
4482
+ // Theme.
4483
+ // TODO(burdon): Make base theme configurable.
4484
+ EditorView17.baseTheme(defaultTheme),
4485
+ theme && EditorView17.theme(theme),
4486
+ EditorView17.darkTheme.of(themeMode === "dark"),
4487
+ slots.editor?.className && EditorView17.editorAttributes.of({
4488
+ class: slots.editor.className
4489
+ }),
4490
+ slots.content?.className && EditorView17.contentAttributes.of({
4491
+ class: slots.content.className
4492
+ }),
4493
+ // NOTE: Assumes default line height.
4494
+ _scrollPastEnd && scrollPastEnd2(),
4266
4495
  // Focus.
4267
- EditorView16.updateListener.of((update2) => {
4496
+ EditorView17.updateListener.of((update2) => {
4268
4497
  update2.transactions.forEach((transaction) => {
4269
4498
  if (transaction.isUserEvent("focus.container")) {
4270
4499
  rootRef.current?.focus();
4271
4500
  }
4272
4501
  });
4273
4502
  }),
4274
- extensions
4275
- ].filter(isNotFalsy3)
4503
+ // State.
4504
+ EditorView17.editable.of(!readonly),
4505
+ EditorState3.readOnly.of(!!readonly),
4506
+ // Storage and replication.
4507
+ // NOTE: This must come before user extensions.
4508
+ model.extension,
4509
+ // Custom.
4510
+ ...extensions
4511
+ ].filter(isNotFalsy4)
4276
4512
  });
4277
- const view2 = new EditorView16({
4513
+ const view2 = new EditorView17({
4278
4514
  state,
4279
4515
  parent: rootRef.current,
4280
4516
  scrollTo,
@@ -4297,16 +4533,18 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4297
4533
  });
4298
4534
  }
4299
4535
  if (state.facet(editorMode).noTabster) {
4300
- rootRef.current?.removeAttribute("data-tabster");
4536
+ rootRef.current.removeAttribute("data-tabster");
4301
4537
  }
4302
4538
  return () => {
4303
4539
  view2?.destroy();
4304
4540
  setView(null);
4305
4541
  };
4306
4542
  }, [
4307
- doc,
4308
- selection,
4309
- extensions
4543
+ rootRef,
4544
+ extensions,
4545
+ model,
4546
+ readonly,
4547
+ themeMode
4310
4548
  ]);
4311
4549
  const handleKeyUp = useCallback((event) => {
4312
4550
  const { key } = event;
@@ -4322,18 +4560,82 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4322
4560
  return /* @__PURE__ */ React.createElement("div", {
4323
4561
  role: "none",
4324
4562
  ref: rootRef,
4563
+ key: model.id,
4325
4564
  tabIndex: 0,
4326
- className,
4327
- "data-testid": dataTestId,
4328
- ...tabsterDOMAttribute,
4329
- onKeyUp: handleKeyUp
4565
+ onKeyUp: handleKeyUp,
4566
+ ...slots.root,
4567
+ ...tabsterDOMAttribute
4568
+ });
4569
+ });
4570
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping = true, theme = textTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4571
+ const { themeMode } = useThemeContext();
4572
+ const updatedSlots = defaultsDeep3({}, slots, defaultTextSlots);
4573
+ const extensions = useMemo3(() => [
4574
+ createBasicExtensions({
4575
+ lineWrapping,
4576
+ placeholder: placeholder3
4577
+ }),
4578
+ createThemeExtensions({
4579
+ themeMode
4580
+ }),
4581
+ ..._extensions ?? []
4582
+ ], [
4583
+ themeMode,
4584
+ placeholder3,
4585
+ lineWrapping,
4586
+ _extensions
4587
+ ]);
4588
+ return /* @__PURE__ */ React.createElement(BaseTextEditor, {
4589
+ ref: forwardedRef,
4590
+ readonly,
4591
+ extensions,
4592
+ theme,
4593
+ slots: updatedSlots,
4594
+ ...props
4595
+ });
4596
+ });
4597
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4598
+ const { themeMode } = useThemeContext();
4599
+ const updatedSlots = defaultsDeep3({}, slots, defaultMarkdownSlots);
4600
+ const extensions = useMemo3(() => [
4601
+ createMarkdownExtensions({
4602
+ themeMode,
4603
+ placeholder: placeholder3
4604
+ }),
4605
+ ..._extensions ?? []
4606
+ ], [
4607
+ themeMode,
4608
+ placeholder3,
4609
+ _extensions
4610
+ ]);
4611
+ return /* @__PURE__ */ React.createElement(BaseTextEditor, {
4612
+ ref: forwardedRef,
4613
+ readonly,
4614
+ extensions,
4615
+ theme,
4616
+ slots: updatedSlots,
4617
+ ...props
4330
4618
  });
4331
4619
  });
4620
+ var defaultSlots = {
4621
+ root: {
4622
+ className: focusRing
4623
+ },
4624
+ editor: {
4625
+ className: "bs-full"
4626
+ }
4627
+ };
4628
+ var defaultTextSlots = {
4629
+ ...defaultSlots
4630
+ };
4631
+ var defaultMarkdownSlots = {
4632
+ ...defaultSlots
4633
+ };
4332
4634
 
4333
4635
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4334
4636
  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";
4335
4637
  import { createContext } from "@radix-ui/react-context";
4336
- import React2, { useRef as useRef2, useState as useState3 } from "react";
4638
+ import React2, { useRef as useRef4, useState as useState6 } from "react";
4337
4639
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4338
4640
  import { getSize } from "@dxos/react-ui-theme";
4339
4641
  var tooltipProps = {
@@ -4391,9 +4693,9 @@ var MarkdownHeading = () => {
4391
4693
  const header = blockType && /heading(\d)/.exec(blockType);
4392
4694
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4393
4695
  const HeadingIcon = HeadingIcons[value ?? "0"];
4394
- const suppressNextTooltip = useRef2(false);
4395
- const [tooltipOpen, setTooltipOpen] = useState3(false);
4396
- const [selectOpen, setSelectOpen] = useState3(false);
4696
+ const suppressNextTooltip = useRef4(false);
4697
+ const [tooltipOpen, setTooltipOpen] = useState6(false);
4698
+ const [selectOpen, setSelectOpen] = useState6(false);
4397
4699
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
4398
4700
  open: tooltipOpen,
4399
4701
  onOpenChange: (nextOpen) => {
@@ -4596,160 +4898,14 @@ var Toolbar = {
4596
4898
  Markdown: MarkdownStandard,
4597
4899
  Extended: MarkdownExtended
4598
4900
  };
4599
-
4600
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4601
- var useActionHandler = (view) => {
4602
- return (action) => {
4603
- if (!view) {
4604
- return;
4605
- }
4606
- let inlineType, listType;
4607
- switch (action.type) {
4608
- case "heading":
4609
- setHeading(parseInt(action.data))(view);
4610
- break;
4611
- case "strong":
4612
- case "emphasis":
4613
- case "strikethrough":
4614
- case "code":
4615
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4616
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4617
- break;
4618
- case "list-ordered":
4619
- case "list-bullet":
4620
- case "list-task":
4621
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4622
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4623
- break;
4624
- case "blockquote":
4625
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4626
- break;
4627
- case "codeblock":
4628
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4629
- break;
4630
- case "table":
4631
- insertTable(view);
4632
- break;
4633
- case "link":
4634
- (action.data === false ? removeLink : addLink)(view);
4635
- break;
4636
- case "comment":
4637
- createComment(view);
4638
- break;
4639
- }
4640
- setTimeout(() => {
4641
- if (!view.hasFocus) {
4642
- view.focus();
4643
- }
4644
- });
4645
- };
4646
- };
4647
-
4648
- // packages/ui/react-ui-editor/src/hooks/useDocAccessor.ts
4649
- import { useMemo as useMemo2 } from "react";
4650
- import { createDocAccessor, getTextContent } from "@dxos/echo-schema";
4651
- var useDocAccessor = (text) => {
4652
- return useMemo2(() => ({
4653
- id: text.id,
4654
- doc: getTextContent(text),
4655
- accessor: createDocAccessor(text)
4656
- }), [
4657
- text
4658
- ]);
4659
- };
4660
-
4661
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4662
- import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4663
- import { EditorView as EditorView17 } from "@codemirror/view";
4664
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4665
- import { log as log10 } from "@dxos/log";
4666
- import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
4667
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4668
- var useTextEditor = (cb = () => ({}), deps = []) => {
4669
- let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
4670
- const onUpdate = useRef3();
4671
- const [view, setView] = useState4();
4672
- const parentRef = useRef3(null);
4673
- useEffect3(() => {
4674
- let view2;
4675
- if (parentRef.current) {
4676
- const state = EditorState3.create({
4677
- doc,
4678
- selection,
4679
- extensions: [
4680
- id && documentId2.of(id),
4681
- // TODO(burdon): Doesn't catch errors in keymap functions.
4682
- EditorView17.exceptionSink.of((err) => {
4683
- log10.catch(err, void 0, {
4684
- F: __dxlog_file12,
4685
- L: 44,
4686
- S: void 0,
4687
- C: (f, a) => f(...a)
4688
- });
4689
- }),
4690
- extensions,
4691
- EditorView17.updateListener.of(() => {
4692
- onUpdate.current?.();
4693
- })
4694
- ].filter(isNotFalsy4)
4695
- });
4696
- view2 = new EditorView17({
4697
- parent: parentRef.current,
4698
- scrollTo,
4699
- state,
4700
- // NOTE: Uncomment to debug/monitor all transactions.
4701
- // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4702
- dispatchTransactions: (trs, view3) => {
4703
- if (debug) {
4704
- logChanges(trs);
4705
- }
4706
- view3.update(trs);
4707
- }
4708
- });
4709
- setView(view2);
4710
- }
4711
- return () => {
4712
- view2?.destroy();
4713
- };
4714
- }, deps);
4715
- useEffect3(() => {
4716
- if (view) {
4717
- if (!selection && !view.state.selection.main.anchor) {
4718
- selection = EditorSelection2.single(view.state.doc.line(1).to);
4719
- }
4720
- if (selection || scrollTo) {
4721
- onUpdate.current = () => {
4722
- onUpdate.current = void 0;
4723
- view.dispatch({
4724
- selection,
4725
- effects: scrollTo && [
4726
- scrollTo
4727
- ],
4728
- scrollIntoView: !scrollTo
4729
- });
4730
- };
4731
- }
4732
- if (autoFocus) {
4733
- view.focus();
4734
- }
4735
- }
4736
- }, [
4737
- view,
4738
- autoFocus,
4739
- selection,
4740
- scrollTo
4741
- ]);
4742
- return {
4743
- parentRef,
4744
- view
4745
- };
4746
- };
4747
4901
  export {
4902
+ BaseTextEditor,
4748
4903
  Cursor,
4749
4904
  Doc,
4750
4905
  EditorModes,
4751
4906
  Inline,
4752
4907
  List,
4908
+ MarkdownEditor,
4753
4909
  RemoteSelectionsDecorator,
4754
4910
  SpaceAwarenessProvider,
4755
4911
  TextEditor,
@@ -4769,26 +4925,29 @@ export {
4769
4925
  awarenessProvider,
4770
4926
  blast,
4771
4927
  callbackWrapper,
4928
+ codeTheme,
4772
4929
  command,
4773
4930
  comments,
4774
4931
  compareFormatting,
4775
4932
  createBasicExtensions,
4776
4933
  createComment,
4777
- createDataExtensions,
4778
4934
  createMarkdownExtensions,
4779
4935
  createThemeExtensions,
4780
4936
  cursorLineMargin,
4937
+ cursorLineMarginFacet,
4781
4938
  decorateMarkdown,
4939
+ defaultMarkdownSlots,
4782
4940
  defaultOptions,
4941
+ defaultSlots,
4942
+ defaultTextSlots,
4783
4943
  defaultTheme,
4784
4944
  dnd,
4785
- documentId2 as documentId,
4786
4945
  editorFillLayoutEditor,
4787
4946
  editorFillLayoutRoot,
4788
4947
  editorMode,
4789
4948
  editorWithToolbarLayout,
4790
4949
  focusComment,
4791
- formattingKeymap,
4950
+ formatting,
4792
4951
  getFormatting,
4793
4952
  getToken,
4794
4953
  image,
@@ -4800,7 +4959,9 @@ export {
4800
4959
  markdownHighlightStyle,
4801
4960
  markdownTags,
4802
4961
  markdownTagsExtensions,
4962
+ markdownTheme,
4803
4963
  mention,
4964
+ outliner,
4804
4965
  removeBlockquote,
4805
4966
  removeCodeblock,
4806
4967
  removeLink,
@@ -4813,6 +4974,7 @@ export {
4813
4974
  setStyle,
4814
4975
  table,
4815
4976
  tags2 as tags,
4977
+ textTheme,
4816
4978
  toggleBlockquote,
4817
4979
  toggleCodeblock,
4818
4980
  toggleEmphasis,
@@ -4825,9 +4987,12 @@ export {
4825
4987
  typewriter,
4826
4988
  useActionHandler,
4827
4989
  useComments,
4828
- useDocAccessor,
4990
+ useEditorView,
4829
4991
  useFormattingState,
4992
+ useInMemoryTextModel,
4830
4993
  useTextEditor,
4994
+ useTextExtensions,
4995
+ useTextModel,
4831
4996
  useToolbarContext
4832
4997
  };
4833
4998
  //# sourceMappingURL=index.mjs.map