@dxos/react-ui-editor 0.4.8-main.0602afb → 0.4.8-main.2d8e727

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 +939 -777
  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 +3 -1
  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 -170
  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,353 +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
- content: {
2238
- className: "!p-2"
2239
- }
2240
- };
2241
- var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2242
- const slots = defaultsDeep2({}, _slots, defaultSlots);
2243
- return [
2244
- EditorView10.baseTheme(defaultTheme),
2245
- EditorView10.darkTheme.of(themeMode === "dark"),
2246
- theme && EditorView10.theme(theme),
2247
- slots.editor?.className && EditorView10.editorAttributes.of({
2248
- class: slots.editor.className
2249
- }),
2250
- slots.content?.className && EditorView10.contentAttributes.of({
2251
- class: slots.content.className
2252
- })
2253
- ].filter(isNotFalsy2);
2254
- };
2255
- var createDataExtensions = ({ id, text, space, identity }) => {
2256
- const extensions = [
2257
- automerge(text)
2258
- ];
2259
- if (space && identity) {
2260
- const peerId = identity?.identityKey.toHex();
2261
- const { cursorLightValue, cursorDarkValue } = hueTokens[identity?.profile?.data?.hue ?? hexToHue(peerId ?? "0")];
2262
- const awarenessProvider2 = new SpaceAwarenessProvider({
2263
- space,
2264
- channel: `awareness.${id}`,
2265
- peerId: identity.identityKey.toHex(),
2266
- info: {
2267
- displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
2268
- color: cursorDarkValue,
2269
- lightColor: cursorLightValue
2270
- }
2271
- });
2272
- extensions.push(awareness(awarenessProvider2));
2273
- }
2274
- return extensions;
2275
- };
2276
-
2277
- // packages/ui/react-ui-editor/src/extensions/listener.ts
2278
- import { EditorView as EditorView11 } from "@codemirror/view";
2279
- var listener = ({ onFocus, onChange }) => {
2280
- const extensions = [];
2281
- onFocus && extensions.push(EditorView11.focusChangeEffect.of((_, focusing) => {
2282
- onFocus(focusing);
2283
- return null;
2284
- }));
2285
- onChange && extensions.push(EditorView11.updateListener.of((update2) => {
2286
- onChange(update2.state.doc.toString());
2287
- }));
2288
- return extensions;
2289
- };
2290
-
2291
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2292
- import { closeBracketsKeymap } from "@codemirror/autocomplete";
2293
- import { historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2294
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
2295
- import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
2296
- import { languages } from "@codemirror/language-data";
2297
- import { searchKeymap } from "@codemirror/search";
2298
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2299
- 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";
2300
1854
 
2301
1855
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
2302
1856
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -2487,8 +2041,19 @@ var markdownHighlightStyle = (readonly) => {
2487
2041
  };
2488
2042
 
2489
2043
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2490
- var createMarkdownExtensions = ({ themeMode } = {}) => {
2044
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
2491
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),
2492
2057
  // Main extension.
2493
2058
  // https://github.com/codemirror/lang-markdown
2494
2059
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -2523,7 +2088,7 @@ var createMarkdownExtensions = ({ themeMode } = {}) => {
2523
2088
  // https://codemirror.net/docs/ref/#commands.standardKeymap
2524
2089
  ...standardKeymap
2525
2090
  ])
2526
- ];
2091
+ ].filter(isNotFalsy2);
2527
2092
  };
2528
2093
 
2529
2094
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
@@ -3453,8 +3018,8 @@ var removeList = (type) => ({ state, dispatch }) => {
3453
3018
  return true;
3454
3019
  };
3455
3020
  var toggleList = (type) => (target) => {
3456
- const formatting = getFormatting(target.state);
3457
- 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");
3458
3023
  return (active ? removeList(type) : addList(type))(target);
3459
3024
  };
3460
3025
  var renumberListItems = (item, counter, changes, doc) => {
@@ -3668,7 +3233,7 @@ var removeCodeblock = ({ state, dispatch }) => {
3668
3233
  var toggleCodeblock = (target) => {
3669
3234
  return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3670
3235
  };
3671
- var formattingKeymap = (options = {}) => {
3236
+ var formatting = (options = {}) => {
3672
3237
  return [
3673
3238
  keymap6.of([
3674
3239
  {
@@ -4049,11 +3614,9 @@ var update = (state, options) => {
4049
3614
  });
4050
3615
  tables.forEach((table2) => {
4051
3616
  const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
4052
- if (hide2) {
4053
- builder.add(table2.from, table2.to, Decoration7.replace({
4054
- widget: new TableWidget(table2)
4055
- }));
4056
- }
3617
+ hide2 && builder.add(table2.from, table2.to, Decoration7.replace({
3618
+ widget: new TableWidget(table2)
3619
+ }));
4057
3620
  builder.add(table2.from, table2.to, Decoration7.mark({
4058
3621
  class: "cm-table"
4059
3622
  }));
@@ -4099,8 +3662,8 @@ var TableWidget = class extends WidgetType5 {
4099
3662
 
4100
3663
  // packages/ui/react-ui-editor/src/extensions/mention.ts
4101
3664
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
4102
- import { log as log8 } from "@dxos/log";
4103
- 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";
4104
3667
  var mention = ({ onSearch }) => {
4105
3668
  return autocompletion2({
4106
3669
  // TODO(burdon): Not working.
@@ -4112,10 +3675,10 @@ var mention = ({ onSearch }) => {
4112
3675
  icons: false,
4113
3676
  override: [
4114
3677
  (context) => {
4115
- log8.info("completion context", {
3678
+ log6.info("completion context", {
4116
3679
  context
4117
3680
  }, {
4118
- F: __dxlog_file10,
3681
+ F: __dxlog_file7,
4119
3682
  L: 26,
4120
3683
  S: void 0,
4121
3684
  C: (f, a) => f(...a)
@@ -4164,6 +3727,11 @@ var EditorModes = {
4164
3727
  ]
4165
3728
  };
4166
3729
 
3730
+ // packages/ui/react-ui-editor/src/extensions/outliner.ts
3731
+ var outliner = (options = {}) => {
3732
+ return [];
3733
+ };
3734
+
4167
3735
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
4168
3736
  import { keymap as keymap8 } from "@codemirror/view";
4169
3737
  var defaultItems = [
@@ -4221,63 +3789,728 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4221
3789
  ];
4222
3790
  };
4223
3791
 
4224
- // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4225
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4226
- var instanceCount = 0;
4227
- var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView16.scrollIntoView(0), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4228
- const [instanceId] = useState2(() => `text-editor-${++instanceCount}`);
4229
- const tabsterDOMAttribute = useFocusableGroup({
4230
- tabBehavior: "limited"
4231
- });
4232
- const rootRef = useRef(null);
4233
- const [view, setView] = useState2(null);
4234
- useImperativeHandle(forwardedRef, () => view, [
4235
- view
4236
- ]);
4237
- useEffect2(() => {
4238
- if (autoFocus) {
4239
- view?.focus();
4240
- }
4241
- }, [
4242
- view,
4243
- autoFocus
4244
- ]);
4245
- useEffect2(() => {
4246
- log9("updating", {
4247
- id,
4248
- instanceId
4249
- }, {
4250
- F: __dxlog_file11,
4251
- L: 88,
4252
- S: void 0,
4253
- 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
+ }
4254
3818
  });
4255
- const state = EditorState2.create({
4256
- doc,
4257
- selection,
4258
- extensions: [
4259
- id && documentId2.of(id),
4260
- // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4261
- EditorView16.exceptionSink.of((err) => {
4262
- log9.catch(err, void 0, {
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, {
4263
4476
  F: __dxlog_file11,
4264
- L: 101,
4477
+ L: 123,
4265
4478
  S: void 0,
4266
4479
  C: (f, a) => f(...a)
4267
4480
  });
4268
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(),
4269
4495
  // Focus.
4270
- EditorView16.updateListener.of((update2) => {
4496
+ EditorView17.updateListener.of((update2) => {
4271
4497
  update2.transactions.forEach((transaction) => {
4272
4498
  if (transaction.isUserEvent("focus.container")) {
4273
4499
  rootRef.current?.focus();
4274
4500
  }
4275
4501
  });
4276
4502
  }),
4277
- extensions
4278
- ].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)
4279
4512
  });
4280
- const view2 = new EditorView16({
4513
+ const view2 = new EditorView17({
4281
4514
  state,
4282
4515
  parent: rootRef.current,
4283
4516
  scrollTo,
@@ -4300,16 +4533,18 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4300
4533
  });
4301
4534
  }
4302
4535
  if (state.facet(editorMode).noTabster) {
4303
- rootRef.current?.removeAttribute("data-tabster");
4536
+ rootRef.current.removeAttribute("data-tabster");
4304
4537
  }
4305
4538
  return () => {
4306
4539
  view2?.destroy();
4307
4540
  setView(null);
4308
4541
  };
4309
4542
  }, [
4310
- doc,
4311
- selection,
4312
- extensions
4543
+ rootRef,
4544
+ extensions,
4545
+ model,
4546
+ readonly,
4547
+ themeMode
4313
4548
  ]);
4314
4549
  const handleKeyUp = useCallback((event) => {
4315
4550
  const { key } = event;
@@ -4325,18 +4560,82 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4325
4560
  return /* @__PURE__ */ React.createElement("div", {
4326
4561
  role: "none",
4327
4562
  ref: rootRef,
4563
+ key: model.id,
4328
4564
  tabIndex: 0,
4329
- className,
4330
- "data-testid": dataTestId,
4331
- ...tabsterDOMAttribute,
4332
- 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
4333
4618
  });
4334
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
+ };
4335
4634
 
4336
4635
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4337
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";
4338
4637
  import { createContext } from "@radix-ui/react-context";
4339
- import React2, { useRef as useRef2, useState as useState3 } from "react";
4638
+ import React2, { useRef as useRef4, useState as useState6 } from "react";
4340
4639
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4341
4640
  import { getSize } from "@dxos/react-ui-theme";
4342
4641
  var tooltipProps = {
@@ -4394,9 +4693,9 @@ var MarkdownHeading = () => {
4394
4693
  const header = blockType && /heading(\d)/.exec(blockType);
4395
4694
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4396
4695
  const HeadingIcon = HeadingIcons[value ?? "0"];
4397
- const suppressNextTooltip = useRef2(false);
4398
- const [tooltipOpen, setTooltipOpen] = useState3(false);
4399
- const [selectOpen, setSelectOpen] = useState3(false);
4696
+ const suppressNextTooltip = useRef4(false);
4697
+ const [tooltipOpen, setTooltipOpen] = useState6(false);
4698
+ const [selectOpen, setSelectOpen] = useState6(false);
4400
4699
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
4401
4700
  open: tooltipOpen,
4402
4701
  onOpenChange: (nextOpen) => {
@@ -4599,160 +4898,14 @@ var Toolbar = {
4599
4898
  Markdown: MarkdownStandard,
4600
4899
  Extended: MarkdownExtended
4601
4900
  };
4602
-
4603
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4604
- var useActionHandler = (view) => {
4605
- return (action) => {
4606
- if (!view) {
4607
- return;
4608
- }
4609
- let inlineType, listType;
4610
- switch (action.type) {
4611
- case "heading":
4612
- setHeading(parseInt(action.data))(view);
4613
- break;
4614
- case "strong":
4615
- case "emphasis":
4616
- case "strikethrough":
4617
- case "code":
4618
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4619
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4620
- break;
4621
- case "list-ordered":
4622
- case "list-bullet":
4623
- case "list-task":
4624
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4625
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4626
- break;
4627
- case "blockquote":
4628
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4629
- break;
4630
- case "codeblock":
4631
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4632
- break;
4633
- case "table":
4634
- insertTable(view);
4635
- break;
4636
- case "link":
4637
- (action.data === false ? removeLink : addLink)(view);
4638
- break;
4639
- case "comment":
4640
- createComment(view);
4641
- break;
4642
- }
4643
- setTimeout(() => {
4644
- if (!view.hasFocus) {
4645
- view.focus();
4646
- }
4647
- });
4648
- };
4649
- };
4650
-
4651
- // packages/ui/react-ui-editor/src/hooks/useDocAccessor.ts
4652
- import { useMemo as useMemo2 } from "react";
4653
- import { createDocAccessor, getTextContent } from "@dxos/echo-schema";
4654
- var useDocAccessor = (text) => {
4655
- return useMemo2(() => ({
4656
- id: text.id,
4657
- doc: getTextContent(text),
4658
- accessor: createDocAccessor(text)
4659
- }), [
4660
- text
4661
- ]);
4662
- };
4663
-
4664
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4665
- import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4666
- import { EditorView as EditorView17 } from "@codemirror/view";
4667
- import { useEffect as useEffect3, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4668
- import { log as log10 } from "@dxos/log";
4669
- import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
4670
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4671
- var useTextEditor = (cb = () => ({}), deps = []) => {
4672
- let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
4673
- const onUpdate = useRef3();
4674
- const [view, setView] = useState4();
4675
- const parentRef = useRef3(null);
4676
- useEffect3(() => {
4677
- let view2;
4678
- if (parentRef.current) {
4679
- const state = EditorState3.create({
4680
- doc,
4681
- selection,
4682
- extensions: [
4683
- id && documentId2.of(id),
4684
- // TODO(burdon): Doesn't catch errors in keymap functions.
4685
- EditorView17.exceptionSink.of((err) => {
4686
- log10.catch(err, void 0, {
4687
- F: __dxlog_file12,
4688
- L: 44,
4689
- S: void 0,
4690
- C: (f, a) => f(...a)
4691
- });
4692
- }),
4693
- extensions,
4694
- EditorView17.updateListener.of(() => {
4695
- onUpdate.current?.();
4696
- })
4697
- ].filter(isNotFalsy4)
4698
- });
4699
- view2 = new EditorView17({
4700
- parent: parentRef.current,
4701
- scrollTo,
4702
- state,
4703
- // NOTE: Uncomment to debug/monitor all transactions.
4704
- // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4705
- dispatchTransactions: (trs, view3) => {
4706
- if (debug) {
4707
- logChanges(trs);
4708
- }
4709
- view3.update(trs);
4710
- }
4711
- });
4712
- setView(view2);
4713
- }
4714
- return () => {
4715
- view2?.destroy();
4716
- };
4717
- }, deps);
4718
- useEffect3(() => {
4719
- if (view) {
4720
- if (!selection && !view.state.selection.main.anchor) {
4721
- selection = EditorSelection2.single(view.state.doc.line(1).to);
4722
- }
4723
- if (selection || scrollTo) {
4724
- onUpdate.current = () => {
4725
- onUpdate.current = void 0;
4726
- view.dispatch({
4727
- selection,
4728
- effects: scrollTo && [
4729
- scrollTo
4730
- ],
4731
- scrollIntoView: !scrollTo
4732
- });
4733
- };
4734
- }
4735
- if (autoFocus) {
4736
- view.focus();
4737
- }
4738
- }
4739
- }, [
4740
- view,
4741
- autoFocus,
4742
- selection,
4743
- scrollTo
4744
- ]);
4745
- return {
4746
- parentRef,
4747
- view
4748
- };
4749
- };
4750
4901
  export {
4902
+ BaseTextEditor,
4751
4903
  Cursor,
4752
4904
  Doc,
4753
4905
  EditorModes,
4754
4906
  Inline,
4755
4907
  List,
4908
+ MarkdownEditor,
4756
4909
  RemoteSelectionsDecorator,
4757
4910
  SpaceAwarenessProvider,
4758
4911
  TextEditor,
@@ -4772,26 +4925,29 @@ export {
4772
4925
  awarenessProvider,
4773
4926
  blast,
4774
4927
  callbackWrapper,
4928
+ codeTheme,
4775
4929
  command,
4776
4930
  comments,
4777
4931
  compareFormatting,
4778
4932
  createBasicExtensions,
4779
4933
  createComment,
4780
- createDataExtensions,
4781
4934
  createMarkdownExtensions,
4782
4935
  createThemeExtensions,
4783
4936
  cursorLineMargin,
4937
+ cursorLineMarginFacet,
4784
4938
  decorateMarkdown,
4939
+ defaultMarkdownSlots,
4785
4940
  defaultOptions,
4941
+ defaultSlots,
4942
+ defaultTextSlots,
4786
4943
  defaultTheme,
4787
4944
  dnd,
4788
- documentId2 as documentId,
4789
4945
  editorFillLayoutEditor,
4790
4946
  editorFillLayoutRoot,
4791
4947
  editorMode,
4792
4948
  editorWithToolbarLayout,
4793
4949
  focusComment,
4794
- formattingKeymap,
4950
+ formatting,
4795
4951
  getFormatting,
4796
4952
  getToken,
4797
4953
  image,
@@ -4803,7 +4959,9 @@ export {
4803
4959
  markdownHighlightStyle,
4804
4960
  markdownTags,
4805
4961
  markdownTagsExtensions,
4962
+ markdownTheme,
4806
4963
  mention,
4964
+ outliner,
4807
4965
  removeBlockquote,
4808
4966
  removeCodeblock,
4809
4967
  removeLink,
@@ -4816,6 +4974,7 @@ export {
4816
4974
  setStyle,
4817
4975
  table,
4818
4976
  tags2 as tags,
4977
+ textTheme,
4819
4978
  toggleBlockquote,
4820
4979
  toggleCodeblock,
4821
4980
  toggleEmphasis,
@@ -4828,9 +4987,12 @@ export {
4828
4987
  typewriter,
4829
4988
  useActionHandler,
4830
4989
  useComments,
4831
- useDocAccessor,
4990
+ useEditorView,
4832
4991
  useFormattingState,
4992
+ useInMemoryTextModel,
4833
4993
  useTextEditor,
4994
+ useTextExtensions,
4995
+ useTextModel,
4834
4996
  useToolbarContext
4835
4997
  };
4836
4998
  //# sourceMappingURL=index.mjs.map