@dxos/react-ui-editor 0.4.7-main.fd27392 → 0.4.7-next.76c1dea

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 (89) hide show
  1. package/dist/lib/browser/index.mjs +976 -737
  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 +9 -0
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  7. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +0 -9
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/TextEditor/index.d.ts +0 -2
  10. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  11. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  13. package/dist/types/src/{components/TextEditor → extensions/automerge}/automerge.stories.d.ts +5 -2
  14. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -0
  15. package/dist/types/src/extensions/command/command.d.ts +10 -0
  16. package/dist/types/src/extensions/command/command.d.ts.map +1 -0
  17. package/dist/types/src/extensions/command/hint.d.ts +7 -0
  18. package/dist/types/src/extensions/command/hint.d.ts.map +1 -0
  19. package/dist/types/src/extensions/command/index.d.ts +2 -0
  20. package/dist/types/src/extensions/command/index.d.ts.map +1 -0
  21. package/dist/types/src/extensions/command/state.d.ts +18 -0
  22. package/dist/types/src/extensions/command/state.d.ts.map +1 -0
  23. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  24. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +1 -1
  25. package/dist/types/src/extensions/dnd.d.ts +3 -0
  26. package/dist/types/src/extensions/dnd.d.ts.map +1 -0
  27. package/dist/types/src/extensions/index.d.ts +2 -1
  28. package/dist/types/src/extensions/index.d.ts.map +1 -1
  29. package/dist/types/src/extensions/listener.d.ts +1 -1
  30. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  31. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  32. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  33. package/dist/types/src/extensions/markdown/decorate.d.ts +3 -0
  34. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  35. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  36. package/dist/types/src/extensions/util/dom.d.ts +15 -0
  37. package/dist/types/src/extensions/util/dom.d.ts.map +1 -0
  38. package/dist/types/src/hooks/defs.d.ts +5 -11
  39. package/dist/types/src/hooks/defs.d.ts.map +1 -1
  40. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -1
  41. package/dist/types/src/hooks/useTextEditor.d.ts +20 -5
  42. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  43. package/dist/types/src/{components/TextEditor/hooks.stories.d.ts → hooks/useTextEditor.stories.d.ts} +2 -3
  44. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -0
  45. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  46. package/dist/types/src/index.d.ts +1 -0
  47. package/dist/types/src/index.d.ts.map +1 -1
  48. package/dist/types/src/styles/index.d.ts +0 -1
  49. package/dist/types/src/styles/index.d.ts.map +1 -1
  50. package/dist/types/src/themes/default.d.ts.map +1 -1
  51. package/package.json +30 -30
  52. package/src/components/TextEditor/MarkdownEditor.stories.tsx +95 -8
  53. package/src/components/TextEditor/TextEditor.stories.tsx +0 -15
  54. package/src/components/TextEditor/TextEditor.tsx +11 -6
  55. package/src/components/TextEditor/index.ts +0 -3
  56. package/src/components/Toolbar/Toolbar.stories.tsx +2 -1
  57. package/src/{components/TextEditor → extensions/automerge}/automerge.stories.tsx +32 -37
  58. package/src/extensions/awareness.ts +3 -3
  59. package/src/extensions/command/command.ts +35 -0
  60. package/src/extensions/command/hint.ts +76 -0
  61. package/src/extensions/command/index.ts +5 -0
  62. package/src/extensions/command/state.ts +106 -0
  63. package/src/extensions/comments.ts +2 -1
  64. package/src/extensions/cursor-line-margin.ts +5 -2
  65. package/src/extensions/dnd.ts +17 -0
  66. package/src/extensions/index.ts +2 -1
  67. package/src/extensions/listener.ts +3 -3
  68. package/src/extensions/markdown/bundle.ts +4 -14
  69. package/src/extensions/markdown/decorate.ts +21 -14
  70. package/src/extensions/mention.ts +3 -1
  71. package/src/extensions/util/dom.ts +36 -0
  72. package/src/hooks/defs.ts +6 -20
  73. package/src/hooks/useActionHandler.ts +11 -11
  74. package/src/hooks/useEditorView.ts +0 -1
  75. package/src/{components/TextEditor/hooks.stories.tsx → hooks/useTextEditor.stories.tsx} +8 -8
  76. package/src/hooks/useTextEditor.ts +69 -14
  77. package/src/hooks/useTextModel.ts +20 -18
  78. package/src/index.ts +2 -0
  79. package/src/styles/index.ts +0 -1
  80. package/src/themes/default.ts +1 -0
  81. package/src/util.ts +1 -1
  82. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +0 -1
  83. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +0 -1
  84. package/dist/types/src/extensions/basic.d.ts +0 -9
  85. package/dist/types/src/extensions/basic.d.ts.map +0 -1
  86. package/dist/types/src/styles/cursors.d.ts +0 -5
  87. package/dist/types/src/styles/cursors.d.ts.map +0 -1
  88. package/src/extensions/basic.ts +0 -41
  89. package/src/styles/cursors.ts +0 -21
@@ -23,20 +23,19 @@ var translations_default = [
23
23
  ];
24
24
 
25
25
  // packages/ui/react-ui-editor/src/index.ts
26
- import { keymap as keymap8 } from "@codemirror/view";
26
+ import { keymap as keymap9 } from "@codemirror/view";
27
+ import { tags as tags2 } from "@lezer/highlight";
27
28
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
28
29
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
29
30
 
30
- // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
31
- import { tags as tags2 } from "@lezer/highlight";
32
-
33
31
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
34
- import { EditorState as EditorState2 } from "@codemirror/state";
35
- import { EditorView as EditorView14, scrollPastEnd } from "@codemirror/view";
32
+ import { EditorState as EditorState3 } from "@codemirror/state";
33
+ import { EditorView as EditorView17, scrollPastEnd as scrollPastEnd2 } from "@codemirror/view";
36
34
  import { useFocusableGroup } from "@fluentui/react-tabster";
37
- import defaultsDeep2 from "lodash.defaultsdeep";
38
- import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2, useMemo as useMemo2 } from "react";
39
- import { log as log3 } from "@dxos/log";
35
+ import defaultsDeep3 from "lodash.defaultsdeep";
36
+ import React, { forwardRef, useCallback, useEffect as useEffect3, useImperativeHandle, useRef as useRef3, useState as useState5, useMemo as useMemo3 } from "react";
37
+ import { DocAccessor } from "@dxos/echo-schema";
38
+ import { log as log9 } from "@dxos/log";
40
39
  import { useThemeContext } from "@dxos/react-ui";
41
40
  import { focusRing } from "@dxos/react-ui-theme";
42
41
  import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
@@ -596,7 +595,7 @@ var RemoteSelectionsDecorator = class {
596
595
  to: startLine.from + startLine.length,
597
596
  value: Decoration2.mark({
598
597
  attributes: {
599
- style: `background-color: ${color}`
598
+ style: `background-color: ${lightColor}`
600
599
  },
601
600
  class: "cm-collab-selection"
602
601
  })
@@ -606,7 +605,7 @@ var RemoteSelectionsDecorator = class {
606
605
  to: end,
607
606
  value: Decoration2.mark({
608
607
  attributes: {
609
- style: `background-color: ${color}`
608
+ style: `background-color: ${lightColor}`
610
609
  },
611
610
  class: "cm-collab-selection"
612
611
  })
@@ -618,7 +617,7 @@ var RemoteSelectionsDecorator = class {
618
617
  to: linePos,
619
618
  value: Decoration2.line({
620
619
  attributes: {
621
- style: `background-color: ${color}`,
620
+ style: `background-color: ${lightColor}`,
622
621
  class: "cm-collab-selectionLine"
623
622
  }
624
623
  })
@@ -733,28 +732,8 @@ var styles2 = EditorView3.baseTheme({
733
732
  }
734
733
  });
735
734
 
736
- // packages/ui/react-ui-editor/src/extensions/basic.ts
737
- import { closeBrackets } from "@codemirror/autocomplete";
738
- import { history } from "@codemirror/commands";
739
- import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
740
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
741
- import { drawSelection, EditorView as EditorView4, highlightActiveLine, placeholder } from "@codemirror/view";
742
- import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
743
- var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true, bracketMatching: _bracketMatching = true } = {}) => [
744
- lineWrapping && EditorView4.lineWrapping,
745
- // https://codemirror.net/docs/ref/#codemirror.minimalSetup
746
- _bracketMatching && bracketMatching(),
747
- closeBrackets(),
748
- drawSelection(),
749
- highlightActiveLine(),
750
- history(),
751
- _placeholder && placeholder(_placeholder),
752
- // https://github.com/codemirror/theme-one-dark
753
- themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
754
- ].filter(isNotFalsy2);
755
-
756
735
  // packages/ui/react-ui-editor/src/extensions/blast.ts
757
- import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
736
+ import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
758
737
  import defaultsDeep from "lodash.defaultsdeep";
759
738
  import { invariant } from "@dxos/invariant";
760
739
  var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
@@ -802,7 +781,7 @@ var blast = (options = defaultOptions) => {
802
781
  };
803
782
  return [
804
783
  // Cursor moved.
805
- EditorView5.updateListener.of((update2) => {
784
+ EditorView4.updateListener.of((update2) => {
806
785
  if (blaster?.node !== update2.view.scrollDOM) {
807
786
  if (blaster) {
808
787
  blaster.destroy();
@@ -1063,52 +1042,232 @@ var random = (min, max) => {
1063
1042
  return min + ~~(Math.random() * (max - min + 1));
1064
1043
  };
1065
1044
 
1066
- // packages/ui/react-ui-editor/src/extensions/comments.ts
1067
- import { invertedEffects } from "@codemirror/commands";
1068
- import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField3 } from "@codemirror/state";
1069
- import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView6 } from "@codemirror/view";
1070
- import sortBy from "lodash.sortby";
1071
- import { useEffect } from "react";
1072
- import { debounce } from "@dxos/async";
1073
- import { nonNullable } from "@dxos/util";
1045
+ // packages/ui/react-ui-editor/src/extensions/command/command.ts
1046
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1074
1047
 
1075
- // packages/ui/react-ui-editor/src/styles/cursors.ts
1076
- import * as random2 from "lib0/random";
1077
- var cursorColors = [
1078
- {
1079
- color: "#30bced",
1080
- light: "#30bced33"
1081
- },
1082
- {
1083
- color: "#6eeb83",
1084
- light: "#6eeb8333"
1085
- },
1086
- {
1087
- color: "#ffbc42",
1088
- light: "#ffbc4233"
1089
- },
1090
- {
1091
- color: "#ecd444",
1092
- light: "#ecd44433"
1093
- },
1094
- {
1095
- color: "#ee6352",
1096
- light: "#ee635233"
1097
- },
1098
- {
1099
- color: "#9ac2c9",
1100
- light: "#9ac2c933"
1048
+ // packages/ui/react-ui-editor/src/extensions/command/hint.ts
1049
+ import { RangeSetBuilder } from "@codemirror/state";
1050
+ import { Decoration as Decoration3, EditorView as EditorView5, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1051
+
1052
+ // packages/ui/react-ui-editor/src/extensions/command/state.ts
1053
+ import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField3 } from "@codemirror/state";
1054
+ import { showTooltip } from "@codemirror/view";
1055
+ var commandConfig = Facet3.define({
1056
+ combine: (providers) => providers[0]
1057
+ });
1058
+ var commandState = StateField3.define({
1059
+ create: () => ({}),
1060
+ update: (state, tr) => {
1061
+ for (const effect of tr.effects) {
1062
+ if (effect.is(closeEffect)) {
1063
+ return {};
1064
+ }
1065
+ if (effect.is(openEffect)) {
1066
+ const options = tr.state.facet(commandConfig);
1067
+ const { pos, fullWidth } = effect.value;
1068
+ const tooltip = {
1069
+ pos,
1070
+ above: false,
1071
+ arrow: false,
1072
+ strictSide: true,
1073
+ create: (view) => {
1074
+ const dom = document.createElement("div");
1075
+ const tooltipView = {
1076
+ dom,
1077
+ mount: (view2) => {
1078
+ if (fullWidth) {
1079
+ const parent = dom.parentElement;
1080
+ const { paddingLeft, paddingRight } = window.getComputedStyle(parent);
1081
+ const widthWithoutPadding = parent.clientWidth - parseFloat(paddingLeft) - parseFloat(paddingRight);
1082
+ dom.style.width = `${widthWithoutPadding}px`;
1083
+ }
1084
+ options.onRender(dom, (action) => {
1085
+ view2.dispatch({
1086
+ effects: closeEffect.of(null)
1087
+ });
1088
+ if (action?.insert?.length) {
1089
+ view2.dispatch({
1090
+ changes: {
1091
+ from: pos,
1092
+ insert: action.insert
1093
+ },
1094
+ selection: {
1095
+ anchor: pos + action.insert.length
1096
+ }
1097
+ });
1098
+ }
1099
+ setTimeout(() => view2.focus());
1100
+ });
1101
+ }
1102
+ };
1103
+ return tooltipView;
1104
+ }
1105
+ };
1106
+ return {
1107
+ tooltip
1108
+ };
1109
+ }
1110
+ }
1111
+ return state;
1101
1112
  },
1113
+ provide: (f) => [
1114
+ showTooltip.from(f, (value) => value.tooltip ?? null)
1115
+ ]
1116
+ });
1117
+ var openEffect = StateEffect2.define();
1118
+ var closeEffect = StateEffect2.define();
1119
+ var openCommand = (view) => {
1120
+ if (view.state.field(commandState, false)) {
1121
+ const selection = view.state.selection.main;
1122
+ const line = view.state.doc.lineAt(selection.from);
1123
+ if (line.from === selection.from && line.from === line.to) {
1124
+ view.dispatch({
1125
+ effects: openEffect.of({
1126
+ pos: selection.anchor,
1127
+ fullWidth: true
1128
+ })
1129
+ });
1130
+ return true;
1131
+ }
1132
+ }
1133
+ return false;
1134
+ };
1135
+ var closeCommand = (view) => {
1136
+ if (view.state.field(commandState, false)) {
1137
+ view.dispatch({
1138
+ effects: closeEffect.of(null)
1139
+ });
1140
+ return true;
1141
+ }
1142
+ return false;
1143
+ };
1144
+ var commandKeyBindings = [
1102
1145
  {
1103
- color: "#8acb88",
1104
- light: "#8acb8833"
1146
+ key: "/",
1147
+ run: openCommand
1105
1148
  },
1106
1149
  {
1107
- color: "#1be7ff",
1108
- light: "#1be7ff33"
1150
+ key: "Escape",
1151
+ run: closeCommand
1109
1152
  }
1110
1153
  ];
1111
- var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
1154
+
1155
+ // packages/ui/react-ui-editor/src/extensions/util/dom.ts
1156
+ var flattenRect = (rect, left) => {
1157
+ const x = left ? rect.left : rect.right;
1158
+ return {
1159
+ left: x,
1160
+ right: x,
1161
+ top: rect.top,
1162
+ bottom: rect.bottom
1163
+ };
1164
+ };
1165
+ var scratchRange;
1166
+ var textRange = (node, from, to = from) => {
1167
+ const range = scratchRange || (scratchRange = document.createRange());
1168
+ range.setEnd(node, to);
1169
+ range.setStart(node, from);
1170
+ return range;
1171
+ };
1172
+ var clientRectsFor = (dom) => {
1173
+ if (dom.nodeType === 3) {
1174
+ return textRange(dom, 0, dom.nodeValue.length).getClientRects();
1175
+ } else if (dom.nodeType === 1) {
1176
+ return dom.getClientRects();
1177
+ } else {
1178
+ return [];
1179
+ }
1180
+ };
1181
+
1182
+ // packages/ui/react-ui-editor/src/extensions/command/hint.ts
1183
+ var CommandHint = class extends WidgetType2 {
1184
+ constructor(content) {
1185
+ super();
1186
+ this.content = content;
1187
+ }
1188
+ toDOM() {
1189
+ const wrap = document.createElement("span");
1190
+ wrap.className = "cm-placeholder";
1191
+ wrap.style.pointerEvents = "none";
1192
+ wrap.appendChild(typeof this.content === "string" ? document.createTextNode(this.content) : this.content);
1193
+ if (typeof this.content === "string") {
1194
+ wrap.setAttribute("aria-label", "placeholder " + this.content);
1195
+ } else {
1196
+ wrap.setAttribute("aria-hidden", "true");
1197
+ }
1198
+ return wrap;
1199
+ }
1200
+ coordsAt(dom) {
1201
+ const rects = dom.firstChild ? clientRectsFor(dom.firstChild) : [];
1202
+ if (!rects.length) {
1203
+ return null;
1204
+ }
1205
+ const style = window.getComputedStyle(dom.parentNode);
1206
+ const rect = flattenRect(rects[0], style.direction !== "rtl");
1207
+ const lineHeight = parseInt(style.lineHeight);
1208
+ if (rect.bottom - rect.top > lineHeight * 1.5) {
1209
+ return {
1210
+ left: rect.left,
1211
+ right: rect.right,
1212
+ top: rect.top,
1213
+ bottom: rect.top + lineHeight
1214
+ };
1215
+ }
1216
+ return rect;
1217
+ }
1218
+ ignoreEvent() {
1219
+ return false;
1220
+ }
1221
+ };
1222
+ var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
1223
+ constructor() {
1224
+ this.deco = Decoration3.none;
1225
+ }
1226
+ update(update2) {
1227
+ const builder = new RangeSetBuilder();
1228
+ const cState = update2.view.state.field(commandState, false);
1229
+ if (!cState?.tooltip) {
1230
+ const selection = update2.view.state.selection.main;
1231
+ const line = update2.view.state.doc.lineAt(selection.from);
1232
+ if (selection.from === selection.to && line.from === line.to) {
1233
+ const hint = onHint();
1234
+ if (hint) {
1235
+ builder.add(selection.from, selection.to, Decoration3.widget({
1236
+ widget: new CommandHint(hint)
1237
+ }));
1238
+ }
1239
+ }
1240
+ }
1241
+ this.deco = builder.finish();
1242
+ }
1243
+ }, {
1244
+ provide: (plugin) => [
1245
+ EditorView5.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
1246
+ ]
1247
+ });
1248
+
1249
+ // packages/ui/react-ui-editor/src/extensions/command/command.ts
1250
+ var command = (options) => {
1251
+ return [
1252
+ commandConfig.of(options),
1253
+ commandState,
1254
+ keymap3.of(commandKeyBindings),
1255
+ hintViewPlugin(options),
1256
+ EditorView6.focusChangeEffect.of((_, focusing) => {
1257
+ return focusing ? closeEffect.of(null) : null;
1258
+ })
1259
+ ];
1260
+ };
1261
+
1262
+ // packages/ui/react-ui-editor/src/extensions/comments.ts
1263
+ import { invertedEffects } from "@codemirror/commands";
1264
+ import { Facet as Facet4, StateEffect as StateEffect3, StateField as StateField4 } from "@codemirror/state";
1265
+ import { hoverTooltip, keymap as keymap4, Decoration as Decoration4, EditorView as EditorView7 } from "@codemirror/view";
1266
+ import sortBy from "lodash.sortby";
1267
+ import { useEffect } from "react";
1268
+ import { debounce } from "@dxos/async";
1269
+ import { log as log3 } from "@dxos/log";
1270
+ import { nonNullable } from "@dxos/util";
1112
1271
 
1113
1272
  // packages/ui/react-ui-editor/src/styles/markdown.ts
1114
1273
  import { mx } from "@dxos/react-ui-theme";
@@ -1175,18 +1334,26 @@ var logChanges = (trs) => {
1175
1334
  return changes2;
1176
1335
  }).filter(Boolean);
1177
1336
  if (changes.length) {
1178
- console.log("changes", changes);
1337
+ log2.info("changes", {
1338
+ changes
1339
+ }, {
1340
+ F: __dxlog_file3,
1341
+ L: 39,
1342
+ S: void 0,
1343
+ C: (f, a) => f(...a)
1344
+ });
1179
1345
  }
1180
1346
  };
1181
1347
 
1182
1348
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1183
- var documentId = Facet3.define({
1349
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1350
+ var documentId = Facet4.define({
1184
1351
  combine: (values) => values[0]
1185
1352
  });
1186
- var setComments = StateEffect2.define();
1187
- var setSelection = StateEffect2.define();
1188
- var setCommentState = StateEffect2.define();
1189
- var commentsState = StateField3.define({
1353
+ var setComments = StateEffect3.define();
1354
+ var setSelection = StateEffect3.define();
1355
+ var setCommentState = StateEffect3.define();
1356
+ var commentsState = StateField4.define({
1190
1357
  create: (state) => ({
1191
1358
  id: state.facet(documentId),
1192
1359
  comments: [],
@@ -1224,7 +1391,7 @@ var commentsState = StateField3.define({
1224
1391
  return value;
1225
1392
  }
1226
1393
  });
1227
- var styles3 = EditorView6.baseTheme({
1394
+ var styles3 = EditorView7.baseTheme({
1228
1395
  "&light .cm-comment, &light .cm-comment-current": {
1229
1396
  mixBlendMode: "darken"
1230
1397
  },
@@ -1246,26 +1413,31 @@ var styles3 = EditorView6.baseTheme({
1246
1413
  backgroundColor: getToken("extend.colors.yellow.950")
1247
1414
  }
1248
1415
  });
1249
- var commentMark = Decoration3.mark({
1416
+ var commentMark = Decoration4.mark({
1250
1417
  class: "cm-comment",
1251
1418
  attributes: {
1252
1419
  "data-testid": "cm-comment"
1253
1420
  }
1254
1421
  });
1255
- var commentCurrentMark = Decoration3.mark({
1422
+ var commentCurrentMark = Decoration4.mark({
1256
1423
  class: "cm-comment-current",
1257
1424
  attributes: {
1258
1425
  "data-testid": "cm-comment"
1259
1426
  }
1260
1427
  });
1261
- var commentsDecorations = EditorView6.decorations.compute([
1428
+ var commentsDecorations = EditorView7.decorations.compute([
1262
1429
  commentsState
1263
1430
  ], (state) => {
1264
1431
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
1265
1432
  const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
1266
1433
  const range = comment.range;
1267
1434
  if (!range || range.from === range.to) {
1268
- console.warn("Invalid range:", range);
1435
+ log3.warn("Invalid range:", range, {
1436
+ F: __dxlog_file4,
1437
+ L: 132,
1438
+ S: void 0,
1439
+ C: (f, a) => f(...a)
1440
+ });
1269
1441
  return void 0;
1270
1442
  }
1271
1443
  if (comment.comment.id === current) {
@@ -1274,7 +1446,7 @@ var commentsDecorations = EditorView6.decorations.compute([
1274
1446
  return commentMark.range(range.from, range.to);
1275
1447
  }
1276
1448
  }).filter(nonNullable);
1277
- return Decoration3.set(decorations);
1449
+ return Decoration4.set(decorations);
1278
1450
  });
1279
1451
  var trackPastedComments = (onUpdate) => {
1280
1452
  let tracked = null;
@@ -1296,7 +1468,7 @@ var trackPastedComments = (onUpdate) => {
1296
1468
  }
1297
1469
  };
1298
1470
  return [
1299
- EditorView6.domEventHandlers({
1471
+ EditorView7.domEventHandlers({
1300
1472
  cut: handleTrack,
1301
1473
  copy: handleTrack
1302
1474
  }),
@@ -1318,7 +1490,7 @@ var trackPastedComments = (onUpdate) => {
1318
1490
  return effects;
1319
1491
  }),
1320
1492
  // Handle paste or the undo of comment deletion.
1321
- EditorView6.updateListener.of((update2) => {
1493
+ EditorView7.updateListener.of((update2) => {
1322
1494
  const restore = [];
1323
1495
  for (let i = 0; i < update2.transactions.length; i++) {
1324
1496
  const tr = update2.transactions[i];
@@ -1374,7 +1546,7 @@ var mapTrackedComment = (comment, changes) => ({
1374
1546
  from: changes.mapPos(comment.from, 1),
1375
1547
  to: changes.mapPos(comment.to, 1)
1376
1548
  });
1377
- var restoreCommentEffect = StateEffect2.define({
1549
+ var restoreCommentEffect = StateEffect3.define({
1378
1550
  map: mapTrackedComment
1379
1551
  });
1380
1552
  var createComment = (view) => {
@@ -1415,7 +1587,7 @@ var createComment = (view) => {
1415
1587
  }
1416
1588
  return false;
1417
1589
  };
1418
- var optionsFacet = Facet3.define({
1590
+ var optionsFacet = Facet4.define({
1419
1591
  combine: (providers) => providers[0]
1420
1592
  });
1421
1593
  var comments = (options = {}) => {
@@ -1430,7 +1602,7 @@ var comments = (options = {}) => {
1430
1602
  //
1431
1603
  // Keymap.
1432
1604
  //
1433
- options.onCreate ? keymap3.of([
1605
+ options.onCreate ? keymap4.of([
1434
1606
  {
1435
1607
  key: shortcut,
1436
1608
  run: callbackWrapper(createComment)
@@ -1469,7 +1641,7 @@ var comments = (options = {}) => {
1469
1641
  //
1470
1642
  // Track deleted ranges and update ranges for decorations.
1471
1643
  //
1472
- EditorView6.updateListener.of(({ view, state, changes }) => {
1644
+ EditorView7.updateListener.of(({ view, state, changes }) => {
1473
1645
  let mod = false;
1474
1646
  const { comments: comments2, ...value } = state.field(commentsState);
1475
1647
  changes.iterChanges((from, to, from2, to2) => {
@@ -1501,7 +1673,7 @@ var comments = (options = {}) => {
1501
1673
  //
1502
1674
  // Track selection/proximity.
1503
1675
  //
1504
- EditorView6.updateListener.of(({ view, state }) => {
1676
+ EditorView7.updateListener.of(({ view, state }) => {
1505
1677
  let min = Infinity;
1506
1678
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
1507
1679
  const { head } = state.selection.main;
@@ -1550,7 +1722,7 @@ var focusComment = (view, id, center = true) => {
1550
1722
  },
1551
1723
  effects: [
1552
1724
  //
1553
- EditorView6.scrollIntoView(range.from, center ? {
1725
+ EditorView7.scrollIntoView(range.from, center ? {
1554
1726
  y: "center"
1555
1727
  } : void 0),
1556
1728
  setSelection.of({
@@ -1578,14 +1750,16 @@ var useComments = (view, id, comments2 = []) => {
1578
1750
  };
1579
1751
 
1580
1752
  // packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts
1581
- import { Facet as Facet4, Transaction } from "@codemirror/state";
1582
- import { EditorView as EditorView7 } from "@codemirror/view";
1583
- var cursorLineMarginFacet = Facet4.define({
1753
+ import { Facet as Facet5, Transaction } from "@codemirror/state";
1754
+ import { EditorView as EditorView8 } from "@codemirror/view";
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({
1584
1758
  combine: (input) => input[0] ?? 3
1585
1759
  });
1586
1760
  var annotation = "cursorLineMargin";
1587
1761
  var lineAtPos = (s, pos) => s.doc.lineAt(pos).number;
1588
- var cursorLineMargin = EditorView7.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1762
+ var cursorLineMargin = EditorView8.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1589
1763
  if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1590
1764
  return;
1591
1765
  }
@@ -1603,7 +1777,12 @@ var cursorLineMargin = EditorView7.updateListener.of(({ transactions, state, sel
1603
1777
  const needsScrollTop = false;
1604
1778
  const needsScrollBot = mainLine >= visBotLine - cursorLineMargin2;
1605
1779
  if (needsScrollTop && needsScrollBot) {
1606
- console.warn("is cursorScrollMargin too large for the editor size?");
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
+ });
1607
1786
  return;
1608
1787
  }
1609
1788
  if (!needsScrollTop && !needsScrollBot) {
@@ -1621,38 +1800,57 @@ var cursorLineMargin = EditorView7.updateListener.of(({ transactions, state, sel
1621
1800
  }
1622
1801
  view.dispatch({
1623
1802
  annotations: Transaction.userEvent.of(annotation),
1624
- effects: EditorView7.scrollIntoView(s.selection.main.head, {
1803
+ effects: EditorView8.scrollIntoView(s.selection.main.head, {
1625
1804
  y: needsScrollTop ? "start" : "end",
1626
1805
  yMargin: view.defaultLineHeight * cursorLineMargin2
1627
1806
  })
1628
1807
  });
1629
1808
  });
1630
1809
 
1810
+ // packages/ui/react-ui-editor/src/extensions/dnd.ts
1811
+ import { EditorView as EditorView9 } from "@codemirror/view";
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";
1814
+ var dnd = () => {
1815
+ return EditorView9.domEventHandlers({
1816
+ drop: (event, view) => {
1817
+ log5.info("domEventHandlersDrop", {
1818
+ event
1819
+ }, {
1820
+ F: __dxlog_file6,
1821
+ L: 14,
1822
+ S: void 0,
1823
+ C: (f, a) => f(...a)
1824
+ });
1825
+ }
1826
+ });
1827
+ };
1828
+
1631
1829
  // packages/ui/react-ui-editor/src/extensions/listener.ts
1632
- import { EditorView as EditorView8 } from "@codemirror/view";
1830
+ import { EditorView as EditorView10 } from "@codemirror/view";
1633
1831
  var listener = ({ onFocus, onChange }) => {
1634
1832
  const extensions = [];
1635
- onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
1636
- onFocus(focused);
1833
+ onFocus && extensions.push(EditorView10.focusChangeEffect.of((_, focusing) => {
1834
+ onFocus(focusing);
1637
1835
  return null;
1638
1836
  }));
1639
- onChange && extensions.push(EditorView8.updateListener.of((update2) => {
1837
+ onChange && extensions.push(EditorView10.updateListener.of((update2) => {
1640
1838
  onChange(update2.state.doc.toString());
1641
1839
  }));
1642
1840
  return extensions;
1643
1841
  };
1644
1842
 
1645
1843
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1646
- import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
1647
- import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1844
+ import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
1845
+ import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1648
1846
  import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
1649
- import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
1847
+ import { bracketMatching, defaultHighlightStyle, indentOnInput, syntaxHighlighting } from "@codemirror/language";
1650
1848
  import { languages } from "@codemirror/language-data";
1651
1849
  import { searchKeymap } from "@codemirror/search";
1652
1850
  import { EditorState } from "@codemirror/state";
1653
- import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1654
- import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1655
- import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
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";
1656
1854
 
1657
1855
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1658
1856
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -1845,19 +2043,17 @@ var markdownHighlightStyle = (readonly) => {
1845
2043
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1846
2044
  var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1847
2045
  return [
1848
- lineWrapping && EditorView9.lineWrapping,
2046
+ // TODO(burdon): Reconcile with createBasicExtensions.
2047
+ lineWrapping && EditorView11.lineWrapping,
1849
2048
  EditorState.allowMultipleSelections.of(true),
1850
2049
  EditorState.tabSize.of(2),
1851
- // https://github.com/codemirror/basic-setup
1852
- bracketMatching2(),
1853
- closeBrackets2(),
1854
- crosshairCursor(),
2050
+ bracketMatching(),
2051
+ closeBrackets(),
1855
2052
  dropCursor(),
1856
- drawSelection2(),
1857
- highlightActiveLine2(),
1858
- history2(),
2053
+ drawSelection(),
2054
+ history(),
1859
2055
  indentOnInput(),
1860
- _placeholder && placeholder2(_placeholder),
2056
+ _placeholder && placeholder(_placeholder),
1861
2057
  // Main extension.
1862
2058
  // https://github.com/codemirror/lang-markdown
1863
2059
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1877,10 +2073,10 @@ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrap
1877
2073
  ]
1878
2074
  }),
1879
2075
  // https://github.com/codemirror/theme-one-dark
1880
- themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
2076
+ themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
1881
2077
  // Custom styles.
1882
- syntaxHighlighting2(markdownHighlightStyle()),
1883
- keymap4.of([
2078
+ syntaxHighlighting(markdownHighlightStyle()),
2079
+ keymap5.of([
1884
2080
  // https://codemirror.net/docs/ref/#commands.indentWithTab
1885
2081
  indentWithTab,
1886
2082
  // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
@@ -1892,22 +2088,22 @@ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrap
1892
2088
  // https://codemirror.net/docs/ref/#commands.standardKeymap
1893
2089
  ...standardKeymap
1894
2090
  ])
1895
- ].filter(isNotFalsy3);
2091
+ ].filter(isNotFalsy2);
1896
2092
  };
1897
2093
 
1898
2094
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
1899
2095
  import { syntaxTree } from "@codemirror/language";
1900
- import { RangeSetBuilder, StateEffect as StateEffect3 } from "@codemirror/state";
1901
- import { EditorView as EditorView10, Decoration as Decoration4, WidgetType as WidgetType2, ViewPlugin as ViewPlugin3 } from "@codemirror/view";
2096
+ import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
2097
+ import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1902
2098
  import { mx as mx2 } from "@dxos/react-ui-theme";
1903
- var HorizontalRuleWidget = class extends WidgetType2 {
2099
+ var HorizontalRuleWidget = class extends WidgetType3 {
1904
2100
  toDOM() {
1905
2101
  const el = document.createElement("span");
1906
2102
  el.className = "cm-hr";
1907
2103
  return el;
1908
2104
  }
1909
2105
  };
1910
- var LinkButton = class extends WidgetType2 {
2106
+ var LinkButton = class extends WidgetType3 {
1911
2107
  constructor(url, render) {
1912
2108
  super();
1913
2109
  this.url = url;
@@ -1922,7 +2118,7 @@ var LinkButton = class extends WidgetType2 {
1922
2118
  return el;
1923
2119
  }
1924
2120
  };
1925
- var CheckboxWidget = class extends WidgetType2 {
2121
+ var CheckboxWidget = class extends WidgetType3 {
1926
2122
  constructor(_checked) {
1927
2123
  super();
1928
2124
  this._checked = _checked;
@@ -1959,28 +2155,28 @@ var CheckboxWidget = class extends WidgetType2 {
1959
2155
  return false;
1960
2156
  }
1961
2157
  };
1962
- var hide = Decoration4.replace({});
1963
- var fencedCodeLine = Decoration4.line({
2158
+ var hide = Decoration5.replace({});
2159
+ var fencedCodeLine = Decoration5.line({
1964
2160
  class: mx2("cm-code cm-codeblock-line")
1965
2161
  });
1966
- var fencedCodeLineFirst = Decoration4.line({
2162
+ var fencedCodeLineFirst = Decoration5.line({
1967
2163
  class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
1968
2164
  });
1969
- var fencedCodeLineLast = Decoration4.line({
2165
+ var fencedCodeLineLast = Decoration5.line({
1970
2166
  class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
1971
2167
  });
1972
- var horizontalRule = Decoration4.replace({
2168
+ var horizontalRule = Decoration5.replace({
1973
2169
  widget: new HorizontalRuleWidget()
1974
2170
  });
1975
- var checkedTask = Decoration4.replace({
2171
+ var checkedTask = Decoration5.replace({
1976
2172
  widget: new CheckboxWidget(true)
1977
2173
  });
1978
- var uncheckedTask = Decoration4.replace({
2174
+ var uncheckedTask = Decoration5.replace({
1979
2175
  widget: new CheckboxWidget(false)
1980
2176
  });
1981
2177
  var editingRange = (state, range, focus) => {
1982
2178
  const { readOnly, selection: { main: { head } } } = state;
1983
- return focus && !readOnly && head >= range.from && head < range.to;
2179
+ return focus && !readOnly && head >= range.from && head <= range.to;
1984
2180
  };
1985
2181
  var MarksByParent = /* @__PURE__ */ new Set([
1986
2182
  "CodeMark",
@@ -1990,8 +2186,8 @@ var MarksByParent = /* @__PURE__ */ new Set([
1990
2186
  "SuperscriptMark"
1991
2187
  ]);
1992
2188
  var buildDecorations = (view, options, focus) => {
1993
- const deco = new RangeSetBuilder();
1994
- const atomicDeco = new RangeSetBuilder();
2189
+ const deco = new RangeSetBuilder2();
2190
+ const atomicDeco = new RangeSetBuilder2();
1995
2191
  const { state } = view;
1996
2192
  for (const { from, to } of view.visibleRanges) {
1997
2193
  syntaxTree(state).iterate({
@@ -2008,7 +2204,7 @@ var buildDecorations = (view, options, focus) => {
2008
2204
  break;
2009
2205
  }
2010
2206
  const first = block.from <= node.from;
2011
- const last = block.to >= node.to;
2207
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
2012
2208
  deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2013
2209
  if (!editing && (first || last)) {
2014
2210
  atomicDeco.add(block.from, block.to, hide);
@@ -2024,7 +2220,7 @@ var buildDecorations = (view, options, focus) => {
2024
2220
  if (!editing) {
2025
2221
  atomicDeco.add(node.from, marks[0].to, hide);
2026
2222
  }
2027
- deco.add(marks[0].to, marks[1].from, Decoration4.mark({
2223
+ deco.add(marks[0].to, marks[1].from, Decoration5.mark({
2028
2224
  tagName: "a",
2029
2225
  attributes: {
2030
2226
  class: "cm-link",
@@ -2034,7 +2230,7 @@ var buildDecorations = (view, options, focus) => {
2034
2230
  }
2035
2231
  }));
2036
2232
  if (!editing) {
2037
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration4.replace({
2233
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
2038
2234
  widget: new LinkButton(url, options.renderLinkButton)
2039
2235
  }) : hide);
2040
2236
  }
@@ -2052,7 +2248,7 @@ var buildDecorations = (view, options, focus) => {
2052
2248
  } else if (node.name === "TaskMarker") {
2053
2249
  if (!editingRange(state, node, focus)) {
2054
2250
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2055
- atomicDeco.add(node.from - 2, node.from - 1, Decoration4.mark({
2251
+ atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
2056
2252
  class: "cm-task"
2057
2253
  }));
2058
2254
  atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
@@ -2070,50 +2266,51 @@ var buildDecorations = (view, options, focus) => {
2070
2266
  atomicDeco: atomicDeco.finish()
2071
2267
  };
2072
2268
  };
2073
- var forceUpdate = StateEffect3.define();
2269
+ var forceUpdate = StateEffect4.define();
2074
2270
  var decorateMarkdown = (options = {}) => {
2075
2271
  return [
2076
- ViewPlugin3.fromClass(class {
2272
+ ViewPlugin4.fromClass(class {
2077
2273
  constructor(view) {
2078
- this.pendingUpdate = -1;
2079
2274
  ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
2080
2275
  }
2081
2276
  update(update2) {
2082
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate)))) {
2277
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
2083
2278
  ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
2084
2279
  this.clearUpdate();
2085
2280
  } else if (update2.selectionSet) {
2086
2281
  this.scheduleUpdate(update2.view);
2087
2282
  }
2088
2283
  }
2089
- clearUpdate() {
2090
- if (this.pendingUpdate > -1) {
2091
- window.clearTimeout(this.pendingUpdate);
2092
- this.pendingUpdate = -1;
2093
- }
2094
- }
2284
+ // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
2285
+ // the cursor will float in space or be in the wrong position after the decoration is applied.
2095
2286
  scheduleUpdate(view) {
2096
2287
  this.clearUpdate();
2097
- this.pendingUpdate = window.setTimeout(() => {
2288
+ this.pendingUpdate = setTimeout(() => {
2098
2289
  view.dispatch({
2099
2290
  effects: forceUpdate.of(null)
2100
2291
  });
2101
- }, options.selectionChangeDelay ?? 400);
2292
+ }, options.selectionChangeDelay);
2293
+ }
2294
+ clearUpdate() {
2295
+ if (this.pendingUpdate) {
2296
+ clearTimeout(this.pendingUpdate);
2297
+ this.pendingUpdate = void 0;
2298
+ }
2102
2299
  }
2103
2300
  destroy() {
2104
2301
  this.clearUpdate();
2105
2302
  }
2106
2303
  }, {
2107
2304
  provide: (plugin) => [
2108
- EditorView10.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration4.none),
2109
- EditorView10.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration4.none),
2110
- EditorView10.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration4.none)
2305
+ EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2306
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2307
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
2111
2308
  ]
2112
2309
  }),
2113
2310
  formattingStyles
2114
2311
  ];
2115
2312
  };
2116
- var formattingStyles = EditorView10.baseTheme({
2313
+ var formattingStyles = EditorView12.baseTheme({
2117
2314
  "& .cm-code": {
2118
2315
  fontFamily: getToken("fontFamily.mono", []).join(",")
2119
2316
  },
@@ -2166,7 +2363,7 @@ var formattingStyles = EditorView10.baseTheme({
2166
2363
  import { snippet } from "@codemirror/autocomplete";
2167
2364
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2168
2365
  import { EditorSelection } from "@codemirror/state";
2169
- import { EditorView as EditorView11, keymap as keymap5 } from "@codemirror/view";
2366
+ import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
2170
2367
  import { useState, useMemo } from "react";
2171
2368
  var compareFormatting = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
2172
2369
  var Inline;
@@ -3038,7 +3235,7 @@ var toggleCodeblock = (target) => {
3038
3235
  };
3039
3236
  var formatting = (options = {}) => {
3040
3237
  return [
3041
- keymap5.of([
3238
+ keymap6.of([
3042
3239
  {
3043
3240
  key: "meta-b",
3044
3241
  run: toggleStrong
@@ -3236,7 +3433,7 @@ var getFormatting = (state) => {
3236
3433
  };
3237
3434
  var useFormattingState = () => {
3238
3435
  const [state, setState] = useState(null);
3239
- const observer = useMemo(() => EditorView11.updateListener.of((update2) => {
3436
+ const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3240
3437
  if (update2.docChanged || update2.selectionSet) {
3241
3438
  const newState = getFormatting(update2.state);
3242
3439
  if (!state || !compareFormatting(state, newState)) {
@@ -3252,12 +3449,12 @@ var useFormattingState = () => {
3252
3449
 
3253
3450
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3254
3451
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3255
- import { StateField as StateField4 } from "@codemirror/state";
3256
- import { Decoration as Decoration5, EditorView as EditorView12, WidgetType as WidgetType3 } from "@codemirror/view";
3452
+ import { StateField as StateField5 } from "@codemirror/state";
3453
+ import { Decoration as Decoration6, EditorView as EditorView14, WidgetType as WidgetType4 } from "@codemirror/view";
3257
3454
  var image = (options = {}) => {
3258
- return StateField4.define({
3455
+ return StateField5.define({
3259
3456
  create: (state) => {
3260
- return Decoration5.set(buildDecorations2(0, state.doc.length, state));
3457
+ return Decoration6.set(buildDecorations2(0, state.doc.length, state));
3261
3458
  },
3262
3459
  update: (value, tr) => {
3263
3460
  if (!tr.docChanged && !tr.selection) {
@@ -3280,7 +3477,7 @@ var image = (options = {}) => {
3280
3477
  add: buildDecorations2(from, to, tr.state)
3281
3478
  });
3282
3479
  },
3283
- provide: (field) => EditorView12.decorations.from(field)
3480
+ provide: (field) => EditorView14.decorations.from(field)
3284
3481
  });
3285
3482
  };
3286
3483
  var preloaded = /* @__PURE__ */ new Set();
@@ -3302,7 +3499,7 @@ var buildDecorations2 = (from, to, state) => {
3302
3499
  const hide2 = state.readOnly || cursor < node.from || cursor > node.to;
3303
3500
  const url = state.sliceDoc(urlNode.from, urlNode.to);
3304
3501
  preloadImage(url);
3305
- decorations.push(Decoration5.replace({
3502
+ decorations.push(Decoration6.replace({
3306
3503
  block: true,
3307
3504
  widget: new ImageWidget(url)
3308
3505
  }).range(hide2 ? node.from : node.to, node.to));
@@ -3314,7 +3511,7 @@ var buildDecorations2 = (from, to, state) => {
3314
3511
  });
3315
3512
  return decorations;
3316
3513
  };
3317
- var ImageWidget = class extends WidgetType3 {
3514
+ var ImageWidget = class extends WidgetType4 {
3318
3515
  constructor(_url) {
3319
3516
  super();
3320
3517
  this._url = _url;
@@ -3367,17 +3564,17 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
3367
3564
 
3368
3565
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
3369
3566
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3370
- import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField5 } from "@codemirror/state";
3371
- import { Decoration as Decoration6, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
3567
+ import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
3568
+ import { Decoration as Decoration7, EditorView as EditorView15, WidgetType as WidgetType5 } from "@codemirror/view";
3372
3569
  var table = (options = {}) => {
3373
- return StateField5.define({
3570
+ return StateField6.define({
3374
3571
  create: (state) => update(state, options),
3375
3572
  update: (_, tr) => update(tr.state, options),
3376
- provide: (field) => EditorView13.decorations.from(field)
3573
+ provide: (field) => EditorView15.decorations.from(field)
3377
3574
  });
3378
3575
  };
3379
3576
  var update = (state, options) => {
3380
- const builder = new RangeSetBuilder2();
3577
+ const builder = new RangeSetBuilder3();
3381
3578
  const cursor = state.selection.main.head;
3382
3579
  const tables = [];
3383
3580
  const getTable = () => tables[tables.length - 1];
@@ -3417,16 +3614,16 @@ var update = (state, options) => {
3417
3614
  });
3418
3615
  tables.forEach((table2) => {
3419
3616
  const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
3420
- hide2 && builder.add(table2.from, table2.to, Decoration6.replace({
3617
+ hide2 && builder.add(table2.from, table2.to, Decoration7.replace({
3421
3618
  widget: new TableWidget(table2)
3422
3619
  }));
3423
- builder.add(table2.from, table2.to, Decoration6.mark({
3620
+ builder.add(table2.from, table2.to, Decoration7.mark({
3424
3621
  class: "cm-table"
3425
3622
  }));
3426
3623
  });
3427
3624
  return builder.finish();
3428
3625
  };
3429
- var TableWidget = class extends WidgetType4 {
3626
+ var TableWidget = class extends WidgetType5 {
3430
3627
  constructor(_table) {
3431
3628
  super();
3432
3629
  this._table = _table;
@@ -3465,6 +3662,8 @@ var TableWidget = class extends WidgetType4 {
3465
3662
 
3466
3663
  // packages/ui/react-ui-editor/src/extensions/mention.ts
3467
3664
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
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";
3468
3667
  var mention = ({ onSearch }) => {
3469
3668
  return autocompletion2({
3470
3669
  // TODO(burdon): Not working.
@@ -3476,7 +3675,14 @@ var mention = ({ onSearch }) => {
3476
3675
  icons: false,
3477
3676
  override: [
3478
3677
  (context) => {
3479
- console.log(context);
3678
+ log6.info("completion context", {
3679
+ context
3680
+ }, {
3681
+ F: __dxlog_file7,
3682
+ L: 26,
3683
+ S: void 0,
3684
+ C: (f, a) => f(...a)
3685
+ });
3480
3686
  const match = context.matchBefore(/@(\w+)?/);
3481
3687
  if (!match || match.from === match.to && !context.explicit) {
3482
3688
  return null;
@@ -3493,10 +3699,10 @@ var mention = ({ onSearch }) => {
3493
3699
  };
3494
3700
 
3495
3701
  // packages/ui/react-ui-editor/src/extensions/modes.ts
3496
- import { Facet as Facet5 } from "@codemirror/state";
3497
- import { keymap as keymap6 } from "@codemirror/view";
3702
+ import { Facet as Facet6 } from "@codemirror/state";
3703
+ import { keymap as keymap7 } from "@codemirror/view";
3498
3704
  import { vim } from "@replit/codemirror-vim";
3499
- var editorMode = Facet5.define({
3705
+ var editorMode = Facet6.define({
3500
3706
  combine: (modes) => modes[0] ?? {}
3501
3707
  });
3502
3708
  var EditorModes = {
@@ -3507,7 +3713,7 @@ var EditorModes = {
3507
3713
  type: "vim",
3508
3714
  noTabster: true
3509
3715
  }),
3510
- keymap6.of([
3716
+ keymap7.of([
3511
3717
  {
3512
3718
  key: "Alt-Escape",
3513
3719
  run: (view) => {
@@ -3527,7 +3733,7 @@ var outliner = (options = {}) => {
3527
3733
  };
3528
3734
 
3529
3735
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
3530
- import { keymap as keymap7 } from "@codemirror/view";
3736
+ import { keymap as keymap8 } from "@codemirror/view";
3531
3737
  var defaultItems = [
3532
3738
  "hello world!",
3533
3739
  "this is a test.",
@@ -3537,7 +3743,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
3537
3743
  let t;
3538
3744
  let idx = 0;
3539
3745
  return [
3540
- keymap7.of([
3746
+ keymap8.of([
3541
3747
  {
3542
3748
  // Reset.
3543
3749
  key: "alt-meta-'",
@@ -3583,161 +3789,345 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
3583
3789
  ];
3584
3790
  };
3585
3791
 
3586
- // packages/ui/react-ui-editor/src/themes/default.ts
3587
- import get4 from "lodash.get";
3588
- var defaultTheme = {
3589
- //
3590
- // Main layout:
3591
- // https://codemirror.net/examples/styling
3592
- //
3593
- // <div class="cm-editor [cm-focused] [generated classes]">
3594
- // <div class="cm-scroller">
3595
- // <div class="cm-gutters">
3596
- // <div class="cm-gutter [...]">
3597
- // <div class="cm-gutterElement">...</div>
3598
- // </div>
3599
- // </div>
3600
- // <div class="cm-content" role="textbox" contenteditable="true">
3601
- // <div class="cm-line"></div>
3602
- // </div>
3603
- // <div class="cm-selectionLayer">
3604
- // <div class="cm-selectionBackground"></div>
3605
- // </div>
3606
- // <div class="cm-cursorLayer">
3607
- // <div class="cm-cursor"></div>
3608
- // </div>
3609
- // </div>
3610
- // </div>
3611
- //
3612
- "&": {},
3613
- "&.cm-focused": {
3614
- outline: "none"
3615
- },
3616
- // NOTE: See https://codemirror.net/docs/guide (DOM Structure).
3617
- ".cm-scroller": {
3618
- // TODO(burdon): Reconcile with docs: https://codemirror.net/docs/guide
3619
- // 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.
3620
- overflowY: "auto",
3621
- fontFamily: get4(tokens, "fontFamily.mono", []).join(","),
3622
- lineHeight: 1.5
3623
- },
3624
- ".cm-content": {
3625
- // TODO(burdon): Is it possible to remove the padding value from CM's default theme?
3626
- // padding: 'unset',
3627
- // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
3628
- fontSize: "16px"
3629
- },
3630
- "&light .cm-content": {
3631
- color: get4(tokens, "extend.semanticColors.base.fg.light", "black"),
3632
- caretColor: "black"
3633
- },
3634
- "&dark .cm-content": {
3635
- color: get4(tokens, "extend.semanticColors.base.fg.dark", "white"),
3636
- caretColor: "white"
3637
- },
3638
- //
3639
- // Cursor
3640
- //
3641
- "&light .cm-cursor, &light .cm-dropCursor": {
3642
- borderLeft: "2px solid black"
3643
- },
3644
- "&dark .cm-cursor, &dark .cm-dropCursor": {
3645
- borderLeft: "2px solid white"
3646
- },
3647
- "&light .cm-placeholder": {
3648
- color: get4(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
3649
- },
3650
- "&dark .cm-placeholder": {
3651
- color: get4(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
3652
- },
3653
- //
3654
- // line
3655
- //
3656
- ".cm-line": {
3657
- paddingInline: 0
3658
- },
3659
- ".cm-activeLine": {
3660
- background: "transparent"
3661
- },
3662
- //
3663
- // gutter
3664
- //
3665
- ".cm-gutterElement": {
3666
- width: "48px"
3667
- },
3668
- //
3669
- // Selection
3670
- //
3671
- "&light .cm-selectionBackground": {
3672
- background: get4(tokens, "extend.colors.primary.100")
3673
- },
3674
- "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
3675
- background: get4(tokens, "extend.colors.primary.200")
3676
- },
3677
- "&dark .cm-selectionBackground": {
3678
- background: get4(tokens, "extend.colors.primary.700")
3679
- },
3680
- "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
3681
- background: get4(tokens, "extend.colors.primary.600")
3682
- },
3683
- //
3684
- // Search
3685
- //
3686
- "&light .cm-searchMatch": {
3687
- backgroundColor: get4(tokens, "extend.colors.yellow.100")
3688
- },
3689
- "&dark .cm-searchMatch": {
3690
- backgroundColor: get4(tokens, "extend.colors.yellow.700")
3691
- },
3692
- //
3693
- // link
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
+ }
3818
+ });
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 = {
3694
3978
  //
3695
- ".cm-link": {
3696
- color: get4(tokens, "extend.colors.primary.500"),
3697
- textDecorationLine: "underline",
3698
- textDecorationThickness: "1px",
3699
- textUnderlineOffset: "2px",
3700
- borderRadius: ".125rem",
3701
- fontFamily: get4(tokens, "fontFamily.body", []).join(",")
3702
- },
3979
+ // Main layout:
3980
+ // https://codemirror.net/examples/styling
3703
3981
  //
3704
- // tooltip
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>
3705
4000
  //
3706
- ".cm-tooltip": {
3707
- border: "none"
4001
+ "&": {},
4002
+ "&.cm-focused": {
4003
+ outline: "none"
3708
4004
  },
3709
- ".cm-tooltip-below": {},
3710
- //
3711
- // autocomplete
3712
- //
3713
- ".cm-tooltip-autocomplete": {
3714
- marginTop: "4px",
3715
- marginLeft: "-3px"
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
3716
4012
  },
3717
- ".cm-tooltip-autocomplete ul li": {},
3718
- ".cm-tooltip-autocomplete ul li[aria-selected]": {},
3719
- ".cm-completionIcon": {
3720
- display: "none"
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"
3721
4018
  },
3722
- ".cm-completionLabel": {
3723
- fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4019
+ "&light .cm-content": {
4020
+ color: get4(tokens, "extend.semanticColors.base.fg.light", "black"),
4021
+ caretColor: "black"
3724
4022
  },
3725
- ".cm-completionMatchedText": {
3726
- textDecoration: "none"
4023
+ "&dark .cm-content": {
4024
+ color: get4(tokens, "extend.semanticColors.base.fg.dark", "white"),
4025
+ caretColor: "white"
3727
4026
  },
3728
4027
  //
3729
- // table
4028
+ // Cursor
3730
4029
  //
3731
- ".cm-table *": {
3732
- fontFamily: `${get4(tokens, "fontFamily.mono", []).join(",")} !important`,
3733
- textDecoration: "none !important"
4030
+ "&light .cm-cursor, &light .cm-dropCursor": {
4031
+ borderLeft: "2px solid black"
3734
4032
  },
3735
- ".cm-table-head": {
3736
- padding: "2px 16px 2px 0px",
3737
- borderBottom: `1px solid ${get4(tokens, "extend.colors.neutral.500")}`,
3738
- fontWeight: 100,
3739
- textAlign: "left",
3740
- color: get4(tokens, "extend.colors.neutral.500")
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")
3741
4131
  },
3742
4132
  ".cm-table-cell": {
3743
4133
  padding: "2px 16px 2px 0px"
@@ -3833,19 +4223,238 @@ var codeTheme = {
3833
4223
  }
3834
4224
  };
3835
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
+
3836
4445
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
3837
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
3838
- var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo = EditorView14.scrollIntoView(0), moveToEndOfLine, selection, theme, slots = defaultSlots, extensions = [], scrollPastEnd: _scrollPastEnd, debug }, forwardedRef) => {
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) => {
3839
4448
  const tabsterDOMAttribute = useFocusableGroup({
3840
4449
  tabBehavior: "limited"
3841
4450
  });
3842
4451
  const { themeMode } = useThemeContext();
3843
- const rootRef = useRef(null);
3844
- const [view, setView] = useState2(null);
4452
+ const rootRef = useRef3(null);
4453
+ const [view, setView] = useState5(null);
3845
4454
  useImperativeHandle(forwardedRef, () => view, [
3846
4455
  view
3847
4456
  ]);
3848
- useEffect2(() => {
4457
+ useEffect3(() => {
3849
4458
  if (autoFocus) {
3850
4459
  view?.focus();
3851
4460
  }
@@ -3853,38 +4462,38 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3853
4462
  view,
3854
4463
  autoFocus
3855
4464
  ]);
3856
- useEffect2(() => {
4465
+ useEffect3(() => {
3857
4466
  if (!model || !rootRef.current) {
3858
4467
  return;
3859
4468
  }
3860
- const state = EditorState2.create({
3861
- doc: model.text(),
4469
+ const state = EditorState3.create({
4470
+ doc: typeof model.content === "string" ? model.content : DocAccessor.getValue(model.content),
3862
4471
  selection,
3863
4472
  extensions: [
3864
4473
  // TODO(burdon): Doesn't catch errors in keymap functions.
3865
- EditorView14.exceptionSink.of((err) => {
3866
- log3.catch(err, void 0, {
3867
- F: __dxlog_file4,
3868
- L: 122,
4474
+ EditorView17.exceptionSink.of((err) => {
4475
+ log9.catch(err, void 0, {
4476
+ F: __dxlog_file11,
4477
+ L: 123,
3869
4478
  S: void 0,
3870
4479
  C: (f, a) => f(...a)
3871
4480
  });
3872
4481
  }),
3873
4482
  // Theme.
3874
4483
  // TODO(burdon): Make base theme configurable.
3875
- EditorView14.baseTheme(defaultTheme),
3876
- theme && EditorView14.theme(theme),
3877
- EditorView14.darkTheme.of(themeMode === "dark"),
3878
- slots.editor?.className && EditorView14.editorAttributes.of({
4484
+ EditorView17.baseTheme(defaultTheme),
4485
+ theme && EditorView17.theme(theme),
4486
+ EditorView17.darkTheme.of(themeMode === "dark"),
4487
+ slots.editor?.className && EditorView17.editorAttributes.of({
3879
4488
  class: slots.editor.className
3880
4489
  }),
3881
- slots.content?.className && EditorView14.contentAttributes.of({
4490
+ slots.content?.className && EditorView17.contentAttributes.of({
3882
4491
  class: slots.content.className
3883
4492
  }),
3884
4493
  // NOTE: Assumes default line height.
3885
- _scrollPastEnd && scrollPastEnd(),
4494
+ _scrollPastEnd && scrollPastEnd2(),
3886
4495
  // Focus.
3887
- EditorView14.updateListener.of((update2) => {
4496
+ EditorView17.updateListener.of((update2) => {
3888
4497
  update2.transactions.forEach((transaction) => {
3889
4498
  if (transaction.isUserEvent("focus.container")) {
3890
4499
  rootRef.current?.focus();
@@ -3892,8 +4501,8 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3892
4501
  });
3893
4502
  }),
3894
4503
  // State.
3895
- EditorView14.editable.of(!readonly),
3896
- EditorState2.readOnly.of(!!readonly),
4504
+ EditorView17.editable.of(!readonly),
4505
+ EditorState3.readOnly.of(!!readonly),
3897
4506
  // Storage and replication.
3898
4507
  // NOTE: This must come before user extensions.
3899
4508
  model.extension,
@@ -3901,7 +4510,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3901
4510
  ...extensions
3902
4511
  ].filter(isNotFalsy4)
3903
4512
  });
3904
- const view2 = new EditorView14({
4513
+ const view2 = new EditorView17({
3905
4514
  state,
3906
4515
  parent: rootRef.current,
3907
4516
  scrollTo,
@@ -3958,14 +4567,16 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3958
4567
  ...tabsterDOMAttribute
3959
4568
  });
3960
4569
  });
3961
- var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4570
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping = true, theme = textTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
3962
4571
  const { themeMode } = useThemeContext();
3963
- const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
3964
- const extensions = useMemo2(() => [
3965
- createBasicBundle({
3966
- themeMode,
3967
- placeholder: placeholder3,
3968
- lineWrapping
4572
+ const updatedSlots = defaultsDeep3({}, slots, defaultTextSlots);
4573
+ const extensions = useMemo3(() => [
4574
+ createBasicExtensions({
4575
+ lineWrapping,
4576
+ placeholder: placeholder3
4577
+ }),
4578
+ createThemeExtensions({
4579
+ themeMode
3969
4580
  }),
3970
4581
  ..._extensions ?? []
3971
4582
  ], [
@@ -3985,8 +4596,8 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
3985
4596
  });
3986
4597
  var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
3987
4598
  const { themeMode } = useThemeContext();
3988
- const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
3989
- const extensions = useMemo2(() => [
4599
+ const updatedSlots = defaultsDeep3({}, slots, defaultMarkdownSlots);
4600
+ const extensions = useMemo3(() => [
3990
4601
  createMarkdownExtensions({
3991
4602
  themeMode,
3992
4603
  placeholder: placeholder3
@@ -4024,7 +4635,7 @@ var defaultMarkdownSlots = {
4024
4635
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4025
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";
4026
4637
  import { createContext } from "@radix-ui/react-context";
4027
- import React2, { useRef as useRef2, useState as useState3 } from "react";
4638
+ import React2, { useRef as useRef4, useState as useState6 } from "react";
4028
4639
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4029
4640
  import { getSize } from "@dxos/react-ui-theme";
4030
4641
  var tooltipProps = {
@@ -4082,9 +4693,9 @@ var MarkdownHeading = () => {
4082
4693
  const header = blockType && /heading(\d)/.exec(blockType);
4083
4694
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4084
4695
  const HeadingIcon = HeadingIcons[value ?? "0"];
4085
- const suppressNextTooltip = useRef2(false);
4086
- const [tooltipOpen, setTooltipOpen] = useState3(false);
4087
- const [selectOpen, setSelectOpen] = useState3(false);
4696
+ const suppressNextTooltip = useRef4(false);
4697
+ const [tooltipOpen, setTooltipOpen] = useState6(false);
4698
+ const [selectOpen, setSelectOpen] = useState6(false);
4088
4699
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
4089
4700
  open: tooltipOpen,
4090
4701
  onOpenChange: (nextOpen) => {
@@ -4287,378 +4898,6 @@ var Toolbar = {
4287
4898
  Markdown: MarkdownStandard,
4288
4899
  Extended: MarkdownExtended
4289
4900
  };
4290
-
4291
- // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
4292
- import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
4293
- import { Context as Context2 } from "@dxos/context";
4294
- import { invariant as invariant2 } from "@dxos/invariant";
4295
- import { log as log4 } from "@dxos/log";
4296
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
4297
- var DEBOUNCE_INTERVAL = 100;
4298
- var SpaceAwarenessProvider = class {
4299
- constructor(params) {
4300
- this._remoteStates = /* @__PURE__ */ new Map();
4301
- this.remoteStateChange = new Event2();
4302
- this._space = params.space;
4303
- this._channel = params.channel;
4304
- this._peerId = params.peerId;
4305
- this._info = params.info;
4306
- }
4307
- open() {
4308
- this._ctx = new Context2();
4309
- this._postTask = new DeferredTask(this._ctx, async () => {
4310
- if (this._localState) {
4311
- await this._space.postMessage(this._channel, {
4312
- kind: "post",
4313
- state: this._localState
4314
- });
4315
- await sleep(DEBOUNCE_INTERVAL);
4316
- }
4317
- });
4318
- this._ctx.onDispose(this._space.listen(this._channel, (message) => {
4319
- switch (message.payload.kind) {
4320
- case "query": {
4321
- this._handleQueryMessage();
4322
- break;
4323
- }
4324
- case "post": {
4325
- this._handlePostMessage(message.payload);
4326
- break;
4327
- }
4328
- }
4329
- }));
4330
- void this._space.postMessage(this._channel, {
4331
- kind: "query"
4332
- }).catch((err) => {
4333
- log4.debug("failed to query awareness", {
4334
- err
4335
- }, {
4336
- F: __dxlog_file5,
4337
- L: 89,
4338
- S: this,
4339
- C: (f, a) => f(...a)
4340
- });
4341
- });
4342
- }
4343
- close() {
4344
- void this._ctx?.dispose();
4345
- this._ctx = void 0;
4346
- this._postTask = void 0;
4347
- }
4348
- getRemoteStates() {
4349
- return Array.from(this._remoteStates.values());
4350
- }
4351
- update(position) {
4352
- invariant2(this._postTask, void 0, {
4353
- F: __dxlog_file5,
4354
- L: 104,
4355
- S: this,
4356
- A: [
4357
- "this._postTask",
4358
- ""
4359
- ]
4360
- });
4361
- this._localState = {
4362
- peerId: this._peerId,
4363
- position,
4364
- info: this._info
4365
- };
4366
- this._postTask.schedule();
4367
- }
4368
- _handleQueryMessage() {
4369
- invariant2(this._postTask, void 0, {
4370
- F: __dxlog_file5,
4371
- L: 115,
4372
- S: this,
4373
- A: [
4374
- "this._postTask",
4375
- ""
4376
- ]
4377
- });
4378
- this._postTask.schedule();
4379
- }
4380
- _handlePostMessage(message) {
4381
- invariant2(message.kind === "post", void 0, {
4382
- F: __dxlog_file5,
4383
- L: 120,
4384
- S: this,
4385
- A: [
4386
- "message.kind === 'post'",
4387
- ""
4388
- ]
4389
- });
4390
- this._remoteStates.set(message.state.peerId, message.state);
4391
- this.remoteStateChange.emit();
4392
- }
4393
- };
4394
-
4395
- // packages/ui/react-ui-editor/src/hooks/defs.ts
4396
- import { StateField as StateField6 } from "@codemirror/state";
4397
- var modelState = StateField6.define({
4398
- create: () => void 0,
4399
- update: (model) => model
4400
- });
4401
-
4402
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4403
- var useActionHandler = (view) => {
4404
- return (action) => {
4405
- if (!view) {
4406
- return;
4407
- }
4408
- let inlineType, listType;
4409
- switch (action.type) {
4410
- case "heading":
4411
- setHeading(parseInt(action.data))(view);
4412
- break;
4413
- case "strong":
4414
- case "emphasis":
4415
- case "strikethrough":
4416
- case "code":
4417
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4418
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4419
- break;
4420
- case "list-ordered":
4421
- case "list-bullet":
4422
- case "list-task":
4423
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4424
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4425
- break;
4426
- case "blockquote":
4427
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4428
- break;
4429
- case "codeblock":
4430
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4431
- break;
4432
- case "table":
4433
- insertTable(view);
4434
- break;
4435
- case "link":
4436
- (action.data === false ? removeLink : addLink)(view);
4437
- break;
4438
- case "comment":
4439
- createComment(view);
4440
- break;
4441
- }
4442
- setTimeout(() => {
4443
- if (!view.hasFocus) {
4444
- view.focus();
4445
- }
4446
- });
4447
- };
4448
- };
4449
-
4450
- // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
4451
- import { useRef as useRef3, useState as useState4 } from "react";
4452
- var useEditorView = (id) => {
4453
- const editorRef = useRef3(null);
4454
- const [prev, setPrev] = useState4([
4455
- null,
4456
- ""
4457
- ]);
4458
- if (prev[0] !== editorRef.current) {
4459
- setPrev([
4460
- editorRef.current,
4461
- id
4462
- ]);
4463
- }
4464
- return [
4465
- editorRef,
4466
- prev[1] !== id
4467
- ];
4468
- };
4469
-
4470
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4471
- import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4472
- import { EditorView as EditorView15 } from "@codemirror/view";
4473
- import { useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
4474
- import { log as log5 } from "@dxos/log";
4475
- import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
4476
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4477
- var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}, deps) => {
4478
- const onUpdate = useRef4();
4479
- const [view, setView] = useState5();
4480
- const parentRef = useRef4(null);
4481
- useEffect3(() => {
4482
- let view2;
4483
- if (parentRef.current) {
4484
- const state = EditorState3.create({
4485
- doc,
4486
- selection,
4487
- extensions: [
4488
- // TODO(burdon): Doesn't catch errors in keymap functions.
4489
- EditorView15.exceptionSink.of((err) => {
4490
- log5.catch(err, void 0, {
4491
- F: __dxlog_file6,
4492
- L: 57,
4493
- S: void 0,
4494
- C: (f, a) => f(...a)
4495
- });
4496
- }),
4497
- extensions,
4498
- EditorView15.updateListener.of(() => {
4499
- onUpdate.current?.();
4500
- })
4501
- ].filter(isNotFalsy5)
4502
- });
4503
- view2 = new EditorView15({
4504
- parent: parentRef.current,
4505
- scrollTo,
4506
- state,
4507
- // NOTE: Uncomment to debug/monitor all transactions.
4508
- // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4509
- dispatchTransactions: (trs, view3) => {
4510
- if (debug) {
4511
- logChanges(trs);
4512
- }
4513
- view3.update(trs);
4514
- }
4515
- });
4516
- setView(view2);
4517
- }
4518
- return () => {
4519
- view2?.destroy();
4520
- };
4521
- }, deps);
4522
- useEffect3(() => {
4523
- if (view) {
4524
- if (!selection && !view.state.selection.main.anchor) {
4525
- selection = EditorSelection2.single(view.state.doc.line(1).to);
4526
- }
4527
- if (selection || scrollTo) {
4528
- onUpdate.current = () => {
4529
- onUpdate.current = void 0;
4530
- view.dispatch({
4531
- selection,
4532
- effects: scrollTo && [
4533
- scrollTo
4534
- ],
4535
- scrollIntoView: !scrollTo
4536
- });
4537
- };
4538
- }
4539
- if (autoFocus) {
4540
- view.focus();
4541
- }
4542
- }
4543
- }, [
4544
- view,
4545
- autoFocus,
4546
- selection,
4547
- scrollTo
4548
- ]);
4549
- return {
4550
- parentRef,
4551
- view
4552
- };
4553
- };
4554
- var createDataExtensions = ({ readonly = false } = {}) => {
4555
- return [
4556
- //
4557
- EditorState3.readOnly.of(readonly),
4558
- EditorView15.editable.of(!readonly)
4559
- ];
4560
- };
4561
- var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4562
- return [
4563
- //
4564
- EditorView15.baseTheme(defaultTheme),
4565
- theme && EditorView15.theme(theme),
4566
- EditorView15.darkTheme.of(themeMode === "dark"),
4567
- EditorView15.editorAttributes.of({
4568
- class: slots?.editor?.className ?? ""
4569
- }),
4570
- EditorView15.contentAttributes.of({
4571
- class: slots?.content?.className ?? ""
4572
- })
4573
- ].filter(isNotFalsy5);
4574
- };
4575
-
4576
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
4577
- import get5 from "lodash.get";
4578
- import { useState as useState6, useMemo as useMemo3 } from "react";
4579
- import { generateName } from "@dxos/display-name";
4580
- import { getRawDoc } from "@dxos/echo-schema";
4581
- import { invariant as invariant3 } from "@dxos/invariant";
4582
- import { isAutomergeObject } from "@dxos/react-client/echo";
4583
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
4584
- var useTextExtensions = ({ id, text, space, identity }) => {
4585
- const extensions = [
4586
- automerge(text)
4587
- ];
4588
- if (space && identity) {
4589
- const awarenessProvider2 = new SpaceAwarenessProvider({
4590
- space,
4591
- channel: `awareness.${id}`,
4592
- peerId: identity.identityKey.toHex(),
4593
- info: {
4594
- displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
4595
- color: cursorColor.color,
4596
- lightColor: cursorColor.light
4597
- }
4598
- });
4599
- extensions.push(awareness(awarenessProvider2));
4600
- }
4601
- return extensions;
4602
- };
4603
- var useTextModel = (props) => useMemo3(() => createModel(props), Object.values(props));
4604
- var useInMemoryTextModel = ({ id, defaultContent }) => {
4605
- const [content, setContent] = useState6(defaultContent ?? "");
4606
- return {
4607
- id,
4608
- content,
4609
- setContent,
4610
- text: () => content
4611
- };
4612
- };
4613
- var createModel = ({ space, identity, text }) => {
4614
- if (!text) {
4615
- return void 0;
4616
- }
4617
- invariant3(isAutomergeObject(text), void 0, {
4618
- F: __dxlog_file7,
4619
- L: 83,
4620
- S: void 0,
4621
- A: [
4622
- "isAutomergeObject(text)",
4623
- ""
4624
- ]
4625
- });
4626
- const obj = text;
4627
- const doc = getRawDoc(obj, [
4628
- obj.field
4629
- ]);
4630
- const awarenessProvider2 = space && new SpaceAwarenessProvider({
4631
- space,
4632
- channel: `automerge.awareness.${obj.id}`,
4633
- info: {
4634
- displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
4635
- color: cursorColor.color,
4636
- lightColor: cursorColor.light
4637
- },
4638
- peerId: identity?.identityKey.toHex() ?? "Anonymous"
4639
- });
4640
- const extensions = [
4641
- modelState.init(() => model),
4642
- automerge({
4643
- handle: doc.handle,
4644
- path: doc.path
4645
- })
4646
- ];
4647
- if (awarenessProvider2) {
4648
- extensions.push(awareness(awarenessProvider2));
4649
- }
4650
- const model = {
4651
- id: obj.id,
4652
- content: doc,
4653
- text: () => get5(doc.handle.docSync(), doc.path),
4654
- extension: extensions,
4655
- peer: identity ? {
4656
- id: identity.identityKey.toHex(),
4657
- name: identity.profile?.displayName
4658
- } : void 0
4659
- };
4660
- return model;
4661
- };
4662
4901
  export {
4663
4902
  BaseTextEditor,
4664
4903
  Cursor,
@@ -4687,11 +4926,11 @@ export {
4687
4926
  blast,
4688
4927
  callbackWrapper,
4689
4928
  codeTheme,
4929
+ command,
4690
4930
  comments,
4691
4931
  compareFormatting,
4692
- createBasicBundle,
4932
+ createBasicExtensions,
4693
4933
  createComment,
4694
- createDataExtensions,
4695
4934
  createMarkdownExtensions,
4696
4935
  createThemeExtensions,
4697
4936
  cursorLineMargin,
@@ -4702,6 +4941,7 @@ export {
4702
4941
  defaultSlots,
4703
4942
  defaultTextSlots,
4704
4943
  defaultTheme,
4944
+ dnd,
4705
4945
  editorFillLayoutEditor,
4706
4946
  editorFillLayoutRoot,
4707
4947
  editorMode,
@@ -4712,7 +4952,7 @@ export {
4712
4952
  getToken,
4713
4953
  image,
4714
4954
  insertTable,
4715
- keymap8 as keymap,
4955
+ keymap9 as keymap,
4716
4956
  linkTooltip,
4717
4957
  listener,
4718
4958
  logChanges,
@@ -4721,7 +4961,6 @@ export {
4721
4961
  markdownTagsExtensions,
4722
4962
  markdownTheme,
4723
4963
  mention,
4724
- modelState,
4725
4964
  outliner,
4726
4965
  removeBlockquote,
4727
4966
  removeCodeblock,