@dxos/react-ui-editor 0.4.7-main.733903e → 0.4.7-main.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 +997 -728
  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 +4 -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 +31 -31
  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 +13 -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 -15
  69. package/src/extensions/markdown/decorate.ts +76 -32
  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 } 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
- var editingRange = (state, range) => {
2177
+ var editingRange = (state, range, focus) => {
1982
2178
  const { readOnly, selection: { main: { head } } } = state;
1983
- return !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",
@@ -1989,8 +2185,9 @@ var MarksByParent = /* @__PURE__ */ new Set([
1989
2185
  "SubscriptMark",
1990
2186
  "SuperscriptMark"
1991
2187
  ]);
1992
- var buildDecorations = (view, options) => {
1993
- const builder = new RangeSetBuilder();
2188
+ var buildDecorations = (view, options, focus) => {
2189
+ const deco = new RangeSetBuilder2();
2190
+ const atomicDeco = new RangeSetBuilder2();
1994
2191
  const { state } = view;
1995
2192
  for (const { from, to } of view.visibleRanges) {
1996
2193
  syntaxTree(state).iterate({
@@ -1998,7 +2195,7 @@ var buildDecorations = (view, options) => {
1998
2195
  to,
1999
2196
  enter: (node) => {
2000
2197
  if (node.name === "FencedCode") {
2001
- const editing = editingRange(state, node);
2198
+ const editing = editingRange(state, node, focus);
2002
2199
  for (const block of view.viewportLineBlocks) {
2003
2200
  if (block.to < node.from) {
2004
2201
  continue;
@@ -2007,23 +2204,23 @@ var buildDecorations = (view, options) => {
2007
2204
  break;
2008
2205
  }
2009
2206
  const first = block.from <= node.from;
2010
- const last = block.to >= node.to;
2011
- builder.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2207
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
2208
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2012
2209
  if (!editing && (first || last)) {
2013
- builder.add(block.from, block.to, hide);
2210
+ atomicDeco.add(block.from, block.to, hide);
2014
2211
  }
2015
2212
  }
2016
2213
  return false;
2017
2214
  } else if (node.name === "Link") {
2018
2215
  const marks = node.node.getChildren("LinkMark");
2019
2216
  const urlNode = node.node.getChild("URL");
2020
- const editing = editingRange(state, node);
2217
+ const editing = editingRange(state, node, focus);
2021
2218
  if (urlNode && marks.length >= 2) {
2022
2219
  const url = state.sliceDoc(urlNode.from, urlNode.to);
2023
2220
  if (!editing) {
2024
- builder.add(node.from, marks[0].to, hide);
2221
+ atomicDeco.add(node.from, marks[0].to, hide);
2025
2222
  }
2026
- builder.add(marks[0].to, marks[1].from, Decoration4.mark({
2223
+ deco.add(marks[0].to, marks[1].from, Decoration5.mark({
2027
2224
  tagName: "a",
2028
2225
  attributes: {
2029
2226
  class: "cm-link",
@@ -2033,57 +2230,87 @@ var buildDecorations = (view, options) => {
2033
2230
  }
2034
2231
  }));
2035
2232
  if (!editing) {
2036
- builder.add(marks[1].from, node.to, options.renderLinkButton ? Decoration4.replace({
2233
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
2037
2234
  widget: new LinkButton(url, options.renderLinkButton)
2038
2235
  }) : hide);
2039
2236
  }
2040
2237
  }
2041
2238
  } else if (node.name === "HeaderMark") {
2042
2239
  const parent = node.node.parent;
2043
- if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from))) {
2240
+ if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from), focus)) {
2044
2241
  const next = state.doc.sliceString(node.to, node.to + 1);
2045
- builder.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2242
+ atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2046
2243
  }
2047
2244
  } else if (node.name === "HorizontalRule") {
2048
- if (!editingRange(state, node)) {
2049
- builder.add(node.from, node.to, horizontalRule);
2245
+ if (!editingRange(state, node, focus)) {
2246
+ deco.add(node.from, node.to, horizontalRule);
2050
2247
  }
2051
2248
  } else if (node.name === "TaskMarker") {
2052
- if (!editingRange(state, node)) {
2249
+ if (!editingRange(state, node, focus)) {
2053
2250
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2054
- builder.add(node.from - 2, node.from - 1, Decoration4.mark({
2251
+ atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
2055
2252
  class: "cm-task"
2056
2253
  }));
2057
- builder.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2254
+ atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2058
2255
  }
2059
2256
  } else if (MarksByParent.has(node.name)) {
2060
- if (!editingRange(state, node.node.parent)) {
2061
- builder.add(node.from, node.to, hide);
2257
+ if (!editingRange(state, node.node.parent, focus)) {
2258
+ atomicDeco.add(node.from, node.to, hide);
2062
2259
  }
2063
2260
  }
2064
2261
  }
2065
2262
  });
2066
2263
  }
2067
- return builder.finish();
2264
+ return {
2265
+ deco: deco.finish(),
2266
+ atomicDeco: atomicDeco.finish()
2267
+ };
2068
2268
  };
2269
+ var forceUpdate = StateEffect4.define();
2069
2270
  var decorateMarkdown = (options = {}) => {
2070
2271
  return [
2071
- ViewPlugin3.fromClass(class {
2272
+ ViewPlugin4.fromClass(class {
2072
2273
  constructor(view) {
2073
- this.decorations = buildDecorations(view, options);
2274
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
2074
2275
  }
2075
2276
  update(update2) {
2076
- if (update2.docChanged || update2.selectionSet || update2.viewportChanged) {
2077
- this.decorations = buildDecorations(update2.view, options);
2277
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
2278
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
2279
+ this.clearUpdate();
2280
+ } else if (update2.selectionSet) {
2281
+ this.scheduleUpdate(update2.view);
2282
+ }
2283
+ }
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.
2286
+ scheduleUpdate(view) {
2287
+ this.clearUpdate();
2288
+ this.pendingUpdate = setTimeout(() => {
2289
+ view.dispatch({
2290
+ effects: forceUpdate.of(null)
2291
+ });
2292
+ }, options.selectionChangeDelay);
2293
+ }
2294
+ clearUpdate() {
2295
+ if (this.pendingUpdate) {
2296
+ clearTimeout(this.pendingUpdate);
2297
+ this.pendingUpdate = void 0;
2078
2298
  }
2079
2299
  }
2300
+ destroy() {
2301
+ this.clearUpdate();
2302
+ }
2080
2303
  }, {
2081
- decorations: (value) => value.decorations
2304
+ provide: (plugin) => [
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)
2308
+ ]
2082
2309
  }),
2083
2310
  formattingStyles
2084
2311
  ];
2085
2312
  };
2086
- var formattingStyles = EditorView10.baseTheme({
2313
+ var formattingStyles = EditorView12.baseTheme({
2087
2314
  "& .cm-code": {
2088
2315
  fontFamily: getToken("fontFamily.mono", []).join(",")
2089
2316
  },
@@ -2136,7 +2363,7 @@ var formattingStyles = EditorView10.baseTheme({
2136
2363
  import { snippet } from "@codemirror/autocomplete";
2137
2364
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2138
2365
  import { EditorSelection } from "@codemirror/state";
2139
- import { EditorView as EditorView11, keymap as keymap5 } from "@codemirror/view";
2366
+ import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
2140
2367
  import { useState, useMemo } from "react";
2141
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;
2142
2369
  var Inline;
@@ -3008,7 +3235,7 @@ var toggleCodeblock = (target) => {
3008
3235
  };
3009
3236
  var formatting = (options = {}) => {
3010
3237
  return [
3011
- keymap5.of([
3238
+ keymap6.of([
3012
3239
  {
3013
3240
  key: "meta-b",
3014
3241
  run: toggleStrong
@@ -3206,7 +3433,7 @@ var getFormatting = (state) => {
3206
3433
  };
3207
3434
  var useFormattingState = () => {
3208
3435
  const [state, setState] = useState(null);
3209
- const observer = useMemo(() => EditorView11.updateListener.of((update2) => {
3436
+ const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3210
3437
  if (update2.docChanged || update2.selectionSet) {
3211
3438
  const newState = getFormatting(update2.state);
3212
3439
  if (!state || !compareFormatting(state, newState)) {
@@ -3222,12 +3449,12 @@ var useFormattingState = () => {
3222
3449
 
3223
3450
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3224
3451
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3225
- import { StateField as StateField4 } from "@codemirror/state";
3226
- 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";
3227
3454
  var image = (options = {}) => {
3228
- return StateField4.define({
3455
+ return StateField5.define({
3229
3456
  create: (state) => {
3230
- return Decoration5.set(buildDecorations2(0, state.doc.length, state));
3457
+ return Decoration6.set(buildDecorations2(0, state.doc.length, state));
3231
3458
  },
3232
3459
  update: (value, tr) => {
3233
3460
  if (!tr.docChanged && !tr.selection) {
@@ -3250,7 +3477,7 @@ var image = (options = {}) => {
3250
3477
  add: buildDecorations2(from, to, tr.state)
3251
3478
  });
3252
3479
  },
3253
- provide: (field) => EditorView12.decorations.from(field)
3480
+ provide: (field) => EditorView14.decorations.from(field)
3254
3481
  });
3255
3482
  };
3256
3483
  var preloaded = /* @__PURE__ */ new Set();
@@ -3272,7 +3499,7 @@ var buildDecorations2 = (from, to, state) => {
3272
3499
  const hide2 = state.readOnly || cursor < node.from || cursor > node.to;
3273
3500
  const url = state.sliceDoc(urlNode.from, urlNode.to);
3274
3501
  preloadImage(url);
3275
- decorations.push(Decoration5.replace({
3502
+ decorations.push(Decoration6.replace({
3276
3503
  block: true,
3277
3504
  widget: new ImageWidget(url)
3278
3505
  }).range(hide2 ? node.from : node.to, node.to));
@@ -3284,7 +3511,7 @@ var buildDecorations2 = (from, to, state) => {
3284
3511
  });
3285
3512
  return decorations;
3286
3513
  };
3287
- var ImageWidget = class extends WidgetType3 {
3514
+ var ImageWidget = class extends WidgetType4 {
3288
3515
  constructor(_url) {
3289
3516
  super();
3290
3517
  this._url = _url;
@@ -3337,17 +3564,17 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
3337
3564
 
3338
3565
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
3339
3566
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3340
- import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField5 } from "@codemirror/state";
3341
- 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";
3342
3569
  var table = (options = {}) => {
3343
- return StateField5.define({
3570
+ return StateField6.define({
3344
3571
  create: (state) => update(state, options),
3345
3572
  update: (_, tr) => update(tr.state, options),
3346
- provide: (field) => EditorView13.decorations.from(field)
3573
+ provide: (field) => EditorView15.decorations.from(field)
3347
3574
  });
3348
3575
  };
3349
3576
  var update = (state, options) => {
3350
- const builder = new RangeSetBuilder2();
3577
+ const builder = new RangeSetBuilder3();
3351
3578
  const cursor = state.selection.main.head;
3352
3579
  const tables = [];
3353
3580
  const getTable = () => tables[tables.length - 1];
@@ -3387,16 +3614,16 @@ var update = (state, options) => {
3387
3614
  });
3388
3615
  tables.forEach((table2) => {
3389
3616
  const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
3390
- hide2 && builder.add(table2.from, table2.to, Decoration6.replace({
3617
+ hide2 && builder.add(table2.from, table2.to, Decoration7.replace({
3391
3618
  widget: new TableWidget(table2)
3392
3619
  }));
3393
- builder.add(table2.from, table2.to, Decoration6.mark({
3620
+ builder.add(table2.from, table2.to, Decoration7.mark({
3394
3621
  class: "cm-table"
3395
3622
  }));
3396
3623
  });
3397
3624
  return builder.finish();
3398
3625
  };
3399
- var TableWidget = class extends WidgetType4 {
3626
+ var TableWidget = class extends WidgetType5 {
3400
3627
  constructor(_table) {
3401
3628
  super();
3402
3629
  this._table = _table;
@@ -3435,6 +3662,8 @@ var TableWidget = class extends WidgetType4 {
3435
3662
 
3436
3663
  // packages/ui/react-ui-editor/src/extensions/mention.ts
3437
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";
3438
3667
  var mention = ({ onSearch }) => {
3439
3668
  return autocompletion2({
3440
3669
  // TODO(burdon): Not working.
@@ -3446,7 +3675,14 @@ var mention = ({ onSearch }) => {
3446
3675
  icons: false,
3447
3676
  override: [
3448
3677
  (context) => {
3449
- 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
+ });
3450
3686
  const match = context.matchBefore(/@(\w+)?/);
3451
3687
  if (!match || match.from === match.to && !context.explicit) {
3452
3688
  return null;
@@ -3463,10 +3699,10 @@ var mention = ({ onSearch }) => {
3463
3699
  };
3464
3700
 
3465
3701
  // packages/ui/react-ui-editor/src/extensions/modes.ts
3466
- import { Facet as Facet5 } from "@codemirror/state";
3467
- import { keymap as keymap6 } from "@codemirror/view";
3702
+ import { Facet as Facet6 } from "@codemirror/state";
3703
+ import { keymap as keymap7 } from "@codemirror/view";
3468
3704
  import { vim } from "@replit/codemirror-vim";
3469
- var editorMode = Facet5.define({
3705
+ var editorMode = Facet6.define({
3470
3706
  combine: (modes) => modes[0] ?? {}
3471
3707
  });
3472
3708
  var EditorModes = {
@@ -3477,7 +3713,7 @@ var EditorModes = {
3477
3713
  type: "vim",
3478
3714
  noTabster: true
3479
3715
  }),
3480
- keymap6.of([
3716
+ keymap7.of([
3481
3717
  {
3482
3718
  key: "Alt-Escape",
3483
3719
  run: (view) => {
@@ -3497,7 +3733,7 @@ var outliner = (options = {}) => {
3497
3733
  };
3498
3734
 
3499
3735
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
3500
- import { keymap as keymap7 } from "@codemirror/view";
3736
+ import { keymap as keymap8 } from "@codemirror/view";
3501
3737
  var defaultItems = [
3502
3738
  "hello world!",
3503
3739
  "this is a test.",
@@ -3507,7 +3743,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
3507
3743
  let t;
3508
3744
  let idx = 0;
3509
3745
  return [
3510
- keymap7.of([
3746
+ keymap8.of([
3511
3747
  {
3512
3748
  // Reset.
3513
3749
  key: "alt-meta-'",
@@ -3553,147 +3789,331 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
3553
3789
  ];
3554
3790
  };
3555
3791
 
3556
- // packages/ui/react-ui-editor/src/themes/default.ts
3557
- import get4 from "lodash.get";
3558
- var defaultTheme = {
3559
- //
3560
- // Main layout:
3561
- // https://codemirror.net/examples/styling
3562
- //
3563
- // <div class="cm-editor [cm-focused] [generated classes]">
3564
- // <div class="cm-scroller">
3565
- // <div class="cm-gutters">
3566
- // <div class="cm-gutter [...]">
3567
- // <div class="cm-gutterElement">...</div>
3568
- // </div>
3569
- // </div>
3570
- // <div class="cm-content" role="textbox" contenteditable="true">
3571
- // <div class="cm-line"></div>
3572
- // </div>
3573
- // <div class="cm-selectionLayer">
3574
- // <div class="cm-selectionBackground"></div>
3575
- // </div>
3576
- // <div class="cm-cursorLayer">
3577
- // <div class="cm-cursor"></div>
3578
- // </div>
3579
- // </div>
3580
- // </div>
3581
- //
3582
- "&": {},
3583
- "&.cm-focused": {
3584
- outline: "none"
3585
- },
3586
- // NOTE: See https://codemirror.net/docs/guide (DOM Structure).
3587
- ".cm-scroller": {
3588
- // TODO(burdon): Reconcile with docs: https://codemirror.net/docs/guide
3589
- // 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.
3590
- overflowY: "auto",
3591
- fontFamily: get4(tokens, "fontFamily.mono", []).join(","),
3592
- lineHeight: 1.5
3593
- },
3594
- ".cm-content": {
3595
- // TODO(burdon): Is it possible to remove the padding value from CM's default theme?
3596
- // padding: 'unset',
3597
- // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
3598
- fontSize: "16px"
3599
- },
3600
- "&light .cm-content": {
3601
- color: get4(tokens, "extend.semanticColors.base.fg.light", "black"),
3602
- caretColor: "black"
3603
- },
3604
- "&dark .cm-content": {
3605
- color: get4(tokens, "extend.semanticColors.base.fg.dark", "white"),
3606
- caretColor: "white"
3607
- },
3608
- //
3609
- // Cursor
3610
- //
3611
- "&light .cm-cursor, &light .cm-dropCursor": {
3612
- borderLeft: "2px solid black"
3613
- },
3614
- "&dark .cm-cursor, &dark .cm-dropCursor": {
3615
- borderLeft: "2px solid white"
3616
- },
3617
- "&light .cm-placeholder": {
3618
- color: get4(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
3619
- },
3620
- "&dark .cm-placeholder": {
3621
- color: get4(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
3622
- },
3623
- //
3624
- // line
3625
- //
3626
- ".cm-line": {
3627
- paddingInline: 0
3628
- },
3629
- ".cm-activeLine": {
3630
- background: "transparent"
3631
- },
3632
- //
3633
- // gutter
3634
- //
3635
- ".cm-gutterElement": {
3636
- width: "48px"
3637
- },
3638
- //
3639
- // Selection
3640
- //
3641
- "&light .cm-selectionBackground": {
3642
- background: get4(tokens, "extend.colors.primary.100")
3643
- },
3644
- "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
3645
- background: get4(tokens, "extend.colors.primary.200")
3646
- },
3647
- "&dark .cm-selectionBackground": {
3648
- background: get4(tokens, "extend.colors.primary.700")
3649
- },
3650
- "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
3651
- background: get4(tokens, "extend.colors.primary.600")
3652
- },
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 = {
3653
3978
  //
3654
- // Search
3979
+ // Main layout:
3980
+ // https://codemirror.net/examples/styling
3655
3981
  //
3656
- "&light .cm-searchMatch": {
3657
- backgroundColor: get4(tokens, "extend.colors.yellow.100")
3982
+ // <div class="cm-editor [cm-focused] [generated classes]">
3983
+ // <div class="cm-scroller">
3984
+ // <div class="cm-gutters">
3985
+ // <div class="cm-gutter [...]">
3986
+ // <div class="cm-gutterElement">...</div>
3987
+ // </div>
3988
+ // </div>
3989
+ // <div class="cm-content" role="textbox" contenteditable="true">
3990
+ // <div class="cm-line"></div>
3991
+ // </div>
3992
+ // <div class="cm-selectionLayer">
3993
+ // <div class="cm-selectionBackground"></div>
3994
+ // </div>
3995
+ // <div class="cm-cursorLayer">
3996
+ // <div class="cm-cursor"></div>
3997
+ // </div>
3998
+ // </div>
3999
+ // </div>
4000
+ //
4001
+ "&": {},
4002
+ "&.cm-focused": {
4003
+ outline: "none"
3658
4004
  },
3659
- "&dark .cm-searchMatch": {
3660
- backgroundColor: get4(tokens, "extend.colors.yellow.700")
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
3661
4012
  },
3662
- //
3663
- // link
3664
- //
3665
- ".cm-link": {
3666
- color: get4(tokens, "extend.colors.primary.500"),
3667
- textDecorationLine: "underline",
3668
- textDecorationThickness: "1px",
3669
- textUnderlineOffset: "2px",
3670
- borderRadius: ".125rem",
3671
- fontFamily: get4(tokens, "fontFamily.body", []).join(",")
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"
3672
4018
  },
3673
- //
3674
- // tooltip
3675
- //
3676
- ".cm-tooltip": {
3677
- border: "none"
4019
+ "&light .cm-content": {
4020
+ color: get4(tokens, "extend.semanticColors.base.fg.light", "black"),
4021
+ caretColor: "black"
4022
+ },
4023
+ "&dark .cm-content": {
4024
+ color: get4(tokens, "extend.semanticColors.base.fg.dark", "white"),
4025
+ caretColor: "white"
3678
4026
  },
3679
- ".cm-tooltip-below": {},
3680
4027
  //
3681
- // autocomplete
4028
+ // Cursor
3682
4029
  //
3683
- ".cm-tooltip-autocomplete": {
3684
- marginTop: "4px",
3685
- marginLeft: "-3px"
4030
+ "&light .cm-cursor, &light .cm-dropCursor": {
4031
+ borderLeft: "2px solid black"
3686
4032
  },
3687
- ".cm-tooltip-autocomplete ul li": {},
3688
- ".cm-tooltip-autocomplete ul li[aria-selected]": {},
3689
- ".cm-completionIcon": {
3690
- display: "none"
4033
+ "&dark .cm-cursor, &dark .cm-dropCursor": {
4034
+ borderLeft: "2px solid white"
3691
4035
  },
3692
- ".cm-completionLabel": {
3693
- fontFamily: get4(tokens, "fontFamily.body", []).join(",")
4036
+ "&light .cm-placeholder": {
4037
+ color: get4(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
3694
4038
  },
3695
- ".cm-completionMatchedText": {
3696
- textDecoration: "none"
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"
3697
4117
  },
3698
4118
  //
3699
4119
  // table
@@ -3803,19 +4223,238 @@ var codeTheme = {
3803
4223
  }
3804
4224
  };
3805
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
+
3806
4445
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
3807
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
3808
- 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) => {
3809
4448
  const tabsterDOMAttribute = useFocusableGroup({
3810
4449
  tabBehavior: "limited"
3811
4450
  });
3812
4451
  const { themeMode } = useThemeContext();
3813
- const rootRef = useRef(null);
3814
- const [view, setView] = useState2(null);
4452
+ const rootRef = useRef3(null);
4453
+ const [view, setView] = useState5(null);
3815
4454
  useImperativeHandle(forwardedRef, () => view, [
3816
4455
  view
3817
4456
  ]);
3818
- useEffect2(() => {
4457
+ useEffect3(() => {
3819
4458
  if (autoFocus) {
3820
4459
  view?.focus();
3821
4460
  }
@@ -3823,38 +4462,38 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3823
4462
  view,
3824
4463
  autoFocus
3825
4464
  ]);
3826
- useEffect2(() => {
4465
+ useEffect3(() => {
3827
4466
  if (!model || !rootRef.current) {
3828
4467
  return;
3829
4468
  }
3830
- const state = EditorState2.create({
3831
- doc: model.text(),
4469
+ const state = EditorState3.create({
4470
+ doc: typeof model.content === "string" ? model.content : DocAccessor.getValue(model.content),
3832
4471
  selection,
3833
4472
  extensions: [
3834
4473
  // TODO(burdon): Doesn't catch errors in keymap functions.
3835
- EditorView14.exceptionSink.of((err) => {
3836
- log3.catch(err, void 0, {
3837
- F: __dxlog_file4,
3838
- L: 122,
4474
+ EditorView17.exceptionSink.of((err) => {
4475
+ log9.catch(err, void 0, {
4476
+ F: __dxlog_file11,
4477
+ L: 123,
3839
4478
  S: void 0,
3840
4479
  C: (f, a) => f(...a)
3841
4480
  });
3842
4481
  }),
3843
4482
  // Theme.
3844
4483
  // TODO(burdon): Make base theme configurable.
3845
- EditorView14.baseTheme(defaultTheme),
3846
- theme && EditorView14.theme(theme),
3847
- EditorView14.darkTheme.of(themeMode === "dark"),
3848
- 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({
3849
4488
  class: slots.editor.className
3850
4489
  }),
3851
- slots.content?.className && EditorView14.contentAttributes.of({
4490
+ slots.content?.className && EditorView17.contentAttributes.of({
3852
4491
  class: slots.content.className
3853
4492
  }),
3854
4493
  // NOTE: Assumes default line height.
3855
- _scrollPastEnd && scrollPastEnd(),
4494
+ _scrollPastEnd && scrollPastEnd2(),
3856
4495
  // Focus.
3857
- EditorView14.updateListener.of((update2) => {
4496
+ EditorView17.updateListener.of((update2) => {
3858
4497
  update2.transactions.forEach((transaction) => {
3859
4498
  if (transaction.isUserEvent("focus.container")) {
3860
4499
  rootRef.current?.focus();
@@ -3862,8 +4501,8 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3862
4501
  });
3863
4502
  }),
3864
4503
  // State.
3865
- EditorView14.editable.of(!readonly),
3866
- EditorState2.readOnly.of(!!readonly),
4504
+ EditorView17.editable.of(!readonly),
4505
+ EditorState3.readOnly.of(!!readonly),
3867
4506
  // Storage and replication.
3868
4507
  // NOTE: This must come before user extensions.
3869
4508
  model.extension,
@@ -3871,7 +4510,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3871
4510
  ...extensions
3872
4511
  ].filter(isNotFalsy4)
3873
4512
  });
3874
- const view2 = new EditorView14({
4513
+ const view2 = new EditorView17({
3875
4514
  state,
3876
4515
  parent: rootRef.current,
3877
4516
  scrollTo,
@@ -3928,14 +4567,16 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3928
4567
  ...tabsterDOMAttribute
3929
4568
  });
3930
4569
  });
3931
- 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) => {
3932
4571
  const { themeMode } = useThemeContext();
3933
- const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
3934
- const extensions = useMemo2(() => [
3935
- createBasicBundle({
3936
- themeMode,
3937
- placeholder: placeholder3,
3938
- lineWrapping
4572
+ const updatedSlots = defaultsDeep3({}, slots, defaultTextSlots);
4573
+ const extensions = useMemo3(() => [
4574
+ createBasicExtensions({
4575
+ lineWrapping,
4576
+ placeholder: placeholder3
4577
+ }),
4578
+ createThemeExtensions({
4579
+ themeMode
3939
4580
  }),
3940
4581
  ..._extensions ?? []
3941
4582
  ], [
@@ -3955,8 +4596,8 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
3955
4596
  });
3956
4597
  var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
3957
4598
  const { themeMode } = useThemeContext();
3958
- const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
3959
- const extensions = useMemo2(() => [
4599
+ const updatedSlots = defaultsDeep3({}, slots, defaultMarkdownSlots);
4600
+ const extensions = useMemo3(() => [
3960
4601
  createMarkdownExtensions({
3961
4602
  themeMode,
3962
4603
  placeholder: placeholder3
@@ -3994,7 +4635,7 @@ var defaultMarkdownSlots = {
3994
4635
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
3995
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";
3996
4637
  import { createContext } from "@radix-ui/react-context";
3997
- import React2, { useRef as useRef2, useState as useState3 } from "react";
4638
+ import React2, { useRef as useRef4, useState as useState6 } from "react";
3998
4639
  import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
3999
4640
  import { getSize } from "@dxos/react-ui-theme";
4000
4641
  var tooltipProps = {
@@ -4052,9 +4693,9 @@ var MarkdownHeading = () => {
4052
4693
  const header = blockType && /heading(\d)/.exec(blockType);
4053
4694
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4054
4695
  const HeadingIcon = HeadingIcons[value ?? "0"];
4055
- const suppressNextTooltip = useRef2(false);
4056
- const [tooltipOpen, setTooltipOpen] = useState3(false);
4057
- const [selectOpen, setSelectOpen] = useState3(false);
4696
+ const suppressNextTooltip = useRef4(false);
4697
+ const [tooltipOpen, setTooltipOpen] = useState6(false);
4698
+ const [selectOpen, setSelectOpen] = useState6(false);
4058
4699
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
4059
4700
  open: tooltipOpen,
4060
4701
  onOpenChange: (nextOpen) => {
@@ -4257,378 +4898,6 @@ var Toolbar = {
4257
4898
  Markdown: MarkdownStandard,
4258
4899
  Extended: MarkdownExtended
4259
4900
  };
4260
-
4261
- // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
4262
- import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
4263
- import { Context as Context2 } from "@dxos/context";
4264
- import { invariant as invariant2 } from "@dxos/invariant";
4265
- import { log as log4 } from "@dxos/log";
4266
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
4267
- var DEBOUNCE_INTERVAL = 100;
4268
- var SpaceAwarenessProvider = class {
4269
- constructor(params) {
4270
- this._remoteStates = /* @__PURE__ */ new Map();
4271
- this.remoteStateChange = new Event2();
4272
- this._space = params.space;
4273
- this._channel = params.channel;
4274
- this._peerId = params.peerId;
4275
- this._info = params.info;
4276
- }
4277
- open() {
4278
- this._ctx = new Context2();
4279
- this._postTask = new DeferredTask(this._ctx, async () => {
4280
- if (this._localState) {
4281
- await this._space.postMessage(this._channel, {
4282
- kind: "post",
4283
- state: this._localState
4284
- });
4285
- await sleep(DEBOUNCE_INTERVAL);
4286
- }
4287
- });
4288
- this._ctx.onDispose(this._space.listen(this._channel, (message) => {
4289
- switch (message.payload.kind) {
4290
- case "query": {
4291
- this._handleQueryMessage();
4292
- break;
4293
- }
4294
- case "post": {
4295
- this._handlePostMessage(message.payload);
4296
- break;
4297
- }
4298
- }
4299
- }));
4300
- void this._space.postMessage(this._channel, {
4301
- kind: "query"
4302
- }).catch((err) => {
4303
- log4.debug("failed to query awareness", {
4304
- err
4305
- }, {
4306
- F: __dxlog_file5,
4307
- L: 89,
4308
- S: this,
4309
- C: (f, a) => f(...a)
4310
- });
4311
- });
4312
- }
4313
- close() {
4314
- void this._ctx?.dispose();
4315
- this._ctx = void 0;
4316
- this._postTask = void 0;
4317
- }
4318
- getRemoteStates() {
4319
- return Array.from(this._remoteStates.values());
4320
- }
4321
- update(position) {
4322
- invariant2(this._postTask, void 0, {
4323
- F: __dxlog_file5,
4324
- L: 104,
4325
- S: this,
4326
- A: [
4327
- "this._postTask",
4328
- ""
4329
- ]
4330
- });
4331
- this._localState = {
4332
- peerId: this._peerId,
4333
- position,
4334
- info: this._info
4335
- };
4336
- this._postTask.schedule();
4337
- }
4338
- _handleQueryMessage() {
4339
- invariant2(this._postTask, void 0, {
4340
- F: __dxlog_file5,
4341
- L: 115,
4342
- S: this,
4343
- A: [
4344
- "this._postTask",
4345
- ""
4346
- ]
4347
- });
4348
- this._postTask.schedule();
4349
- }
4350
- _handlePostMessage(message) {
4351
- invariant2(message.kind === "post", void 0, {
4352
- F: __dxlog_file5,
4353
- L: 120,
4354
- S: this,
4355
- A: [
4356
- "message.kind === 'post'",
4357
- ""
4358
- ]
4359
- });
4360
- this._remoteStates.set(message.state.peerId, message.state);
4361
- this.remoteStateChange.emit();
4362
- }
4363
- };
4364
-
4365
- // packages/ui/react-ui-editor/src/hooks/defs.ts
4366
- import { StateField as StateField6 } from "@codemirror/state";
4367
- var modelState = StateField6.define({
4368
- create: () => void 0,
4369
- update: (model) => model
4370
- });
4371
-
4372
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4373
- var useActionHandler = (view) => {
4374
- return (action) => {
4375
- if (!view) {
4376
- return;
4377
- }
4378
- let inlineType, listType;
4379
- switch (action.type) {
4380
- case "heading":
4381
- setHeading(parseInt(action.data))(view);
4382
- break;
4383
- case "strong":
4384
- case "emphasis":
4385
- case "strikethrough":
4386
- case "code":
4387
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4388
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4389
- break;
4390
- case "list-ordered":
4391
- case "list-bullet":
4392
- case "list-task":
4393
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4394
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4395
- break;
4396
- case "blockquote":
4397
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4398
- break;
4399
- case "codeblock":
4400
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4401
- break;
4402
- case "table":
4403
- insertTable(view);
4404
- break;
4405
- case "link":
4406
- (action.data === false ? removeLink : addLink)(view);
4407
- break;
4408
- case "comment":
4409
- createComment(view);
4410
- break;
4411
- }
4412
- setTimeout(() => {
4413
- if (!view.hasFocus) {
4414
- view.focus();
4415
- }
4416
- });
4417
- };
4418
- };
4419
-
4420
- // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
4421
- import { useRef as useRef3, useState as useState4 } from "react";
4422
- var useEditorView = (id) => {
4423
- const editorRef = useRef3(null);
4424
- const [prev, setPrev] = useState4([
4425
- null,
4426
- ""
4427
- ]);
4428
- if (prev[0] !== editorRef.current) {
4429
- setPrev([
4430
- editorRef.current,
4431
- id
4432
- ]);
4433
- }
4434
- return [
4435
- editorRef,
4436
- prev[1] !== id
4437
- ];
4438
- };
4439
-
4440
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4441
- import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4442
- import { EditorView as EditorView15 } from "@codemirror/view";
4443
- import { useEffect as useEffect3, useRef as useRef4, useState as useState5 } from "react";
4444
- import { log as log5 } from "@dxos/log";
4445
- import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
4446
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4447
- var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}, deps) => {
4448
- const onUpdate = useRef4();
4449
- const [view, setView] = useState5();
4450
- const parentRef = useRef4(null);
4451
- useEffect3(() => {
4452
- let view2;
4453
- if (parentRef.current) {
4454
- const state = EditorState3.create({
4455
- doc,
4456
- selection,
4457
- extensions: [
4458
- // TODO(burdon): Doesn't catch errors in keymap functions.
4459
- EditorView15.exceptionSink.of((err) => {
4460
- log5.catch(err, void 0, {
4461
- F: __dxlog_file6,
4462
- L: 57,
4463
- S: void 0,
4464
- C: (f, a) => f(...a)
4465
- });
4466
- }),
4467
- extensions,
4468
- EditorView15.updateListener.of(() => {
4469
- onUpdate.current?.();
4470
- })
4471
- ].filter(isNotFalsy5)
4472
- });
4473
- view2 = new EditorView15({
4474
- parent: parentRef.current,
4475
- scrollTo,
4476
- state,
4477
- // NOTE: Uncomment to debug/monitor all transactions.
4478
- // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4479
- dispatchTransactions: (trs, view3) => {
4480
- if (debug) {
4481
- logChanges(trs);
4482
- }
4483
- view3.update(trs);
4484
- }
4485
- });
4486
- setView(view2);
4487
- }
4488
- return () => {
4489
- view2?.destroy();
4490
- };
4491
- }, deps);
4492
- useEffect3(() => {
4493
- if (view) {
4494
- if (!selection && !view.state.selection.main.anchor) {
4495
- selection = EditorSelection2.single(view.state.doc.line(1).to);
4496
- }
4497
- if (selection || scrollTo) {
4498
- onUpdate.current = () => {
4499
- onUpdate.current = void 0;
4500
- view.dispatch({
4501
- selection,
4502
- effects: scrollTo && [
4503
- scrollTo
4504
- ],
4505
- scrollIntoView: !scrollTo
4506
- });
4507
- };
4508
- }
4509
- if (autoFocus) {
4510
- view.focus();
4511
- }
4512
- }
4513
- }, [
4514
- view,
4515
- autoFocus,
4516
- selection,
4517
- scrollTo
4518
- ]);
4519
- return {
4520
- parentRef,
4521
- view
4522
- };
4523
- };
4524
- var createDataExtensions = ({ readonly = false } = {}) => {
4525
- return [
4526
- //
4527
- EditorState3.readOnly.of(readonly),
4528
- EditorView15.editable.of(!readonly)
4529
- ];
4530
- };
4531
- var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4532
- return [
4533
- //
4534
- EditorView15.baseTheme(defaultTheme),
4535
- theme && EditorView15.theme(theme),
4536
- EditorView15.darkTheme.of(themeMode === "dark"),
4537
- EditorView15.editorAttributes.of({
4538
- class: slots?.editor?.className ?? ""
4539
- }),
4540
- EditorView15.contentAttributes.of({
4541
- class: slots?.content?.className ?? ""
4542
- })
4543
- ].filter(isNotFalsy5);
4544
- };
4545
-
4546
- // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
4547
- import get5 from "lodash.get";
4548
- import { useState as useState6, useMemo as useMemo3 } from "react";
4549
- import { generateName } from "@dxos/display-name";
4550
- import { getRawDoc } from "@dxos/echo-schema";
4551
- import { invariant as invariant3 } from "@dxos/invariant";
4552
- import { isAutomergeObject } from "@dxos/react-client/echo";
4553
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
4554
- var useTextExtensions = ({ id, text, space, identity }) => {
4555
- const extensions = [
4556
- automerge(text)
4557
- ];
4558
- if (space && identity) {
4559
- const awarenessProvider2 = new SpaceAwarenessProvider({
4560
- space,
4561
- channel: `awareness.${id}`,
4562
- peerId: identity.identityKey.toHex(),
4563
- info: {
4564
- displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
4565
- color: cursorColor.color,
4566
- lightColor: cursorColor.light
4567
- }
4568
- });
4569
- extensions.push(awareness(awarenessProvider2));
4570
- }
4571
- return extensions;
4572
- };
4573
- var useTextModel = (props) => useMemo3(() => createModel(props), Object.values(props));
4574
- var useInMemoryTextModel = ({ id, defaultContent }) => {
4575
- const [content, setContent] = useState6(defaultContent ?? "");
4576
- return {
4577
- id,
4578
- content,
4579
- setContent,
4580
- text: () => content
4581
- };
4582
- };
4583
- var createModel = ({ space, identity, text }) => {
4584
- if (!text) {
4585
- return void 0;
4586
- }
4587
- invariant3(isAutomergeObject(text), void 0, {
4588
- F: __dxlog_file7,
4589
- L: 83,
4590
- S: void 0,
4591
- A: [
4592
- "isAutomergeObject(text)",
4593
- ""
4594
- ]
4595
- });
4596
- const obj = text;
4597
- const doc = getRawDoc(obj, [
4598
- obj.field
4599
- ]);
4600
- const awarenessProvider2 = space && new SpaceAwarenessProvider({
4601
- space,
4602
- channel: `automerge.awareness.${obj.id}`,
4603
- info: {
4604
- displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
4605
- color: cursorColor.color,
4606
- lightColor: cursorColor.light
4607
- },
4608
- peerId: identity?.identityKey.toHex() ?? "Anonymous"
4609
- });
4610
- const extensions = [
4611
- modelState.init(() => model),
4612
- automerge({
4613
- handle: doc.handle,
4614
- path: doc.path
4615
- })
4616
- ];
4617
- if (awarenessProvider2) {
4618
- extensions.push(awareness(awarenessProvider2));
4619
- }
4620
- const model = {
4621
- id: obj.id,
4622
- content: doc,
4623
- text: () => get5(doc.handle.docSync(), doc.path),
4624
- extension: extensions,
4625
- peer: identity ? {
4626
- id: identity.identityKey.toHex(),
4627
- name: identity.profile?.displayName
4628
- } : void 0
4629
- };
4630
- return model;
4631
- };
4632
4901
  export {
4633
4902
  BaseTextEditor,
4634
4903
  Cursor,
@@ -4657,11 +4926,11 @@ export {
4657
4926
  blast,
4658
4927
  callbackWrapper,
4659
4928
  codeTheme,
4929
+ command,
4660
4930
  comments,
4661
4931
  compareFormatting,
4662
- createBasicBundle,
4932
+ createBasicExtensions,
4663
4933
  createComment,
4664
- createDataExtensions,
4665
4934
  createMarkdownExtensions,
4666
4935
  createThemeExtensions,
4667
4936
  cursorLineMargin,
@@ -4672,6 +4941,7 @@ export {
4672
4941
  defaultSlots,
4673
4942
  defaultTextSlots,
4674
4943
  defaultTheme,
4944
+ dnd,
4675
4945
  editorFillLayoutEditor,
4676
4946
  editorFillLayoutRoot,
4677
4947
  editorMode,
@@ -4682,7 +4952,7 @@ export {
4682
4952
  getToken,
4683
4953
  image,
4684
4954
  insertTable,
4685
- keymap8 as keymap,
4955
+ keymap9 as keymap,
4686
4956
  linkTooltip,
4687
4957
  listener,
4688
4958
  logChanges,
@@ -4691,7 +4961,6 @@ export {
4691
4961
  markdownTagsExtensions,
4692
4962
  markdownTheme,
4693
4963
  mention,
4694
- modelState,
4695
4964
  outliner,
4696
4965
  removeBlockquote,
4697
4966
  removeCodeblock,