@dxos/react-ui-editor 0.3.11-main.a757f8c → 0.3.11-main.a8c932f

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 (93) hide show
  1. package/dist/lib/browser/index.mjs +1915 -326
  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 +1 -2
  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 +1 -1
  8. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
  9. package/dist/types/src/components/TextEditor/{comments.stories.d.ts → hooks.stories.d.ts} +9 -12
  10. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts +29 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +25 -0
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  15. package/dist/types/src/components/Toolbar/index.d.ts +2 -0
  16. package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
  17. package/dist/types/src/components/index.d.ts +1 -0
  18. package/dist/types/src/components/index.d.ts.map +1 -1
  19. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  21. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  22. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  23. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  24. package/dist/types/src/extensions/automerge/defs.d.ts +5 -5
  25. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  26. package/dist/types/src/extensions/automerge/semaphore.d.ts +2 -1
  27. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  28. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  29. package/dist/types/src/extensions/awareness.d.ts +1 -1
  30. package/dist/types/src/extensions/basic.d.ts +1 -1
  31. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  32. package/dist/types/src/extensions/comments.d.ts +11 -3
  33. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  34. package/dist/types/src/extensions/markdown/bundle.d.ts +2 -2
  35. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -1
  37. package/dist/types/src/extensions/markdown/formatting.d.ts +58 -0
  38. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  39. package/dist/types/src/extensions/markdown/formatting.test.d.ts +3 -0
  40. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -0
  41. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  42. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  43. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  44. package/dist/types/src/hooks/index.d.ts +2 -0
  45. package/dist/types/src/hooks/index.d.ts.map +1 -1
  46. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  47. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  48. package/dist/types/src/hooks/useEditorView.d.ts +17 -0
  49. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
  50. package/dist/types/src/hooks/useTextEditor.d.ts +36 -14
  51. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  52. package/dist/types/src/hooks/useTextModel.d.ts +12 -0
  53. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  54. package/dist/types/src/index.d.ts +1 -1
  55. package/dist/types/src/index.d.ts.map +1 -1
  56. package/dist/types/src/themes/default.d.ts.map +1 -1
  57. package/package.json +40 -27
  58. package/src/components/TextEditor/MarkdownEditor.stories.tsx +12 -18
  59. package/src/components/TextEditor/TextEditor.tsx +27 -16
  60. package/src/components/TextEditor/automerge.stories.tsx +1 -2
  61. package/src/components/TextEditor/hooks.stories.tsx +111 -0
  62. package/src/components/Toolbar/Toolbar.stories.tsx +102 -0
  63. package/src/components/Toolbar/Toolbar.tsx +264 -0
  64. package/src/components/Toolbar/index.ts +5 -0
  65. package/src/components/index.ts +1 -0
  66. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  67. package/src/extensions/automerge/automerge.test.ts +13 -0
  68. package/src/extensions/automerge/automerge.ts +26 -11
  69. package/src/extensions/automerge/cursor.ts +3 -3
  70. package/src/extensions/automerge/defs.ts +9 -9
  71. package/src/extensions/automerge/semaphore.ts +17 -19
  72. package/src/extensions/automerge/update-automerge.ts +12 -6
  73. package/src/extensions/automerge/update-codemirror.ts +5 -5
  74. package/src/extensions/awareness.ts +7 -4
  75. package/src/extensions/basic.ts +15 -13
  76. package/src/extensions/blast.ts +4 -1
  77. package/src/extensions/comments.ts +126 -68
  78. package/src/extensions/markdown/bundle.ts +10 -5
  79. package/src/extensions/markdown/code.ts +36 -13
  80. package/src/extensions/markdown/formatting.test.ts +482 -0
  81. package/src/extensions/markdown/formatting.ts +1237 -0
  82. package/src/extensions/markdown/heading.ts +1 -1
  83. package/src/extensions/markdown/index.ts +1 -0
  84. package/src/extensions/markdown/link.ts +12 -2
  85. package/src/hooks/index.ts +2 -0
  86. package/src/hooks/useActionHandler.ts +93 -0
  87. package/src/hooks/useEditorView.ts +29 -0
  88. package/src/hooks/useTextEditor.ts +143 -19
  89. package/src/hooks/useTextModel.ts +28 -9
  90. package/src/index.ts +1 -1
  91. package/src/themes/default.ts +11 -6
  92. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
  93. package/src/components/TextEditor/comments.stories.tsx +0 -357
@@ -1,20 +1,21 @@
1
1
  // packages/ui/react-ui-editor/src/index.ts
2
2
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
3
3
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
4
+ import { keymap as keymap7 } from "@codemirror/view";
4
5
 
5
6
  // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
7
  import { tags as tags2 } from "@lezer/highlight";
7
8
 
8
9
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
10
  import { EditorState as EditorState2 } from "@codemirror/state";
10
- import { EditorView as EditorView13 } from "@codemirror/view";
11
- import { useFocusableGroup } from "@fluentui/react-tabster";
11
+ import { EditorView as EditorView14 } from "@codemirror/view";
12
12
  import { vim } from "@replit/codemirror-vim";
13
13
  import defaultsDeep2 from "lodash.defaultsdeep";
14
- import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
14
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2 } from "react";
15
15
  import { log as log2 } from "@dxos/log";
16
16
  import { useThemeContext } from "@dxos/react-ui";
17
- import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
17
+ import { focusRing, mx as mx3 } from "@dxos/react-ui-theme";
18
+ import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
18
19
 
19
20
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
20
21
  import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
@@ -53,14 +54,14 @@ var autocomplete = ({ onSearch }) => {
53
54
  };
54
55
 
55
56
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
57
+ import { invertedEffects } from "@codemirror/commands";
56
58
  import { StateField } from "@codemirror/state";
57
59
  import { EditorView, ViewPlugin } from "@codemirror/view";
58
- import { next as A } from "@dxos/automerge/automerge";
59
- import { invariant } from "@dxos/invariant";
60
+ import { next as A4 } from "@dxos/automerge/automerge";
60
61
 
61
62
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
62
63
  import get from "lodash.get";
63
- import { next as automerge } from "@dxos/automerge/automerge";
64
+ import { next as A } from "@dxos/automerge/automerge";
64
65
  var cursorConverter = (handle, path) => ({
65
66
  // TODO(burdon): Handle assoc to associate with a previous character.
66
67
  toCursor: (pos) => {
@@ -72,7 +73,7 @@ var cursorConverter = (handle, path) => ({
72
73
  if (typeof value === "string" && value.length <= pos) {
73
74
  return "end";
74
75
  }
75
- return automerge.getCursor(doc, path.slice(), pos);
76
+ return A.getCursor(doc, path.slice(), pos);
76
77
  },
77
78
  fromCursor: (cursor) => {
78
79
  if (cursor === "") {
@@ -90,26 +91,28 @@ var cursorConverter = (handle, path) => ({
90
91
  return 0;
91
92
  }
92
93
  }
93
- return automerge.getCursorPosition(doc, path.slice(), cursor);
94
+ return A.getCursorPosition(doc, path.slice(), cursor);
94
95
  }
95
96
  });
96
97
 
97
98
  // packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
98
99
  import { Annotation, StateEffect } from "@codemirror/state";
99
- var effectType = StateEffect.define({});
100
- var reconcileAnnotationType = Annotation.define();
101
100
  var getPath = (state, field) => state.field(field).path;
102
101
  var getLastHeads = (state, field) => state.field(field).lastHeads;
103
- var updateHeads = (newHeads) => effectType.of({
102
+ var updateHeadsEffect = StateEffect.define({});
103
+ var updateHeads = (newHeads) => updateHeadsEffect.of({
104
104
  newHeads
105
105
  });
106
- var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
106
+ var reconcileAnnotation = Annotation.define();
107
+ var isReconcile = (tr) => {
108
+ return !!tr.annotation(reconcileAnnotation);
109
+ };
107
110
 
108
111
  // packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
109
- import { next as automerge2 } from "@dxos/automerge/automerge";
112
+ import { next as A3 } from "@dxos/automerge/automerge";
110
113
 
111
114
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
112
- import { next as am } from "@dxos/automerge/automerge";
115
+ import { next as A2 } from "@dxos/automerge/automerge";
113
116
  var updateAutomerge = (field, handle, transactions, state) => {
114
117
  const { lastHeads, path } = state.field(field);
115
118
  let hasChanges = false;
@@ -122,11 +125,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
122
125
  return void 0;
123
126
  }
124
127
  const newHeads = handle.changeAt(lastHeads, (doc) => {
128
+ const invertedTransactions = [];
125
129
  for (const tr of transactions) {
126
- tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
127
- am.splice(doc, path, fromA, toA - fromA, inserted.toString());
130
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
131
+ invertedTransactions.push({
132
+ from: fromA,
133
+ del: toA - fromA,
134
+ insert
135
+ });
128
136
  });
129
137
  }
138
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
139
+ A2.splice(doc, path.slice(), from, del, insert.toString());
140
+ });
130
141
  });
131
142
  return newHeads ?? void 0;
132
143
  };
@@ -141,13 +152,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
141
152
  selection = selection.map(changeSet, 1);
142
153
  view.dispatch({
143
154
  changes: changeSet,
144
- annotations: reconcileAnnotationType.of({})
155
+ annotations: reconcileAnnotation.of(false)
145
156
  });
146
157
  }
147
158
  }
148
159
  view.dispatch({
149
160
  selection,
150
- annotations: reconcileAnnotationType.of({})
161
+ annotations: reconcileAnnotation.of(false)
151
162
  });
152
163
  };
153
164
  var handlePatch = (patch, target, state) => {
@@ -168,7 +179,7 @@ var handleInsert = (target, patch) => {
168
179
  if (index == null) {
169
180
  return [];
170
181
  }
171
- const text = patch.values.map((v) => v ? v.toString() : "").join("");
182
+ const text = patch.values.map((value) => value ? value.toString() : "").join("");
172
183
  return [
173
184
  {
174
185
  from: index,
@@ -246,36 +257,37 @@ var PatchSemaphore = class {
246
257
  this._state = _state;
247
258
  this._inReconcile = false;
248
259
  }
249
- // NOTE: Cannot destruct view.state.
250
260
  reconcile(view) {
251
- if (this._inReconcile) {
252
- return;
261
+ if (!this._inReconcile) {
262
+ this._inReconcile = true;
263
+ this.doReconcile(view);
264
+ this._inReconcile = false;
253
265
  }
254
- this._inReconcile = true;
266
+ }
267
+ doReconcile(view) {
255
268
  const path = getPath(view.state, this._state);
256
269
  const oldHeads = getLastHeads(view.state, this._state);
257
270
  let selection = view.state.selection;
258
- const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
271
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
259
272
  const toInvert = transactions.slice().reverse();
260
- for (const tx of toInvert) {
261
- const inverted = tx.changes.invert(tx.startState.doc);
273
+ for (const tr of toInvert) {
274
+ const inverted = tr.changes.invert(tr.startState.doc);
262
275
  selection = selection.map(inverted);
263
276
  view.dispatch({
264
277
  changes: inverted,
265
- annotations: reconcileAnnotationType.of(true)
278
+ annotations: reconcileAnnotation.of(true)
266
279
  });
267
280
  }
268
281
  let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
269
282
  if (newHeads === null || newHeads === void 0) {
270
- newHeads = automerge2.getHeads(this._handle.docSync());
283
+ newHeads = A3.getHeads(this._handle.docSync());
271
284
  }
272
- const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
285
+ const diff = A3.equals(oldHeads, newHeads) ? [] : A3.diff(this._handle.docSync(), oldHeads, newHeads);
273
286
  updateCodeMirror(view, selection, path, diff);
274
287
  view.dispatch({
275
288
  effects: updateHeads(newHeads),
276
- annotations: reconcileAnnotationType.of({})
289
+ annotations: reconcileAnnotation.of(false)
277
290
  });
278
- this._inReconcile = false;
279
291
  }
280
292
  };
281
293
 
@@ -317,12 +329,11 @@ var Cursor = class _Cursor {
317
329
  };
318
330
 
319
331
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
320
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts";
321
- var automerge3 = ({ handle, path }) => {
322
- const state = StateField.define({
332
+ var automerge = ({ handle, path }) => {
333
+ const syncState = StateField.define({
323
334
  create: () => ({
324
335
  path: path.slice(),
325
- lastHeads: A.getHeads(handle.docSync()),
336
+ lastHeads: A4.getHeads(handle.docSync()),
326
337
  unreconciledTransactions: []
327
338
  }),
328
339
  update: (value, tr) => {
@@ -333,7 +344,7 @@ var automerge3 = ({ handle, path }) => {
333
344
  };
334
345
  let clearUnreconciled = false;
335
346
  for (const effect of tr.effects) {
336
- if (effect.is(effectType)) {
347
+ if (effect.is(updateHeadsEffect)) {
337
348
  result.lastHeads = effect.value.newHeads;
338
349
  clearUnreconciled = true;
339
350
  }
@@ -341,29 +352,21 @@ var automerge3 = ({ handle, path }) => {
341
352
  if (clearUnreconciled) {
342
353
  result.unreconciledTransactions = [];
343
354
  } else {
344
- if (!isReconcileTx(tr)) {
355
+ if (!isReconcile(tr)) {
345
356
  result.unreconciledTransactions.push(tr);
346
357
  }
347
358
  }
348
359
  return result;
349
360
  }
350
361
  });
351
- const semaphore = new PatchSemaphore(handle, state);
362
+ const semaphore = new PatchSemaphore(handle, syncState);
352
363
  return [
353
364
  Cursor.converter.of(cursorConverter(handle, path)),
365
+ syncState,
354
366
  // Reconcile external updates.
355
367
  ViewPlugin.fromClass(class {
356
368
  constructor(_view) {
357
369
  this._view = _view;
358
- invariant(this._view, void 0, {
359
- F: __dxlog_file,
360
- L: 66,
361
- S: this,
362
- A: [
363
- "this._view",
364
- ""
365
- ]
366
- });
367
370
  handle.addListener("change", this._handleChange.bind(this));
368
371
  }
369
372
  destroy() {
@@ -379,7 +382,17 @@ var automerge3 = ({ handle, path }) => {
379
382
  semaphore.reconcile(view);
380
383
  }
381
384
  }),
382
- state
385
+ // TODO(burdon): Record undo transactions.
386
+ // See https://github.com/yjs/y-codemirror.next/tree/main
387
+ // https://codemirror.net/examples/inverted-effect
388
+ invertedEffects.of((tr) => {
389
+ const effects = [];
390
+ if (!tr.changes.empty) {
391
+ tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
392
+ });
393
+ }
394
+ return effects;
395
+ })
383
396
  ];
384
397
  };
385
398
 
@@ -398,13 +411,13 @@ var dummyProvider = {
398
411
  update: () => {
399
412
  }
400
413
  };
401
- var AwarenessProvider = Facet2.define({
414
+ var awarenessProvider = Facet2.define({
402
415
  combine: (providers) => providers[0] ?? dummyProvider
403
416
  });
404
417
  var RemoteSelectionChangedAnnotation = Annotation2.define();
405
418
  var awareness = (provider = dummyProvider) => {
406
419
  return [
407
- AwarenessProvider.of(provider),
420
+ awarenessProvider.of(provider),
408
421
  ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
409
422
  decorations: (value) => value.decorations
410
423
  }),
@@ -418,7 +431,7 @@ var RemoteSelectionsDecorator = class {
418
431
  this._lastAnchor = void 0;
419
432
  this._lastHead = void 0;
420
433
  this._cursorConverter = view.state.facet(Cursor.converter);
421
- this._provider = view.state.facet(AwarenessProvider);
434
+ this._provider = view.state.facet(awarenessProvider);
422
435
  this._provider.open();
423
436
  this._provider.remoteStateChange.on(this._ctx, () => {
424
437
  view.dispatch({
@@ -624,24 +637,25 @@ import { history } from "@codemirror/commands";
624
637
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
625
638
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
626
639
  import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
627
- var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
628
- lineWrapping ? EditorView3.lineWrapping : [],
640
+ import { isNotFalsy } from "@dxos/util";
641
+ var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => [
642
+ lineWrapping && EditorView3.lineWrapping,
629
643
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
630
644
  bracketMatching(),
631
645
  closeBrackets(),
632
646
  drawSelection(),
633
647
  highlightActiveLine(),
634
648
  history(),
635
- _placeholder ? placeholder(_placeholder) : [],
649
+ _placeholder && placeholder(_placeholder),
636
650
  // https://github.com/codemirror/theme-one-dark
637
651
  themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
638
- ];
652
+ ].filter(isNotFalsy);
639
653
 
640
654
  // packages/ui/react-ui-editor/src/extensions/blast.ts
641
655
  import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
642
656
  import defaultsDeep from "lodash.defaultsdeep";
643
- import { invariant as invariant2 } from "@dxos/invariant";
644
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
657
+ import { invariant } from "@dxos/invariant";
658
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
645
659
  var defaultOptions = {
646
660
  effect: 2,
647
661
  maxParticles: 200,
@@ -767,9 +781,9 @@ var Blaster = class {
767
781
  return this._node;
768
782
  }
769
783
  initialize() {
770
- invariant2(!this._canvas && !this._ctx, void 0, {
771
- F: __dxlog_file2,
772
- L: 138,
784
+ invariant(!this._canvas && !this._ctx, void 0, {
785
+ F: __dxlog_file,
786
+ L: 141,
773
787
  S: this,
774
788
  A: [
775
789
  "!this._canvas && !this._ctx",
@@ -804,9 +818,9 @@ var Blaster = class {
804
818
  }
805
819
  }
806
820
  start() {
807
- invariant2(this._canvas && this._ctx, void 0, {
808
- F: __dxlog_file2,
809
- L: 177,
821
+ invariant(this._canvas && this._ctx, void 0, {
822
+ F: __dxlog_file,
823
+ L: 180,
810
824
  S: this,
811
825
  A: [
812
826
  "this._canvas && this._ctx",
@@ -948,12 +962,12 @@ var random = (min, max) => {
948
962
  };
949
963
 
950
964
  // packages/ui/react-ui-editor/src/extensions/comments.ts
951
- import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
965
+ import { invertedEffects as invertedEffects2 } from "@codemirror/commands";
966
+ import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
952
967
  import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
953
968
  import sortBy from "lodash.sortby";
954
969
  import { useEffect } from "react";
955
970
  import { debounce } from "@dxos/async";
956
- import { invariant as invariant3 } from "@dxos/invariant";
957
971
  import { nonNullable } from "@dxos/util";
958
972
 
959
973
  // packages/ui/react-ui-editor/src/styles/cursors.ts
@@ -1025,13 +1039,13 @@ var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue)
1025
1039
 
1026
1040
  // packages/ui/react-ui-editor/src/util.ts
1027
1041
  import { log } from "@dxos/log";
1028
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1042
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
1029
1043
  var callbackWrapper = (fn) => (...args) => {
1030
1044
  try {
1031
1045
  return fn(...args);
1032
1046
  } catch (err) {
1033
1047
  log.catch(err, void 0, {
1034
- F: __dxlog_file3,
1048
+ F: __dxlog_file2,
1035
1049
  L: 18,
1036
1050
  S: void 0,
1037
1051
  C: (f, a) => f(...a)
@@ -1059,24 +1073,35 @@ var logChanges = (trs) => {
1059
1073
  };
1060
1074
 
1061
1075
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1062
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
1063
1076
  var styles2 = EditorView5.baseTheme({
1064
- "& .cm-comment": {
1077
+ "&light .cm-comment, &light .cm-comment-current": {
1078
+ mixBlendMode: "darken"
1079
+ },
1080
+ "&dark .cm-comment, &dark .cm-comment-current": {
1081
+ mixBlendMode: "plus-lighter"
1082
+ },
1083
+ "&light .cm-comment": {
1065
1084
  backgroundColor: getToken("extend.colors.yellow.50")
1066
1085
  },
1067
- "& .cm-comment-current": {
1086
+ "&light .cm-comment-current": {
1068
1087
  backgroundColor: getToken("extend.colors.yellow.100")
1088
+ },
1089
+ "&dark .cm-comment": {
1090
+ color: getToken("extend.colors.yellow.50"),
1091
+ backgroundColor: getToken("extend.colors.yellow.900")
1092
+ },
1093
+ "&dark .cm-comment-current": {
1094
+ color: getToken("extend.colors.yellow.100"),
1095
+ backgroundColor: getToken("extend.colors.yellow.950")
1069
1096
  }
1070
1097
  });
1071
- var marks = {
1072
- highlight: Decoration2.mark({
1073
- class: "cm-comment"
1074
- }),
1075
- highlightActive: Decoration2.mark({
1076
- class: "cm-comment-current"
1077
- })
1078
- };
1079
- var setFocus = (view, id, center = true) => {
1098
+ var commentMark = Decoration2.mark({
1099
+ class: "cm-comment"
1100
+ });
1101
+ var commentCurrentMark = Decoration2.mark({
1102
+ class: "cm-comment-current"
1103
+ });
1104
+ var focusComment = (view, id, center = true) => {
1080
1105
  const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
1081
1106
  if (!comment?.comment.cursor) {
1082
1107
  return;
@@ -1138,7 +1163,7 @@ var commentsState = StateField2.define({
1138
1163
  return value;
1139
1164
  }
1140
1165
  });
1141
- var highlightDecorations = EditorView5.decorations.compute([
1166
+ var commentsDecorations = EditorView5.decorations.compute([
1142
1167
  commentsState
1143
1168
  ], (state) => {
1144
1169
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -1149,9 +1174,9 @@ var highlightDecorations = EditorView5.decorations.compute([
1149
1174
  return void 0;
1150
1175
  }
1151
1176
  if (comment.comment.id === current) {
1152
- return marks.highlightActive.range(range.from, range.to);
1177
+ return commentCurrentMark.range(range.from, range.to);
1153
1178
  } else {
1154
- return marks.highlight.range(range.from, range.to);
1179
+ return commentMark.range(range.from, range.to);
1155
1180
  }
1156
1181
  }).filter(nonNullable);
1157
1182
  return Decoration2.set(decorations);
@@ -1180,84 +1205,128 @@ var trackPastedComments = (onUpdate) => {
1180
1205
  cut: handleTrack,
1181
1206
  copy: handleTrack
1182
1207
  }),
1183
- // Handle paste.
1184
- EditorView5.updateListener.of(({ view, state, transactions }) => {
1208
+ // Track deleted comments.
1209
+ invertedEffects2.of((tr) => {
1210
+ const { comments: comments2 } = tr.startState.field(commentsState);
1211
+ const effects = [];
1212
+ tr.changes.iterChangedRanges((fromA, toA) => {
1213
+ for (const { comment: { id }, range: { from, to } } of comments2) {
1214
+ if (from < to && from >= fromA && to <= toA) {
1215
+ effects.push(restoreCommentEffect.of({
1216
+ id,
1217
+ from,
1218
+ to
1219
+ }));
1220
+ }
1221
+ }
1222
+ });
1223
+ return effects;
1224
+ }),
1225
+ // Handle paste or the undo of comment deletion.
1226
+ EditorView5.updateListener.of((update2) => {
1227
+ const restore = [];
1228
+ for (let i = 0; i < update2.transactions.length; i++) {
1229
+ const tr = update2.transactions[i];
1230
+ for (let j = 0; j < restore.length; j++) {
1231
+ restore[j] = mapTrackedComment(restore[j], tr.changes);
1232
+ }
1233
+ for (const effect of tr.effects) {
1234
+ if (effect.is(restoreCommentEffect)) {
1235
+ restore.push(effect.value);
1236
+ }
1237
+ }
1238
+ }
1185
1239
  if (tracked) {
1186
- const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
1240
+ const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
1187
1241
  if (paste) {
1188
1242
  let found = -1;
1189
1243
  paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
1190
1244
  if (text.eq(tracked.text)) {
1191
- for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
1192
- fromB = transactions[i].changes.mapPos(fromB);
1245
+ for (let i = update2.transactions.indexOf(paste) + 1; i < update2.transactions.length; i++) {
1246
+ fromB = update2.transactions[i].changes.mapPos(fromB);
1193
1247
  }
1194
1248
  found = fromB;
1195
1249
  }
1196
1250
  });
1197
1251
  if (found > -1) {
1198
1252
  for (const moved of tracked.comments) {
1199
- const range = {
1253
+ restore.push({
1254
+ id: moved.id,
1200
1255
  from: found + moved.from,
1201
1256
  to: found + moved.to
1202
- };
1203
- const cursor = Cursor.getCursorFromRange(state, range);
1204
- onUpdate(moved.id, cursor);
1257
+ });
1205
1258
  }
1206
1259
  }
1207
1260
  tracked = null;
1208
1261
  }
1209
1262
  }
1263
+ for (const comment of restore) {
1264
+ const { comments: comments2 } = update2.startState.field(commentsState);
1265
+ const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
1266
+ if (!exists) {
1267
+ const cursor = Cursor.getCursorFromRange(update2.state, comment);
1268
+ onUpdate(comment.id, cursor);
1269
+ }
1270
+ }
1210
1271
  })
1211
1272
  ];
1212
1273
  };
1213
- var comments = (options = {}) => {
1214
- const { key: shortcut = "meta-'" } = options;
1215
- const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1216
- const createCommentThread = (view) => {
1217
- invariant3(options.onCreate, void 0, {
1218
- F: __dxlog_file4,
1219
- L: 259,
1220
- S: void 0,
1221
- A: [
1222
- "options.onCreate",
1223
- ""
1224
- ]
1274
+ var mapTrackedComment = (comment, changes) => ({
1275
+ id: comment.id,
1276
+ from: changes.mapPos(comment.from, 1),
1277
+ to: changes.mapPos(comment.to, 1)
1278
+ });
1279
+ var restoreCommentEffect = StateEffect2.define({
1280
+ map: mapTrackedComment
1281
+ });
1282
+ var optionsFacet = Facet3.define({
1283
+ combine: (providers) => providers[0]
1284
+ });
1285
+ var createComment = (view) => {
1286
+ const options = view.state.facet(optionsFacet);
1287
+ const { from, to } = view.state.selection.main;
1288
+ if (from === to) {
1289
+ return false;
1290
+ }
1291
+ if (to === view.state.doc.length) {
1292
+ view.dispatch({
1293
+ changes: {
1294
+ from: to,
1295
+ insert: "\n"
1296
+ }
1225
1297
  });
1226
- const { from, to } = view.state.selection.main;
1227
- if (from === to) {
1228
- return false;
1229
- }
1230
- if (to === view.state.doc.length) {
1298
+ }
1299
+ const cursor = Cursor.getCursorFromRange(view.state, {
1300
+ from,
1301
+ to
1302
+ });
1303
+ if (cursor) {
1304
+ const id = options.onCreate?.({
1305
+ cursor,
1306
+ from,
1307
+ location: view.coordsAtPos(from)
1308
+ });
1309
+ if (id) {
1231
1310
  view.dispatch({
1232
- changes: {
1233
- from: to,
1234
- insert: "\n"
1311
+ effects: setSelection.of({
1312
+ current: id
1313
+ }),
1314
+ selection: {
1315
+ anchor: to
1235
1316
  }
1236
1317
  });
1318
+ return true;
1237
1319
  }
1238
- const cursor = Cursor.getCursorFromRange(view.state, {
1239
- from,
1240
- to
1241
- });
1242
- if (cursor) {
1243
- const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1244
- if (id) {
1245
- view.dispatch({
1246
- effects: setSelection.of({
1247
- current: id
1248
- }),
1249
- selection: {
1250
- anchor: from
1251
- }
1252
- });
1253
- return true;
1254
- }
1255
- }
1256
- return false;
1257
- };
1320
+ }
1321
+ return false;
1322
+ };
1323
+ var comments = (options = {}) => {
1324
+ const { key: shortcut = "meta-'" } = options;
1325
+ const handleSelect = debounce((state) => options.onSelect?.(state), 200);
1258
1326
  return [
1327
+ optionsFacet.of(options),
1259
1328
  commentsState,
1260
- highlightDecorations,
1329
+ commentsDecorations,
1261
1330
  styles2,
1262
1331
  //
1263
1332
  // Keymap.
@@ -1265,7 +1334,7 @@ var comments = (options = {}) => {
1265
1334
  options.onCreate ? keymap3.of([
1266
1335
  {
1267
1336
  key: shortcut,
1268
- run: callbackWrapper(createCommentThread)
1337
+ run: callbackWrapper(createComment)
1269
1338
  }
1270
1339
  ]) : [],
1271
1340
  //
@@ -1403,6 +1472,7 @@ import { searchKeymap } from "@codemirror/search";
1403
1472
  import { EditorState } from "@codemirror/state";
1404
1473
  import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1405
1474
  import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1475
+ import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
1406
1476
 
1407
1477
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
1408
1478
  import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
@@ -1593,11 +1663,11 @@ var markdownHighlightStyle = (readonly) => {
1593
1663
  };
1594
1664
 
1595
1665
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1596
- var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1666
+ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1597
1667
  return [
1668
+ lineWrapping && EditorView7.lineWrapping,
1598
1669
  EditorState.allowMultipleSelections.of(true),
1599
1670
  EditorState.tabSize.of(2),
1600
- EditorView7.lineWrapping,
1601
1671
  // https://github.com/codemirror/basic-setup
1602
1672
  bracketMatching2(),
1603
1673
  closeBrackets2(),
@@ -1607,7 +1677,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1607
1677
  highlightActiveLine2(),
1608
1678
  history2(),
1609
1679
  indentOnInput(),
1610
- _placeholder ? placeholder2(_placeholder) : [],
1680
+ _placeholder && placeholder2(_placeholder),
1611
1681
  // Main extension.
1612
1682
  // https://github.com/codemirror/lang-markdown
1613
1683
  // https://codemirror.net/5/mode/markdown/index.html (demo).
@@ -1642,7 +1712,7 @@ var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
1642
1712
  // https://codemirror.net/docs/ref/#commands.standardKeymap
1643
1713
  ...standardKeymap
1644
1714
  ])
1645
- ];
1715
+ ].filter(isNotFalsy2);
1646
1716
  };
1647
1717
 
1648
1718
  // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
@@ -1652,26 +1722,43 @@ import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Dec
1652
1722
  import { mx as mx2 } from "@dxos/react-ui-theme";
1653
1723
  var styles3 = EditorView8.baseTheme({
1654
1724
  "& .cm-code": {
1655
- paddingInline: "1rem !important",
1656
1725
  fontFamily: getToken("fontFamily.mono", []).join(",")
1657
1726
  },
1658
1727
  "& .cm-codeblock": {
1659
- display: "inline-block",
1660
- width: "100%"
1728
+ width: "100%",
1729
+ paddingInline: "1rem !important",
1730
+ position: "relative",
1731
+ "&::after": {
1732
+ content: '""',
1733
+ position: "absolute",
1734
+ inset: 0
1735
+ }
1661
1736
  },
1662
- "&light .cm-codeblock, &light .cm-codeblock.cm-activeLine": {
1663
- background: getToken("extend.colors.neutral.25")
1737
+ "&light .cm-codeblock": {
1738
+ "&::after": {
1739
+ background: getToken("extend.semanticColors.input.light"),
1740
+ mixBlendMode: "darken"
1741
+ }
1664
1742
  },
1665
- "&dark .cm-codeblock, &dark .cm-codeblock.cm-activeLine": {
1666
- background: getToken("extend.colors.neutral.850")
1743
+ "&dark .cm-codeblock": {
1744
+ "&::after": {
1745
+ background: getToken("extend.semanticColors.input.dark"),
1746
+ mixBlendMode: "lighten"
1747
+ }
1667
1748
  },
1668
1749
  "& .cm-codeblock-first": {
1669
- borderTopLeftRadius: ".5rem",
1670
- borderTopRightRadius: ".5rem"
1750
+ display: "inline-block",
1751
+ "&::after": {
1752
+ borderTopLeftRadius: ".5rem",
1753
+ borderTopRightRadius: ".5rem"
1754
+ }
1671
1755
  },
1672
1756
  "& .cm-codeblock-last": {
1673
- borderBottomLeftRadius: ".5rem",
1674
- borderBottomRightRadius: ".5rem"
1757
+ display: "inline-block",
1758
+ "&::after": {
1759
+ borderBottomLeftRadius: ".5rem",
1760
+ borderBottomRightRadius: ".5rem"
1761
+ }
1675
1762
  }
1676
1763
  });
1677
1764
  var getLineRange = (lines, from, to) => {
@@ -1715,7 +1802,7 @@ var buildDecorations = (view) => {
1715
1802
  }));
1716
1803
  } else {
1717
1804
  builder.add(block.from, block.from, Decoration3.line({
1718
- class: mx2("cm-code cm-codeblock")
1805
+ class: mx2("cm-code cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1719
1806
  }));
1720
1807
  }
1721
1808
  }
@@ -1743,98 +1830,1188 @@ var code2 = () => {
1743
1830
  ];
1744
1831
  };
1745
1832
 
1746
- // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1833
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1834
+ import { snippet } from "@codemirror/autocomplete";
1747
1835
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1748
- import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1749
- import { Decoration as Decoration4, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1750
- var buildDecorations2 = (view) => {
1751
- const builder = new RangeSetBuilder2();
1752
- const { state } = view;
1753
- const cursor = state.selection.main.head;
1754
- for (const { from, to } of view.visibleRanges) {
1836
+ import { RangeSetBuilder as RangeSetBuilder2, EditorSelection } from "@codemirror/state";
1837
+ import { Decoration as Decoration4, EditorView as EditorView9, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1838
+ import { useState, useMemo } from "react";
1839
+ 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;
1840
+ var Inline;
1841
+ (function(Inline2) {
1842
+ Inline2[Inline2["Strong"] = 0] = "Strong";
1843
+ Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
1844
+ Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
1845
+ Inline2[Inline2["Code"] = 3] = "Code";
1846
+ })(Inline || (Inline = {}));
1847
+ var List;
1848
+ (function(List2) {
1849
+ List2[List2["Ordered"] = 0] = "Ordered";
1850
+ List2[List2["Bullet"] = 1] = "Bullet";
1851
+ List2[List2["Task"] = 2] = "Task";
1852
+ })(List || (List = {}));
1853
+ var setHeading = (level) => ({ state, dispatch }) => {
1854
+ const { selection: { ranges }, doc } = state;
1855
+ const changes = [];
1856
+ let prevBlock = -1;
1857
+ for (const range of ranges) {
1755
1858
  syntaxTree2(state).iterate({
1859
+ from: range.from,
1860
+ to: range.to,
1756
1861
  enter: (node) => {
1757
- switch (node.name) {
1758
- case "ATXHeading1":
1759
- case "ATXHeading2":
1760
- case "ATXHeading3":
1761
- case "ATXHeading4":
1762
- case "ATXHeading5":
1763
- case "ATXHeading6": {
1764
- const mark2 = node.node.getChild("HeaderMark");
1765
- if (mark2) {
1766
- if (view.state.readOnly || cursor < node.from || cursor > node.to) {
1767
- builder.add(mark2.from, mark2.to + 1, Decoration4.replace({}));
1862
+ if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
1863
+ return;
1864
+ }
1865
+ prevBlock = node.from;
1866
+ const blockType = Textblocks[node.name];
1867
+ const isHeading = /heading(\d)/.exec(blockType);
1868
+ const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
1869
+ if (curLevel < 0 || curLevel === level) {
1870
+ return;
1871
+ }
1872
+ if (curLevel === 0) {
1873
+ changes.push({
1874
+ from: node.from,
1875
+ insert: "#".repeat(level) + " "
1876
+ });
1877
+ } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
1878
+ const nextLine = doc.lineAt(node.to);
1879
+ if (level) {
1880
+ changes.push({
1881
+ from: node.from,
1882
+ insert: "#".repeat(level) + " "
1883
+ });
1884
+ }
1885
+ changes.push({
1886
+ from: nextLine.from - 1,
1887
+ to: nextLine.to
1888
+ });
1889
+ } else {
1890
+ if (level === 0) {
1891
+ changes.push({
1892
+ from: node.from,
1893
+ to: Math.min(node.to, node.from + curLevel + 1)
1894
+ });
1895
+ } else if (level < curLevel) {
1896
+ changes.push({
1897
+ from: node.from,
1898
+ to: node.from + (curLevel - level)
1899
+ });
1900
+ } else {
1901
+ changes.push({
1902
+ from: node.from,
1903
+ insert: "#".repeat(level - curLevel)
1904
+ });
1905
+ }
1906
+ }
1907
+ }
1908
+ });
1909
+ }
1910
+ if (!changes.length) {
1911
+ return false;
1912
+ }
1913
+ dispatch(state.update({
1914
+ changes,
1915
+ userEvent: "format.setHeading",
1916
+ scrollIntoView: true
1917
+ }));
1918
+ return true;
1919
+ };
1920
+ var setStyle = (type, enable) => ({ state, dispatch }) => {
1921
+ const marker = inlineMarkerText(type);
1922
+ const changes = state.changeByRange((range) => {
1923
+ if (!enable && range.empty) {
1924
+ const after = state.doc.sliceString(range.head, range.head + 6);
1925
+ const found = after.indexOf(marker);
1926
+ if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
1927
+ const before = state.doc.sliceString(range.head - 6, range.head);
1928
+ if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
1929
+ ...before.slice(before.length - found)
1930
+ ].reverse().join("") === after.slice(0, found)) {
1931
+ return {
1932
+ changes: [
1933
+ {
1934
+ from: range.head - marker.length - found,
1935
+ to: range.head - found
1936
+ },
1937
+ {
1938
+ from: range.head + found,
1939
+ to: range.head + found + marker.length
1940
+ }
1941
+ ],
1942
+ range: EditorSelection.cursor(range.from - marker.length)
1943
+ };
1944
+ }
1945
+ }
1946
+ }
1947
+ const changes2 = [];
1948
+ const changesAtEnd = [];
1949
+ let blockStart = -1;
1950
+ let blockEnd = -1;
1951
+ let startCovered = false;
1952
+ let endCovered = false;
1953
+ let { from, to } = range;
1954
+ syntaxTree2(state).iterate({
1955
+ from,
1956
+ to,
1957
+ enter: (node) => {
1958
+ const { name } = node;
1959
+ if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
1960
+ blockStart = blockContentStart(node);
1961
+ blockEnd = blockContentEnd(node, state.doc);
1962
+ startCovered = endCovered = false;
1963
+ } else if (name === "Link" || name === "Image" && enable) {
1964
+ if (from < node.from && to > node.from && to <= node.to) {
1965
+ to = node.to;
1966
+ } else if (to > node.to && from >= node.from && from < node.to) {
1967
+ from = node.from;
1968
+ }
1969
+ } else if (IgnoreInline.has(name) && enable) {
1970
+ if (node.from < from && node.to > from) {
1971
+ if (to === from) {
1972
+ to = node.to;
1973
+ }
1974
+ from = node.to;
1975
+ }
1976
+ if (node.from < to && node.to > to) {
1977
+ to = node.from;
1978
+ }
1979
+ } else if (Object.hasOwn(InlineMarker, name)) {
1980
+ const markType = InlineMarker[name];
1981
+ const size = inlineMarkerText(markType).length;
1982
+ const openEnd = node.from + size;
1983
+ const closeStart = node.to - size;
1984
+ if (markType === type) {
1985
+ if (openEnd <= from && closeStart >= from) {
1986
+ startCovered = openEnd === from ? "adjacent" : true;
1987
+ }
1988
+ if (openEnd <= to && closeStart >= to) {
1989
+ endCovered = closeStart === to ? "adjacent" : true;
1990
+ }
1991
+ }
1992
+ if (markType === type || type === 3 && enable) {
1993
+ if (node.from >= from && openEnd <= to) {
1994
+ changes2.push({
1995
+ from: node.from,
1996
+ to: openEnd
1997
+ });
1998
+ if (markType !== type && closeStart >= to) {
1999
+ changesAtEnd.push({
2000
+ from: skipSpaces(Math.min(to, blockEnd), state.doc, 1, blockEnd),
2001
+ insert: inlineMarkerText(markType)
2002
+ });
2003
+ }
2004
+ }
2005
+ if (closeStart >= from && node.to <= to) {
2006
+ changes2.push({
2007
+ from: closeStart,
2008
+ to: node.to
2009
+ });
2010
+ if (markType !== type && openEnd <= from) {
2011
+ changes2.push({
2012
+ from: skipSpaces(Math.max(from, blockStart), state.doc, -1, blockStart),
2013
+ insert: inlineMarkerText(markType)
2014
+ });
1768
2015
  }
1769
2016
  }
1770
2017
  }
1771
2018
  }
1772
2019
  },
1773
- from,
1774
- to
2020
+ leave: (node) => {
2021
+ if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
2022
+ const rangeStart = Math.max(from, blockStart);
2023
+ const rangeEnd = Math.min(to, blockEnd);
2024
+ if (enable) {
2025
+ if (!startCovered) {
2026
+ changes2.push({
2027
+ from: rangeStart,
2028
+ insert: marker
2029
+ });
2030
+ }
2031
+ if (!endCovered) {
2032
+ changes2.push({
2033
+ from: rangeEnd,
2034
+ insert: marker
2035
+ });
2036
+ }
2037
+ } else {
2038
+ if (startCovered === "adjacent") {
2039
+ changes2.push({
2040
+ from: from - marker.length,
2041
+ to: from
2042
+ });
2043
+ } else if (startCovered) {
2044
+ changes2.push({
2045
+ from: skipSpaces(rangeStart, state.doc, -1, blockStart),
2046
+ insert: marker
2047
+ });
2048
+ }
2049
+ if (endCovered === "adjacent") {
2050
+ changes2.push({
2051
+ from: to,
2052
+ to: to + marker.length
2053
+ });
2054
+ } else if (endCovered) {
2055
+ changes2.push({
2056
+ from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
2057
+ insert: marker
2058
+ });
2059
+ }
2060
+ }
2061
+ }
2062
+ }
1775
2063
  });
2064
+ const changeSet = state.changes(changes2.concat(changesAtEnd));
2065
+ return {
2066
+ changes: changeSet,
2067
+ range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
2068
+ };
2069
+ });
2070
+ dispatch(state.update(changes, {
2071
+ userEvent: enable ? "format.style.add" : "format.style.remove",
2072
+ scrollIntoView: true
2073
+ }));
2074
+ return true;
2075
+ };
2076
+ var addStyle = (style) => setStyle(style, true);
2077
+ var removeStyle = (style) => setStyle(style, false);
2078
+ var toggleStyle = (style) => (arg) => {
2079
+ const form = getFormatting(arg.state);
2080
+ return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
2081
+ };
2082
+ var toggleStrong = toggleStyle(0);
2083
+ var toggleEmphasis = toggleStyle(1);
2084
+ var toggleStrikethrough = toggleStyle(2);
2085
+ var toggleInlineCode = toggleStyle(3);
2086
+ var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
2087
+ var blockContentStart = (node) => {
2088
+ const atx = /^ATXHeading(\d)/.exec(node.name);
2089
+ if (atx) {
2090
+ return Math.min(node.to, node.from + +atx[1] + 1);
1776
2091
  }
1777
- return builder.finish();
2092
+ return node.from;
1778
2093
  };
1779
- var heading2 = () => {
1780
- return [
1781
- ViewPlugin4.fromClass(class {
1782
- constructor(view) {
1783
- this.decorations = buildDecorations2(view);
1784
- }
1785
- update(update2) {
1786
- this.decorations = buildDecorations2(update2.view);
2094
+ var blockContentEnd = (node, doc) => {
2095
+ const setext = /^SetextHeading(\d)/.exec(node.name);
2096
+ const lastLine = doc.lineAt(node.to);
2097
+ if (setext || /^[\s>]*$/.exec(lastLine.text)) {
2098
+ return lastLine.from - 1;
2099
+ }
2100
+ return node.to;
2101
+ };
2102
+ var skipSpaces = (pos, doc, dir, limit) => {
2103
+ const line = doc.lineAt(pos);
2104
+ while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
2105
+ pos += dir;
2106
+ }
2107
+ return pos;
2108
+ };
2109
+ var snippets = {
2110
+ codeblock: snippet([
2111
+ //
2112
+ "```#{}",
2113
+ "#{}",
2114
+ "```"
2115
+ ].join("\n")),
2116
+ table: snippet([
2117
+ //
2118
+ "| #{col1} | #{col2} |",
2119
+ "| ---- | ---- |",
2120
+ "| #{val1} | #{val2} |",
2121
+ "| #{val3} | #{val4} |",
2122
+ ""
2123
+ ].join("\n"))
2124
+ };
2125
+ var insertTable = (view) => {
2126
+ const { selection: { main }, doc } = view.state;
2127
+ const { number } = doc.lineAt(main.anchor);
2128
+ const { from } = doc.line(number);
2129
+ snippets.table(view, null, from, from);
2130
+ };
2131
+ var removeLinkInner = (from, to, changes, state) => {
2132
+ syntaxTree2(state).iterate({
2133
+ from,
2134
+ to,
2135
+ enter: (node) => {
2136
+ if (node.name === "Link" && node.from < to && node.to > from) {
2137
+ node.node.cursor().iterate((node2) => {
2138
+ const { name } = node2;
2139
+ if (name === "LinkMark" || name === "LinkLabel") {
2140
+ changes.push({
2141
+ from: node2.from,
2142
+ to: node2.to
2143
+ });
2144
+ } else if (name === "LinkTitle" || name === "URL") {
2145
+ changes.push({
2146
+ from: skipSpaces(node2.from, state.doc, -1),
2147
+ to: skipSpaces(node2.to, state.doc, 1)
2148
+ });
2149
+ }
2150
+ });
2151
+ return false;
1787
2152
  }
1788
- }, {
1789
- decorations: (value) => value.decorations
1790
- })
1791
- ];
2153
+ }
2154
+ });
1792
2155
  };
1793
-
1794
- // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
1795
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
1796
- import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
1797
- import { Decoration as Decoration5, EditorView as EditorView9, ViewPlugin as ViewPlugin5, WidgetType as WidgetType3 } from "@codemirror/view";
1798
- var styles4 = EditorView9.baseTheme({
1799
- "& .cm-hr": {
1800
- // Note that block-level decorations should not have vertical margins,
1801
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2156
+ var removeLink = ({ state, dispatch }) => {
2157
+ const changes = [];
2158
+ for (const { from, to } of state.selection.ranges) {
2159
+ removeLinkInner(from, to, changes, state);
1802
2160
  }
1803
- });
1804
- var HorizontalRuleWidget = class extends WidgetType3 {
1805
- toDOM() {
1806
- const el = document.createElement("div");
1807
- el.className = "cm-hr";
1808
- return el;
2161
+ if (!changes) {
2162
+ return false;
1809
2163
  }
2164
+ dispatch(state.update({
2165
+ changes,
2166
+ userEvent: "format.link.remove",
2167
+ scrollIntoView: true
2168
+ }));
2169
+ return true;
1810
2170
  };
1811
- var decoration = Decoration5.replace({
1812
- widget: new HorizontalRuleWidget()
1813
- });
1814
- var buildDecorations3 = (view) => {
1815
- const builder = new RangeSetBuilder3();
1816
- const { state } = view;
1817
- const cursor = state.selection.main.head;
1818
- for (const { from, to } of view.visibleRanges) {
1819
- syntaxTree3(state).iterate({
2171
+ var addLink = ({ state, dispatch }) => {
2172
+ const changes = state.changeByRange((range) => {
2173
+ let { from, to } = range;
2174
+ const cutStyles = [];
2175
+ let okay = null;
2176
+ syntaxTree2(state).iterate({
2177
+ from,
2178
+ to,
1820
2179
  enter: (node) => {
1821
- if (node.name === "HorizontalRule") {
1822
- if (cursor <= node.from || cursor >= node.to) {
1823
- builder.add(node.from, node.to, decoration);
2180
+ if (Object.hasOwn(Textblocks, node.name)) {
2181
+ okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state.doc);
2182
+ } else if (Object.hasOwn(InlineMarker, node.name)) {
2183
+ const sNode = node.node;
2184
+ if (node.from < from && node.to <= to) {
2185
+ if (sNode.firstChild.to === from) {
2186
+ from = node.from;
2187
+ } else {
2188
+ cutStyles.push(sNode);
2189
+ }
2190
+ } else if (node.from >= from && node.to > to) {
2191
+ if (sNode.lastChild.from === to) {
2192
+ to = node.to;
2193
+ } else {
2194
+ cutStyles.push(sNode);
2195
+ }
1824
2196
  }
1825
2197
  }
1826
- },
2198
+ }
2199
+ });
2200
+ if (okay === null) {
2201
+ const line = state.doc.lineAt(from);
2202
+ okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
2203
+ }
2204
+ if (!okay) {
2205
+ return {
2206
+ range
2207
+ };
2208
+ }
2209
+ const changes2 = [];
2210
+ const changesAfter = [];
2211
+ removeLinkInner(from, to, changesAfter, state);
2212
+ let cursorOffset = 1;
2213
+ for (const style of cutStyles) {
2214
+ const type = InlineMarker[style.name];
2215
+ const mark2 = inlineMarkerText(type);
2216
+ if (style.from < from) {
2217
+ changes2.push({
2218
+ from: skipSpaces(from, state.doc, -1),
2219
+ insert: mark2
2220
+ });
2221
+ changesAfter.push({
2222
+ from: skipSpaces(from, state.doc, 1, to),
2223
+ insert: mark2
2224
+ });
2225
+ } else {
2226
+ changes2.push({
2227
+ from: skipSpaces(to, state.doc, -1, from),
2228
+ insert: mark2
2229
+ });
2230
+ const after = skipSpaces(to, state.doc, 1);
2231
+ if (after === to) {
2232
+ cursorOffset += mark2.length;
2233
+ }
2234
+ changesAfter.push({
2235
+ from: after,
2236
+ insert: mark2
2237
+ });
2238
+ }
2239
+ }
2240
+ changes2.push({
1827
2241
  from,
1828
- to
2242
+ insert: "["
2243
+ }, {
2244
+ from: to,
2245
+ insert: "]()"
1829
2246
  });
2247
+ const changeSet = state.changes(changes2.concat(changesAfter));
2248
+ return {
2249
+ changes: changeSet,
2250
+ range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
2251
+ };
2252
+ });
2253
+ if (changes.changes.empty) {
2254
+ return false;
1830
2255
  }
1831
- return builder.finish();
2256
+ dispatch(state.update(changes, {
2257
+ userEvent: "format.link.add",
2258
+ scrollIntoView: true
2259
+ }));
2260
+ return true;
1832
2261
  };
1833
- var hr = () => {
1834
- return [
1835
- ViewPlugin5.fromClass(class {
1836
- constructor(view) {
1837
- this.decorations = buildDecorations3(view);
2262
+ var addList = (type) => ({ state, dispatch }) => {
2263
+ let lastBlock = -1;
2264
+ let counter = 1;
2265
+ let first = true;
2266
+ let parentColumn = null;
2267
+ const blocks = [];
2268
+ for (const { from, to } of state.selection.ranges) {
2269
+ syntaxTree2(state).iterate({
2270
+ from,
2271
+ to,
2272
+ enter: (node) => {
2273
+ if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
2274
+ if (first) {
2275
+ let before = node.node.prevSibling;
2276
+ while (before && /Mark$/.test(before.name)) {
2277
+ before = before.prevSibling;
2278
+ }
2279
+ if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
2280
+ const item = before.lastChild;
2281
+ const itemLine = state.doc.lineAt(item.from);
2282
+ const itemText = itemLine.text.slice(item.from - itemLine.from);
2283
+ parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
2284
+ if (type === 0) {
2285
+ const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
2286
+ if (mark2) {
2287
+ parentColumn += mark2[1].length;
2288
+ counter = +mark2[1] + 1;
2289
+ }
2290
+ }
2291
+ }
2292
+ first = false;
2293
+ }
2294
+ if (node.from === lastBlock) {
2295
+ return;
2296
+ }
2297
+ lastBlock = node.from;
2298
+ blocks.push({
2299
+ node: node.node,
2300
+ counter,
2301
+ parentColumn
2302
+ });
2303
+ counter++;
2304
+ return false;
2305
+ }
2306
+ },
2307
+ leave: (node) => {
2308
+ if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
2309
+ counter = 1;
2310
+ parentColumn = null;
2311
+ }
2312
+ }
2313
+ });
2314
+ }
2315
+ if (!blocks.length) {
2316
+ const { from, to } = state.doc.lineAt(state.selection.main.anchor);
2317
+ if (from === to) {
2318
+ dispatch(state.update({
2319
+ changes: [
2320
+ {
2321
+ from,
2322
+ insert: type === 1 ? "- " : type === 0 ? "1. " : "- [ ] "
2323
+ }
2324
+ ],
2325
+ userEvent: "format.list.add",
2326
+ scrollIntoView: true
2327
+ }));
2328
+ return true;
2329
+ }
2330
+ return false;
2331
+ }
2332
+ const changes = [];
2333
+ for (let i = 0; i < blocks.length; i++) {
2334
+ const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
2335
+ const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
2336
+ let padding = nodeFrom > 0 && !/\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
2337
+ if (type === 0) {
2338
+ padding += String(counter2).length;
2339
+ }
2340
+ let line = state.doc.lineAt(nodeFrom);
2341
+ const column = nodeFrom - line.from;
2342
+ if (parentColumn2 !== null && parentColumn2 > column) {
2343
+ padding = Math.max(padding, parentColumn2 - column);
2344
+ }
2345
+ let mark2;
2346
+ if (type === 0) {
2347
+ let max = counter2;
2348
+ for (let j = i + 1; j < blocks.length; j++) {
2349
+ if (blocks[j].counter !== max + 1) {
2350
+ break;
2351
+ }
2352
+ max++;
2353
+ }
2354
+ const num = String(counter2);
2355
+ padding = Math.max(String(max).length, padding);
2356
+ mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
2357
+ } else {
2358
+ mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
2359
+ }
2360
+ changes.push({
2361
+ from: nodeFrom,
2362
+ insert: mark2
2363
+ });
2364
+ while (line.to < node.to) {
2365
+ line = state.doc.lineAt(line.to + 1);
2366
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2367
+ changes.push({
2368
+ from: line.from + Math.min(open, column),
2369
+ insert: " ".repeat(mark2.length)
2370
+ });
2371
+ }
2372
+ }
2373
+ if (type === 0) {
2374
+ const last = blocks[blocks.length - 1];
2375
+ let next = last.node.nextSibling;
2376
+ while (next && /Mark$/.test(next.name)) {
2377
+ next = next.nextSibling;
2378
+ }
2379
+ if (next?.name === "OrderedList") {
2380
+ renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
2381
+ }
2382
+ }
2383
+ dispatch(state.update({
2384
+ changes,
2385
+ userEvent: "format.list.add",
2386
+ scrollIntoView: true
2387
+ }));
2388
+ return true;
2389
+ };
2390
+ var removeList = (type) => ({ state, dispatch }) => {
2391
+ let lastBlock = -1;
2392
+ const changes = [];
2393
+ const stack = [];
2394
+ const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
2395
+ for (const { from, to } of state.selection.ranges) {
2396
+ syntaxTree2(state).iterate({
2397
+ from,
2398
+ to,
2399
+ enter: (node) => {
2400
+ const { name } = node;
2401
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2402
+ stack.push(name);
2403
+ } else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
2404
+ stack[stack.length - 1] = "TaskList";
2405
+ }
2406
+ },
2407
+ leave: (node) => {
2408
+ const { name } = node;
2409
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2410
+ stack.pop();
2411
+ } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
2412
+ lastBlock = node.from;
2413
+ let line = state.doc.lineAt(node.from);
2414
+ const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
2415
+ if (!mark2) {
2416
+ return false;
2417
+ }
2418
+ const column = node.from - line.from;
2419
+ changes.push({
2420
+ from: node.from,
2421
+ to: node.from + mark2[0].length
2422
+ });
2423
+ while (line.to < node.to) {
2424
+ line = state.doc.lineAt(line.to + 1);
2425
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2426
+ if (open > column) {
2427
+ changes.push({
2428
+ from: line.from + column,
2429
+ to: line.from + Math.min(column + mark2[0].length, open)
2430
+ });
2431
+ }
2432
+ }
2433
+ if (node.to >= to) {
2434
+ renumberListItems(node.node.nextSibling, 1, changes, state.doc);
2435
+ }
2436
+ return false;
2437
+ }
2438
+ }
2439
+ });
2440
+ }
2441
+ if (!changes.length) {
2442
+ return false;
2443
+ }
2444
+ dispatch(state.update({
2445
+ changes,
2446
+ userEvent: "format.list.remove",
2447
+ scrollIntoView: true
2448
+ }));
2449
+ return true;
2450
+ };
2451
+ var toggleList = (type) => (target) => {
2452
+ const formatting2 = getFormatting(target.state);
2453
+ const active = formatting2.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
2454
+ return (active ? removeList(type) : addList(type))(target);
2455
+ };
2456
+ var renumberListItems = (item, counter, changes, doc) => {
2457
+ for (; item; item = item.nextSibling) {
2458
+ if (item.name === "ListItem") {
2459
+ const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
2460
+ if (!number || +number[2] === counter) {
2461
+ break;
2462
+ }
2463
+ const size = number[1].length + number[2].length;
2464
+ const newNum = String(counter);
2465
+ changes.push({
2466
+ from: item.from + Math.max(0, size - newNum.length),
2467
+ to: item.from + size,
2468
+ insert: newNum
2469
+ });
2470
+ counter++;
2471
+ }
2472
+ }
2473
+ };
2474
+ var setBlockquote = (enable) => ({ state, dispatch }) => {
2475
+ const lines = [];
2476
+ let lastBlock = -1;
2477
+ for (const { from, to } of state.selection.ranges) {
2478
+ syntaxTree2(state).iterate({
2479
+ from,
2480
+ to,
2481
+ enter: (node) => {
2482
+ if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
2483
+ if (node.from === lastBlock) {
2484
+ return false;
2485
+ }
2486
+ lastBlock = node.from;
2487
+ let line = state.doc.lineAt(node.from);
2488
+ if (line.number > 1) {
2489
+ const prevLine = state.doc.line(line.number - 1);
2490
+ if (/^[>\s]*$/.test(prevLine.text)) {
2491
+ if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
2492
+ lines.push(prevLine);
2493
+ }
2494
+ }
2495
+ }
2496
+ for (; ; ) {
2497
+ lines.push(line);
2498
+ if (line.to >= node.to) {
2499
+ break;
2500
+ }
2501
+ line = state.doc.line(line.number + 1);
2502
+ }
2503
+ if (!enable && line.number < state.doc.lines) {
2504
+ const nextLine = state.doc.line(line.number + 1);
2505
+ if (/^[>\s]*$/.test(nextLine.text)) {
2506
+ lines.push(nextLine);
2507
+ }
2508
+ }
2509
+ return false;
2510
+ }
2511
+ }
2512
+ });
2513
+ }
2514
+ const changes = [];
2515
+ for (const line of lines) {
2516
+ if (enable) {
2517
+ changes.push({
2518
+ from: line.from,
2519
+ insert: /\S/.test(line.text) ? "> " : ">"
2520
+ });
2521
+ } else {
2522
+ const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
2523
+ if (quote) {
2524
+ changes.push({
2525
+ from: line.from + quote[1].length,
2526
+ to: line.from + quote[0].length
2527
+ });
2528
+ }
2529
+ }
2530
+ }
2531
+ if (!changes.length) {
2532
+ return false;
2533
+ }
2534
+ dispatch(state.update({
2535
+ changes,
2536
+ userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
2537
+ scrollIntoView: true
2538
+ }));
2539
+ return true;
2540
+ };
2541
+ var addBlockquote = setBlockquote(true);
2542
+ var removeBlockquote = setBlockquote(false);
2543
+ var toggleBlockquote = (target) => {
2544
+ return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
2545
+ };
2546
+ var addCodeblock = (target) => {
2547
+ const { state, dispatch } = target;
2548
+ const { selection } = state;
2549
+ if (selection.ranges.length === 1 && selection.main.empty) {
2550
+ const { head } = selection.main;
2551
+ const line = state.doc.lineAt(head);
2552
+ if (!/\S/.test(line.text) && head === line.from) {
2553
+ snippets.codeblock(target, null, line.from, line.to);
2554
+ return true;
2555
+ }
2556
+ }
2557
+ const ranges = [];
2558
+ for (const { from, to } of selection.ranges) {
2559
+ let blockFrom = from;
2560
+ let blockTo = to;
2561
+ syntaxTree2(state).iterate({
2562
+ from,
2563
+ to,
2564
+ enter: (node) => {
2565
+ if (Object.hasOwn(Textblocks, node.name)) {
2566
+ if (from >= node.from && to <= node.to) {
2567
+ blockFrom = node.from;
2568
+ blockTo = node.to;
2569
+ } else {
2570
+ blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);
2571
+ blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);
2572
+ }
2573
+ }
2574
+ }
2575
+ });
2576
+ if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
2577
+ ranges[ranges.length - 1].to = blockTo;
2578
+ } else {
2579
+ ranges.push({
2580
+ from: blockFrom,
2581
+ to: blockTo
2582
+ });
2583
+ }
2584
+ }
2585
+ if (!ranges.length) {
2586
+ return false;
2587
+ }
2588
+ const changes = ranges.map(({ from, to }) => {
2589
+ const column = from - state.doc.lineAt(from).from;
2590
+ return [
2591
+ {
2592
+ from,
2593
+ insert: "```\n" + " ".repeat(column)
2594
+ },
2595
+ {
2596
+ from: to,
2597
+ insert: "\n" + " ".repeat(column) + "```"
2598
+ }
2599
+ ];
2600
+ });
2601
+ dispatch(state.update({
2602
+ changes,
2603
+ userEvent: "format.codeblock.add",
2604
+ scrollIntoView: true
2605
+ }));
2606
+ return true;
2607
+ };
2608
+ var removeCodeblock = ({ state, dispatch }) => {
2609
+ const changes = [];
2610
+ let lastBlock = -1;
2611
+ for (const { from, to } of state.selection.ranges) {
2612
+ syntaxTree2(state).iterate({
2613
+ from,
2614
+ to,
2615
+ enter: (node) => {
2616
+ if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
2617
+ lastBlock = node.from;
2618
+ const firstLine = state.doc.lineAt(node.from);
2619
+ if (node.name === "FencedCode") {
2620
+ changes.push({
2621
+ from: node.from,
2622
+ to: firstLine.to + 1 + node.from - firstLine.from
2623
+ });
2624
+ const lastLine = state.doc.lineAt(node.to);
2625
+ if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
2626
+ changes.push({
2627
+ from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
2628
+ to: lastLine.to
2629
+ });
2630
+ }
2631
+ } else {
2632
+ const column = node.from - firstLine.from;
2633
+ for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
2634
+ changes.push({
2635
+ from: line.from + column - 4,
2636
+ to: line.from + column
2637
+ });
2638
+ if (line.to >= node.to) {
2639
+ break;
2640
+ }
2641
+ }
2642
+ }
2643
+ }
2644
+ }
2645
+ });
2646
+ }
2647
+ if (!changes.length) {
2648
+ return false;
2649
+ }
2650
+ dispatch(state.update({
2651
+ changes,
2652
+ userEvent: "format.codeblock.remove",
2653
+ scrollIntoView: true
2654
+ }));
2655
+ return true;
2656
+ };
2657
+ var toggleCodeblock = (target) => {
2658
+ return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
2659
+ };
2660
+ var formatting = (options = {}) => {
2661
+ return [
2662
+ keymap5.of([
2663
+ {
2664
+ key: "meta-b",
2665
+ run: toggleStrong
2666
+ }
2667
+ ]),
2668
+ styling()
2669
+ ];
2670
+ };
2671
+ var styling = () => {
2672
+ const buildDecorations7 = (view) => {
2673
+ const builder = new RangeSetBuilder2();
2674
+ const { state } = view;
2675
+ const cursor = state.selection.main.head;
2676
+ const replace = (node, marks) => {
2677
+ if (cursor <= node.from || cursor >= node.to) {
2678
+ for (const mark2 of marks) {
2679
+ builder.add(mark2.from, mark2.to, Decoration4.replace({}));
2680
+ }
2681
+ }
2682
+ };
2683
+ for (const { from, to } of view.visibleRanges) {
2684
+ syntaxTree2(state).iterate({
2685
+ enter: (node) => {
2686
+ switch (node.name) {
2687
+ case "Emphasis":
2688
+ case "StrongEmphasis": {
2689
+ const marks = node.node.getChildren("EmphasisMark");
2690
+ replace(node, marks);
2691
+ break;
2692
+ }
2693
+ case "Strikethrough": {
2694
+ const marks = node.node.getChildren("StrikethroughMark");
2695
+ replace(node, marks);
2696
+ break;
2697
+ }
2698
+ }
2699
+ },
2700
+ from,
2701
+ to
2702
+ });
2703
+ }
2704
+ return builder.finish();
2705
+ };
2706
+ return [
2707
+ ViewPlugin4.fromClass(class {
2708
+ constructor(view) {
2709
+ this.decorations = buildDecorations7(view);
2710
+ }
2711
+ update(update2) {
2712
+ this.decorations = buildDecorations7(update2.view);
2713
+ }
2714
+ }, {
2715
+ decorations: (value) => value.decorations
2716
+ })
2717
+ ];
2718
+ };
2719
+ var InlineMarker = {
2720
+ Emphasis: 1,
2721
+ StrongEmphasis: 0,
2722
+ InlineCode: 3,
2723
+ Strikethrough: 2
2724
+ };
2725
+ var IgnoreInline = /* @__PURE__ */ new Set([
2726
+ "Autolink",
2727
+ "CodeMark",
2728
+ "CodeText",
2729
+ "Comment",
2730
+ "EmphasisMark",
2731
+ "Hardbreak",
2732
+ "HeaderMark",
2733
+ "HTMLTag",
2734
+ "LinkMark",
2735
+ "ListMark",
2736
+ "ProcessingInstruction",
2737
+ "QuoteMark",
2738
+ "StrikethroughMark",
2739
+ "SubscriptMark",
2740
+ "SuperscriptMark",
2741
+ "TaskMarker"
2742
+ ]);
2743
+ var Textblocks = {
2744
+ ATXHeading1: "heading1",
2745
+ ATXHeading2: "heading2",
2746
+ ATXHeading3: "heading3",
2747
+ ATXHeading4: "heading4",
2748
+ ATXHeading5: "heading5",
2749
+ ATXHeading6: "heading6",
2750
+ CodeBlock: "codeblock",
2751
+ FencedCode: "codeblock",
2752
+ Paragraph: "paragraph",
2753
+ SetextHeading1: "heading1",
2754
+ SetextHeading2: "heading2",
2755
+ TableCell: "tablecell",
2756
+ Task: "paragraph"
2757
+ };
2758
+ var getFormatting = (state) => {
2759
+ let blockType = null;
2760
+ const inline = [
2761
+ null,
2762
+ null,
2763
+ null,
2764
+ null
2765
+ ];
2766
+ let link2 = false;
2767
+ let blockQuote = null;
2768
+ let listStyle = null;
2769
+ const stack = [];
2770
+ let currentBlock = null;
2771
+ const advanceInline = (upto) => {
2772
+ if (!currentBlock) {
2773
+ return;
2774
+ }
2775
+ upto = Math.min(upto, currentBlock.end);
2776
+ if (upto <= currentBlock.pos) {
2777
+ return;
2778
+ }
2779
+ for (let i = 0; i < currentBlock.active.length; i++) {
2780
+ if (inline[i] === false) {
2781
+ continue;
2782
+ } else if (currentBlock.active[i]) {
2783
+ inline[i] = true;
2784
+ } else if (/\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {
2785
+ inline[i] = false;
2786
+ }
2787
+ }
2788
+ currentBlock.pos = upto;
2789
+ };
2790
+ const skipInline = (upto) => {
2791
+ if (currentBlock && upto > currentBlock.pos) {
2792
+ currentBlock.pos = Math.min(upto, currentBlock.end);
2793
+ }
2794
+ };
2795
+ const { selection } = state;
2796
+ for (const range of selection.ranges) {
2797
+ if (range.empty && inline.some((v) => v === null)) {
2798
+ const contextSize = Math.min(range.head, 6);
2799
+ const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);
2800
+ let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);
2801
+ for (let i = 0; i < contextSize; i++) {
2802
+ const ch = contextAfter[i];
2803
+ if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
2804
+ contextAfter = contextAfter.slice(0, i);
2805
+ break;
2806
+ }
2807
+ }
2808
+ for (let i = 0; i < inline.length; i++) {
2809
+ const mark2 = inlineMarkerText(i);
2810
+ const found = contextAfter.indexOf(mark2);
2811
+ if (found > -1) {
2812
+ contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
2813
+ if (inline[i] === null) {
2814
+ inline[i] = true;
2815
+ }
2816
+ }
2817
+ }
2818
+ }
2819
+ syntaxTree2(state).iterate({
2820
+ from: range.from,
2821
+ to: range.to,
2822
+ enter: (node) => {
2823
+ advanceInline(node.from);
2824
+ const { name } = node;
2825
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2826
+ stack.push(name);
2827
+ } else if (name === "Link") {
2828
+ link2 = true;
2829
+ } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
2830
+ if (name === "Task" && stack[stack.length - 1] === "BulletList") {
2831
+ stack[stack.length - 1] = "TaskList";
2832
+ }
2833
+ const blockCode = Textblocks[name];
2834
+ if (blockType === null) {
2835
+ blockType = blockCode;
2836
+ } else if (blockType !== blockCode) {
2837
+ blockType = false;
2838
+ }
2839
+ if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
2840
+ currentBlock = {
2841
+ pos: Math.max(range.from, node.from),
2842
+ end: Math.min(range.to, node.to),
2843
+ active: [
2844
+ false,
2845
+ false,
2846
+ false,
2847
+ false
2848
+ ]
2849
+ };
2850
+ }
2851
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
2852
+ const index = InlineMarker[name];
2853
+ if (range.empty && inline[index] === null) {
2854
+ inline[index] = true;
2855
+ }
2856
+ currentBlock.active[index] = true;
2857
+ } else if (IgnoreInline.has(name)) {
2858
+ skipInline(node.to);
2859
+ }
2860
+ },
2861
+ leave: (node) => {
2862
+ advanceInline(node.to);
2863
+ const { name } = node;
2864
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2865
+ stack.pop();
2866
+ } else if (Object.hasOwn(Textblocks, name)) {
2867
+ let hasList = false;
2868
+ let hasQuote = false;
2869
+ for (let i = stack.length - 1; i >= 0; i--) {
2870
+ if (stack[i] === "Blockquote") {
2871
+ hasQuote = true;
2872
+ } else if (!hasList) {
2873
+ hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
2874
+ }
2875
+ }
2876
+ if (blockQuote === null) {
2877
+ blockQuote = hasQuote;
2878
+ } else if (!hasQuote && blockQuote) {
2879
+ blockQuote = false;
2880
+ }
2881
+ if (listStyle === null) {
2882
+ listStyle = hasList;
2883
+ } else if (listStyle !== hasList) {
2884
+ listStyle = false;
2885
+ }
2886
+ currentBlock = null;
2887
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
2888
+ currentBlock.active[InlineMarker[name]] = false;
2889
+ }
2890
+ }
2891
+ });
2892
+ }
2893
+ const { from, to } = state.doc.lineAt(selection.main.anchor);
2894
+ const blankLine = from === to;
2895
+ return {
2896
+ blankLine,
2897
+ blockType: blockType || null,
2898
+ blockQuote: blockQuote ?? false,
2899
+ code: inline[3] ?? false,
2900
+ emphasis: inline[1] ?? false,
2901
+ strong: inline[0] ?? false,
2902
+ strikethrough: inline[2] ?? false,
2903
+ link: link2,
2904
+ listStyle: listStyle || null
2905
+ };
2906
+ };
2907
+ var useFormattingState = () => {
2908
+ const [state, setState] = useState(null);
2909
+ const observer = useMemo(() => EditorView9.updateListener.of((update2) => {
2910
+ if (update2.docChanged || update2.selectionSet) {
2911
+ const newState = getFormatting(update2.state);
2912
+ if (!state || !compareFormatting(state, newState)) {
2913
+ setState(newState);
2914
+ }
2915
+ }
2916
+ }), []);
2917
+ return [
2918
+ state,
2919
+ observer
2920
+ ];
2921
+ };
2922
+
2923
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
2924
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
2925
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
2926
+ import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
2927
+ var buildDecorations2 = (view) => {
2928
+ const builder = new RangeSetBuilder3();
2929
+ const { state } = view;
2930
+ const cursor = state.selection.main.head;
2931
+ for (const { from, to } of view.visibleRanges) {
2932
+ syntaxTree3(state).iterate({
2933
+ enter: (node) => {
2934
+ switch (node.name) {
2935
+ case "ATXHeading1":
2936
+ case "ATXHeading2":
2937
+ case "ATXHeading3":
2938
+ case "ATXHeading4":
2939
+ case "ATXHeading5":
2940
+ case "ATXHeading6": {
2941
+ const mark2 = node.node.getChild("HeaderMark");
2942
+ if (mark2) {
2943
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
2944
+ builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
2945
+ }
2946
+ }
2947
+ }
2948
+ }
2949
+ },
2950
+ from,
2951
+ to
2952
+ });
2953
+ }
2954
+ return builder.finish();
2955
+ };
2956
+ var heading2 = () => {
2957
+ return [
2958
+ ViewPlugin5.fromClass(class {
2959
+ constructor(view) {
2960
+ this.decorations = buildDecorations2(view);
2961
+ }
2962
+ update(update2) {
2963
+ this.decorations = buildDecorations2(update2.view);
2964
+ }
2965
+ }, {
2966
+ decorations: (value) => value.decorations
2967
+ })
2968
+ ];
2969
+ };
2970
+
2971
+ // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
2972
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2973
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2974
+ import { Decoration as Decoration6, EditorView as EditorView10, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
2975
+ var styles4 = EditorView10.baseTheme({
2976
+ "& .cm-hr": {
2977
+ // Note that block-level decorations should not have vertical margins,
2978
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2979
+ }
2980
+ });
2981
+ var HorizontalRuleWidget = class extends WidgetType3 {
2982
+ toDOM() {
2983
+ const el = document.createElement("div");
2984
+ el.className = "cm-hr";
2985
+ return el;
2986
+ }
2987
+ };
2988
+ var decoration = Decoration6.replace({
2989
+ widget: new HorizontalRuleWidget()
2990
+ });
2991
+ var buildDecorations3 = (view) => {
2992
+ const builder = new RangeSetBuilder4();
2993
+ const { state } = view;
2994
+ const cursor = state.selection.main.head;
2995
+ for (const { from, to } of view.visibleRanges) {
2996
+ syntaxTree4(state).iterate({
2997
+ enter: (node) => {
2998
+ if (node.name === "HorizontalRule") {
2999
+ if (cursor <= node.from || cursor >= node.to) {
3000
+ builder.add(node.from, node.to, decoration);
3001
+ }
3002
+ }
3003
+ },
3004
+ from,
3005
+ to
3006
+ });
3007
+ }
3008
+ return builder.finish();
3009
+ };
3010
+ var hr = () => {
3011
+ return [
3012
+ ViewPlugin6.fromClass(class {
3013
+ constructor(view) {
3014
+ this.decorations = buildDecorations3(view);
1838
3015
  }
1839
3016
  update(update2) {
1840
3017
  this.decorations = buildDecorations3(update2.view);
@@ -1847,13 +3024,13 @@ var hr = () => {
1847
3024
  };
1848
3025
 
1849
3026
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
1850
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3027
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1851
3028
  import { StateField as StateField3 } from "@codemirror/state";
1852
- import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
3029
+ import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType4 } from "@codemirror/view";
1853
3030
  var image = (options = {}) => {
1854
3031
  return StateField3.define({
1855
3032
  create: (state) => {
1856
- return Decoration6.set(buildDecorations4(0, state.doc.length, state));
3033
+ return Decoration7.set(buildDecorations4(0, state.doc.length, state));
1857
3034
  },
1858
3035
  update: (value, tr) => {
1859
3036
  if (!tr.docChanged && !tr.selection) {
@@ -1876,20 +3053,20 @@ var image = (options = {}) => {
1876
3053
  add: buildDecorations4(from, to, tr.state)
1877
3054
  });
1878
3055
  },
1879
- provide: (field) => EditorView10.decorations.from(field)
3056
+ provide: (field) => EditorView11.decorations.from(field)
1880
3057
  });
1881
3058
  };
1882
3059
  var buildDecorations4 = (from, to, state) => {
1883
3060
  const decorations = [];
1884
3061
  const cursor = state.selection.main.head;
1885
- syntaxTree4(state).iterate({
3062
+ syntaxTree5(state).iterate({
1886
3063
  enter: (node) => {
1887
3064
  if (node.name === "Image") {
1888
3065
  const urlNode = node.node.getChild("URL");
1889
3066
  if (urlNode) {
1890
3067
  const hide = state.readOnly || cursor < node.from || cursor > node.to;
1891
3068
  const url = state.sliceDoc(urlNode.from, urlNode.to);
1892
- decorations.push(Decoration6.replace({
3069
+ decorations.push(Decoration7.replace({
1893
3070
  block: true,
1894
3071
  widget: new ImageWidget(url)
1895
3072
  }).range(hide ? node.from : node.to, node.to));
@@ -1919,13 +3096,13 @@ var ImageWidget = class extends WidgetType4 {
1919
3096
  };
1920
3097
 
1921
3098
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
1922
- import { syntaxTree as syntaxTree5 } from "@codemirror/language";
1923
- import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
1924
- import { hoverTooltip as hoverTooltip2, Decoration as Decoration7, ViewPlugin as ViewPlugin6, WidgetType as WidgetType5 } from "@codemirror/view";
3099
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
3100
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
3101
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
1925
3102
  import { tooltipContent } from "@dxos/react-ui-theme";
1926
3103
  var link = (options = {}) => {
1927
3104
  const extensions = [
1928
- ViewPlugin6.fromClass(class {
3105
+ ViewPlugin7.fromClass(class {
1929
3106
  constructor(view) {
1930
3107
  this.decorations = buildDecorations5(view, options);
1931
3108
  }
@@ -1944,7 +3121,7 @@ var link = (options = {}) => {
1944
3121
  // https://codemirror.net/docs/ref/#view.hoverTooltip
1945
3122
  // https://github.com/codemirror/view/blob/main/src/tooltip.ts
1946
3123
  hoverTooltip2((view, pos, side) => {
1947
- const syntax = syntaxTree5(view.state).resolveInner(pos, side);
3124
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
1948
3125
  let link2 = null;
1949
3126
  for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
1950
3127
  link2 = node.name === "Link" ? node : null;
@@ -2021,27 +3198,27 @@ var LinkText = class extends WidgetType5 {
2021
3198
  }
2022
3199
  };
2023
3200
  var buildDecorations5 = (view, options) => {
2024
- const builder = new RangeSetBuilder4();
3201
+ const builder = new RangeSetBuilder5();
2025
3202
  const { state } = view;
2026
3203
  const cursor = state.selection.main.head;
2027
3204
  for (const { from, to } of view.visibleRanges) {
2028
- syntaxTree5(state).iterate({
3205
+ syntaxTree6(state).iterate({
2029
3206
  enter: (node) => {
2030
3207
  if (node.name === "Link") {
2031
- const marks2 = node.node.getChildren("LinkMark");
2032
- const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
3208
+ const marks = node.node.getChildren("LinkMark");
3209
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
2033
3210
  const urlNode = node.node.getChild("URL");
2034
3211
  const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
2035
3212
  if (!url) {
2036
3213
  return false;
2037
3214
  }
2038
3215
  if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
2039
- builder.add(node.from, node.to, Decoration7.replace({
3216
+ builder.add(node.from, node.to, Decoration8.replace({
2040
3217
  widget: new LinkText(text, options.link ? url : void 0)
2041
3218
  }));
2042
3219
  }
2043
3220
  if (options.onRender) {
2044
- builder.add(node.to, node.to, Decoration7.replace({
3221
+ builder.add(node.to, node.to, Decoration8.replace({
2045
3222
  widget: new LinkButton(url, options.onRender)
2046
3223
  }));
2047
3224
  }
@@ -2056,18 +3233,18 @@ var buildDecorations5 = (view, options) => {
2056
3233
  };
2057
3234
 
2058
3235
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
2059
- import { syntaxTree as syntaxTree6 } from "@codemirror/language";
2060
- import { RangeSetBuilder as RangeSetBuilder5, StateField as StateField4 } from "@codemirror/state";
2061
- import { Decoration as Decoration8, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
3236
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
3237
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
3238
+ import { Decoration as Decoration9, EditorView as EditorView12, WidgetType as WidgetType6 } from "@codemirror/view";
2062
3239
  var table = (options = {}) => {
2063
3240
  return StateField4.define({
2064
3241
  create: (state) => update(state, options),
2065
3242
  update: (_, tr) => update(tr.state, options),
2066
- provide: (field) => EditorView11.decorations.from(field)
3243
+ provide: (field) => EditorView12.decorations.from(field)
2067
3244
  });
2068
3245
  };
2069
3246
  var update = (state, options) => {
2070
- const builder = new RangeSetBuilder5();
3247
+ const builder = new RangeSetBuilder6();
2071
3248
  const cursor = state.selection.main.head;
2072
3249
  const tables = [];
2073
3250
  const getTable = () => tables[tables.length - 1];
@@ -2075,7 +3252,7 @@ var update = (state, options) => {
2075
3252
  const table2 = getTable();
2076
3253
  return table2.rows?.[table2.rows.length - 1];
2077
3254
  };
2078
- syntaxTree6(state).iterate({
3255
+ syntaxTree7(state).iterate({
2079
3256
  enter: (node) => {
2080
3257
  switch (node.name) {
2081
3258
  case "Table": {
@@ -2107,10 +3284,10 @@ var update = (state, options) => {
2107
3284
  });
2108
3285
  tables.forEach((table2) => {
2109
3286
  const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
2110
- hide && builder.add(table2.from, table2.to, Decoration8.replace({
3287
+ hide && builder.add(table2.from, table2.to, Decoration9.replace({
2111
3288
  widget: new TableWidget(table2)
2112
3289
  }));
2113
- builder.add(table2.from, table2.to, Decoration8.mark({
3290
+ builder.add(table2.from, table2.to, Decoration9.mark({
2114
3291
  class: "cm-table"
2115
3292
  }));
2116
3293
  });
@@ -2154,10 +3331,10 @@ var TableWidget = class extends WidgetType6 {
2154
3331
  };
2155
3332
 
2156
3333
  // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
2157
- import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2158
- import { RangeSetBuilder as RangeSetBuilder6 } from "@codemirror/state";
2159
- import { EditorView as EditorView12, Decoration as Decoration9, WidgetType as WidgetType7, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
2160
- var styles5 = EditorView12.baseTheme({
3334
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
3335
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
3336
+ import { EditorView as EditorView13, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
3337
+ var styles5 = EditorView13.baseTheme({
2161
3338
  "& .cm-task": {
2162
3339
  color: getToken("extend.colors.blue.500")
2163
3340
  },
@@ -2203,23 +3380,23 @@ var CheckboxWidget = class extends WidgetType7 {
2203
3380
  return false;
2204
3381
  }
2205
3382
  };
2206
- var checkedDecoration = Decoration9.replace({
3383
+ var checkedDecoration = Decoration10.replace({
2207
3384
  widget: new CheckboxWidget(true)
2208
3385
  });
2209
- var uncheckedDecoration = Decoration9.replace({
3386
+ var uncheckedDecoration = Decoration10.replace({
2210
3387
  widget: new CheckboxWidget(false)
2211
3388
  });
2212
3389
  var buildDecorations6 = (view) => {
2213
- const builder = new RangeSetBuilder6();
3390
+ const builder = new RangeSetBuilder7();
2214
3391
  const { state } = view;
2215
3392
  const cursor = state.selection.main.head;
2216
3393
  for (const { from, to } of view.visibleRanges) {
2217
- syntaxTree7(state).iterate({
3394
+ syntaxTree8(state).iterate({
2218
3395
  enter: (node) => {
2219
3396
  if (node.name === "TaskMarker") {
2220
3397
  if (cursor < node.from || cursor > node.to) {
2221
3398
  const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2222
- builder.add(node.from - 2, node.from - 1, Decoration9.mark({
3399
+ builder.add(node.from - 2, node.from - 1, Decoration10.mark({
2223
3400
  class: "cm-task"
2224
3401
  }));
2225
3402
  builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
@@ -2234,7 +3411,7 @@ var buildDecorations6 = (view) => {
2234
3411
  };
2235
3412
  var tasklist = (options = {}) => {
2236
3413
  return [
2237
- ViewPlugin7.fromClass(class {
3414
+ ViewPlugin8.fromClass(class {
2238
3415
  constructor(view) {
2239
3416
  this.decorations = buildDecorations6(view);
2240
3417
  }
@@ -2285,7 +3462,7 @@ var outliner = (options = {}) => {
2285
3462
  };
2286
3463
 
2287
3464
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
2288
- import { keymap as keymap5 } from "@codemirror/view";
3465
+ import { keymap as keymap6 } from "@codemirror/view";
2289
3466
  var defaultItems = [
2290
3467
  "hello world!",
2291
3468
  "this is a test.",
@@ -2295,7 +3472,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
2295
3472
  let t;
2296
3473
  let idx = 0;
2297
3474
  return [
2298
- keymap5.of([
3475
+ keymap6.of([
2299
3476
  {
2300
3477
  // Reset.
2301
3478
  key: "alt-meta-'",
@@ -2372,8 +3549,9 @@ var defaultTheme = {
2372
3549
  outline: "none"
2373
3550
  },
2374
3551
  ".cm-scroller": {
2375
- overflow: "visible",
2376
- fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
3552
+ // overflow: 'visible',
3553
+ fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
3554
+ lineHeight: 1.4
2377
3555
  },
2378
3556
  ".cm-content": {
2379
3557
  padding: 0,
@@ -2381,11 +3559,11 @@ var defaultTheme = {
2381
3559
  fontSize: "16px"
2382
3560
  },
2383
3561
  "&light .cm-content": {
2384
- color: get3(tokens, "extend.colors.neutral.900", "black"),
3562
+ color: get3(tokens, "extend.semanticColors.base.fg.light", "black"),
2385
3563
  caretColor: "black"
2386
3564
  },
2387
3565
  "&dark .cm-content": {
2388
- color: get3(tokens, "extend.colors.neutral.100", "white"),
3566
+ color: get3(tokens, "extend.semanticColors.base.fg.dark", "white"),
2389
3567
  caretColor: "white"
2390
3568
  },
2391
3569
  //
@@ -2397,8 +3575,11 @@ var defaultTheme = {
2397
3575
  "&dark .cm-cursor, &dark .cm-dropCursor": {
2398
3576
  borderLeft: "2px solid white"
2399
3577
  },
2400
- ".cm-placeholder": {
2401
- fontWeight: 100
3578
+ "&light .cm-placeholder": {
3579
+ color: get3(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
3580
+ },
3581
+ "&dark .cm-placeholder": {
3582
+ color: get3(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
2402
3583
  },
2403
3584
  //
2404
3585
  // line
@@ -2407,7 +3588,7 @@ var defaultTheme = {
2407
3588
  paddingInline: 0
2408
3589
  },
2409
3590
  ".cm-activeLine": {
2410
- background: "inherit"
3591
+ background: "transparent"
2411
3592
  },
2412
3593
  //
2413
3594
  // Selection
@@ -2578,23 +3759,28 @@ var codeTheme = {
2578
3759
  };
2579
3760
 
2580
3761
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
2581
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
3762
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
2582
3763
  var EditorModes = [
2583
3764
  "default",
2584
3765
  "vim"
2585
3766
  ];
2586
3767
  var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2587
- const tabsterDOMAttribute = useFocusableGroup({
2588
- tabBehavior: "limited"
2589
- });
2590
3768
  const { themeMode } = useThemeContext();
2591
3769
  const rootRef = useRef(null);
2592
- const [view, setView] = useState(null);
3770
+ const [view, setView] = useState2(null);
2593
3771
  useImperativeHandle(forwardedRef, () => view, [
2594
3772
  view
2595
3773
  ]);
2596
3774
  useEffect2(() => {
2597
- if (autoFocus) {
3775
+ if (autoFocus && !view?.hasFocus) {
3776
+ if (view?.state.selection.main?.from === 0) {
3777
+ const { to } = view.state.doc.lineAt(0);
3778
+ view?.dispatch({
3779
+ selection: {
3780
+ anchor: to
3781
+ }
3782
+ });
3783
+ }
2598
3784
  view?.focus();
2599
3785
  }
2600
3786
  }, [
@@ -2610,37 +3796,38 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2610
3796
  selection,
2611
3797
  extensions: [
2612
3798
  // TODO(burdon): Doesn't catch errors in keymap functions.
2613
- EditorView13.exceptionSink.of((err) => {
3799
+ EditorView14.exceptionSink.of((err) => {
2614
3800
  log2.catch(err, void 0, {
2615
- F: __dxlog_file5,
2616
- L: 123,
3801
+ F: __dxlog_file3,
3802
+ L: 134,
2617
3803
  S: void 0,
2618
3804
  C: (f, a) => f(...a)
2619
3805
  });
2620
3806
  }),
2621
3807
  // Theme.
2622
3808
  // TODO(burdon): Make configurable.
2623
- EditorView13.baseTheme(defaultTheme),
2624
- EditorView13.theme(theme ?? {}),
2625
- EditorView13.darkTheme.of(themeMode === "dark"),
2626
- EditorView13.editorAttributes.of({
3809
+ EditorView14.baseTheme(defaultTheme),
3810
+ EditorView14.theme(theme ?? {}),
3811
+ EditorView14.darkTheme.of(themeMode === "dark"),
3812
+ EditorView14.editorAttributes.of({
2627
3813
  class: slots.editor?.className ?? ""
2628
3814
  }),
2629
- EditorView13.contentAttributes.of({
3815
+ EditorView14.contentAttributes.of({
2630
3816
  class: slots.content?.className ?? ""
2631
3817
  }),
2632
3818
  // State.
3819
+ EditorView14.editable.of(!readonly),
2633
3820
  EditorState2.readOnly.of(!!readonly),
2634
3821
  // Storage and replication.
2635
3822
  // NOTE: This must come before user extensions.
2636
3823
  model.extension,
2637
- // TODO(burdon): Factor out (requires special handling for Escape/focus).
3824
+ // TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
2638
3825
  editorMode === "vim" && vim(),
2639
3826
  // Custom.
2640
3827
  ...extensions
2641
- ].filter(Boolean)
3828
+ ].filter(isNotFalsy3)
2642
3829
  });
2643
- const newView = new EditorView13({
3830
+ const newView = new EditorView14({
2644
3831
  state,
2645
3832
  parent: rootRef.current,
2646
3833
  scrollTo,
@@ -2675,7 +3862,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2675
3862
  }
2676
3863
  case "Escape": {
2677
3864
  if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
2678
- rootRef.current?.focus();
3865
+ view?.focus();
2679
3866
  }
2680
3867
  break;
2681
3868
  }
@@ -2688,9 +3875,9 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2688
3875
  key: model.id,
2689
3876
  role: "none",
2690
3877
  tabIndex: 0,
2691
- onKeyUp: handleKeyUp,
2692
3878
  ...slots.root,
2693
- ...editorMode !== "vim" && tabsterDOMAttribute,
3879
+ // {...(editorMode !== 'vim' && tabsterDOMAttribute)}
3880
+ onKeyUp: handleKeyUp,
2694
3881
  ref: rootRef
2695
3882
  });
2696
3883
  });
@@ -2701,7 +3888,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
2701
3888
  ref: forwardedRef,
2702
3889
  readonly,
2703
3890
  extensions: [
2704
- basicBundle({
3891
+ createBasicBundle({
2705
3892
  themeMode,
2706
3893
  placeholder: placeholder3,
2707
3894
  lineWrapping
@@ -2720,7 +3907,7 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2720
3907
  ref: forwardedRef,
2721
3908
  readonly,
2722
3909
  extensions: [
2723
- markdownBundle({
3910
+ createMarkdownExtensions({
2724
3911
  themeMode,
2725
3912
  placeholder: placeholder3
2726
3913
  }),
@@ -2733,11 +3920,10 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2733
3920
  });
2734
3921
  var defaultSlots = {
2735
3922
  root: {
2736
- // TODO(burdon): Add focusRing by default/as property?
2737
- className: mx3("flex flex-col grow overflow-y-auto", attentionSurface)
3923
+ className: mx3("grow", focusRing)
2738
3924
  },
2739
3925
  editor: {
2740
- className: "h-full p-2"
3926
+ className: "bs-full"
2741
3927
  }
2742
3928
  };
2743
3929
  var defaultTextSlots = {
@@ -2747,12 +3933,212 @@ var defaultMarkdownSlots = {
2747
3933
  ...defaultSlots
2748
3934
  };
2749
3935
 
3936
+ // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
3937
+ import { CaretRight, ChatText, Code, CodeBlock, Link, ListBullets, ListChecks, ListNumbers, Paragraph, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic } from "@phosphor-icons/react";
3938
+ import { createContext } from "@radix-ui/react-context";
3939
+ import React2 from "react";
3940
+ import { Button, DensityProvider, Select } from "@dxos/react-ui";
3941
+ import { getSize, mx as mx4 } from "@dxos/react-ui-theme";
3942
+ var HeadingIcons = {
3943
+ "0": Paragraph,
3944
+ "1": TextHOne,
3945
+ "2": TextHTwo,
3946
+ "3": TextHThree,
3947
+ "4": TextHFour,
3948
+ "5": TextHFive,
3949
+ "6": TextHSix
3950
+ };
3951
+ var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
3952
+ var ToolbarRoot = ({ children, onAction, state }) => {
3953
+ return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
3954
+ onAction,
3955
+ state
3956
+ }, /* @__PURE__ */ React2.createElement(DensityProvider, {
3957
+ density: "fine"
3958
+ }, /* @__PURE__ */ React2.createElement("div", {
3959
+ role: "toolbar",
3960
+ className: "flex w-full shrink-0 p-1 gap-4 items-center whitespace-nowrap overflow-hidden"
3961
+ }, children)));
3962
+ };
3963
+ var ToolbarButton = ({ Icon, onClick, title, getState, disable }) => {
3964
+ const { onAction, state } = useToolbarContext("ToolbarButton");
3965
+ const active = getState && state ? getState(state) : false;
3966
+ const disabled = disable && state ? disable(state) : false;
3967
+ const handleClick = (event) => {
3968
+ const action = onClick(state);
3969
+ if (action) {
3970
+ onAction?.(action);
3971
+ event.preventDefault();
3972
+ }
3973
+ };
3974
+ return /* @__PURE__ */ React2.createElement(Button, {
3975
+ variant: "ghost",
3976
+ classNames: mx4("p-2", active && "ring-[1px]"),
3977
+ onMouseDown: handleClick,
3978
+ title,
3979
+ disabled
3980
+ }, /* @__PURE__ */ React2.createElement(Icon, {
3981
+ className: getSize(5)
3982
+ }));
3983
+ };
3984
+ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
3985
+ className: "grow"
3986
+ });
3987
+ var MarkdownHeading = () => {
3988
+ const { onAction, state } = useToolbarContext("MarkdownFormatting");
3989
+ const blockType = state ? state.blockType : "paragraph";
3990
+ const header = blockType && /heading(\d)/.exec(blockType);
3991
+ const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
3992
+ const HeadingIcon = HeadingIcons[value ?? "0"];
3993
+ return /* @__PURE__ */ React2.createElement(Select.Root, {
3994
+ disabled: value === null,
3995
+ value: value ?? "0",
3996
+ onValueChange: (value2) => onAction?.({
3997
+ type: "heading",
3998
+ data: parseInt(value2)
3999
+ })
4000
+ }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, null, /* @__PURE__ */ React2.createElement(HeadingIcon, {
4001
+ className: getSize(5)
4002
+ })), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
4003
+ value: "0"
4004
+ }, "Paragraph"), [
4005
+ 1,
4006
+ 2,
4007
+ 3,
4008
+ 4,
4009
+ 5,
4010
+ 6
4011
+ ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
4012
+ key: level,
4013
+ value: String(level)
4014
+ }, "Heading ", level))))));
4015
+ };
4016
+ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
4017
+ role: "none"
4018
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4019
+ Icon: TextB,
4020
+ title: "String",
4021
+ disable: (s) => s.blockType === "codeblock",
4022
+ getState: (s) => s.strong,
4023
+ onClick: (s) => ({
4024
+ type: "strong",
4025
+ data: s ? !s.strong : null
4026
+ })
4027
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4028
+ Icon: TextItalic,
4029
+ title: "Emphasis",
4030
+ disable: (s) => s.blockType === "codeblock",
4031
+ getState: (s) => s.emphasis,
4032
+ onClick: (s) => ({
4033
+ type: "emphasis",
4034
+ data: s ? !s.emphasis : null
4035
+ })
4036
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4037
+ Icon: TextStrikethrough,
4038
+ title: "Strike-through",
4039
+ disable: (s) => s.blockType === "codeblock",
4040
+ getState: (s) => s.strikethrough,
4041
+ onClick: (s) => ({
4042
+ type: "strikethrough",
4043
+ data: s ? !s.strikethrough : null
4044
+ })
4045
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4046
+ Icon: Code,
4047
+ title: "Inline code",
4048
+ disable: (s) => s.blockType === "codeblock",
4049
+ getState: (s) => s.code,
4050
+ onClick: (s) => ({
4051
+ type: "code",
4052
+ data: s ? !s.code : null
4053
+ })
4054
+ }));
4055
+ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
4056
+ role: "none"
4057
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4058
+ Icon: ListBullets,
4059
+ title: "Bullet list",
4060
+ getState: (s) => s.listStyle === "bullet",
4061
+ onClick: (s) => ({
4062
+ type: "list-bullet",
4063
+ data: s ? s.listStyle !== "bullet" : null
4064
+ })
4065
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4066
+ Icon: ListNumbers,
4067
+ title: "Numbered list",
4068
+ getState: (s) => s.listStyle === "ordered",
4069
+ onClick: (s) => ({
4070
+ type: "list-ordered",
4071
+ data: s ? s.listStyle !== "ordered" : null
4072
+ })
4073
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4074
+ Icon: ListChecks,
4075
+ title: "Task list",
4076
+ getState: (s) => s.listStyle === "task",
4077
+ onClick: (s) => ({
4078
+ type: "list-tasks",
4079
+ data: s ? s.listStyle !== "task" : null
4080
+ })
4081
+ }));
4082
+ var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
4083
+ role: "none"
4084
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4085
+ Icon: CaretRight,
4086
+ title: "Block quote",
4087
+ getState: (s) => s.blockQuote,
4088
+ onClick: (s) => ({
4089
+ type: "blockquote",
4090
+ data: s ? !s.blockQuote : null
4091
+ })
4092
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4093
+ Icon: CodeBlock,
4094
+ title: "Code block",
4095
+ disable: (s) => !s.blankLine,
4096
+ getState: (s) => s.blockType === "codeblock",
4097
+ onClick: (s) => ({
4098
+ type: "codeblock",
4099
+ data: s ? s.blockType !== "codeblock" : null
4100
+ })
4101
+ }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
4102
+ Icon: Table2,
4103
+ title: "Table",
4104
+ disable: (s) => !s.blankLine,
4105
+ onClick: () => ({
4106
+ type: "table"
4107
+ })
4108
+ }));
4109
+ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
4110
+ role: "none"
4111
+ }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4112
+ Icon: Link,
4113
+ title: "Link",
4114
+ getState: (s) => s.link,
4115
+ onClick: (s) => ({
4116
+ type: "link",
4117
+ data: s ? !s.link : null
4118
+ })
4119
+ }));
4120
+ var MarkdownStandard = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(MarkdownHeading, null), /* @__PURE__ */ React2.createElement(MarkdownStyles, null), /* @__PURE__ */ React2.createElement(MarkdownLists, null), /* @__PURE__ */ React2.createElement(MarkdownBlocks, null), /* @__PURE__ */ React2.createElement(MarkdownLinks, null));
4121
+ var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
4122
+ Icon: ChatText,
4123
+ title: "Create comment",
4124
+ onClick: () => ({
4125
+ type: "comment"
4126
+ })
4127
+ });
4128
+ var Toolbar = {
4129
+ Root: ToolbarRoot,
4130
+ Button: ToolbarButton,
4131
+ Separator: ToolbarSeparator,
4132
+ Markdown: MarkdownStandard,
4133
+ Extended: MarkdownExtended
4134
+ };
4135
+
2750
4136
  // packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
2751
4137
  import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
2752
4138
  import { Context as Context2 } from "@dxos/context";
2753
- import { invariant as invariant4 } from "@dxos/invariant";
4139
+ import { invariant as invariant2 } from "@dxos/invariant";
2754
4140
  import { log as log3 } from "@dxos/log";
2755
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
4141
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
2756
4142
  var DEBOUNCE_INTERVAL = 100;
2757
4143
  var SpaceAwarenessProvider = class {
2758
4144
  constructor(params) {
@@ -2792,7 +4178,7 @@ var SpaceAwarenessProvider = class {
2792
4178
  log3.debug("failed to query awareness", {
2793
4179
  err
2794
4180
  }, {
2795
- F: __dxlog_file6,
4181
+ F: __dxlog_file4,
2796
4182
  L: 89,
2797
4183
  S: this,
2798
4184
  C: (f, a) => f(...a)
@@ -2808,8 +4194,8 @@ var SpaceAwarenessProvider = class {
2808
4194
  return Array.from(this._remoteStates.values());
2809
4195
  }
2810
4196
  update(position) {
2811
- invariant4(this._postTask, void 0, {
2812
- F: __dxlog_file6,
4197
+ invariant2(this._postTask, void 0, {
4198
+ F: __dxlog_file4,
2813
4199
  L: 104,
2814
4200
  S: this,
2815
4201
  A: [
@@ -2825,8 +4211,8 @@ var SpaceAwarenessProvider = class {
2825
4211
  this._postTask.schedule();
2826
4212
  }
2827
4213
  _handleQueryMessage() {
2828
- invariant4(this._postTask, void 0, {
2829
- F: __dxlog_file6,
4214
+ invariant2(this._postTask, void 0, {
4215
+ F: __dxlog_file4,
2830
4216
  L: 115,
2831
4217
  S: this,
2832
4218
  A: [
@@ -2837,8 +4223,8 @@ var SpaceAwarenessProvider = class {
2837
4223
  this._postTask.schedule();
2838
4224
  }
2839
4225
  _handlePostMessage(message) {
2840
- invariant4(message.kind === "post", void 0, {
2841
- F: __dxlog_file6,
4226
+ invariant2(message.kind === "post", void 0, {
4227
+ F: __dxlog_file4,
2842
4228
  L: 120,
2843
4229
  S: this,
2844
4230
  A: [
@@ -2858,10 +4244,58 @@ var modelState = StateField5.define({
2858
4244
  update: (model) => model
2859
4245
  });
2860
4246
 
2861
- // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
2862
- import { useState as useState2 } from "react";
2863
- var useTextEditor = () => {
2864
- const [view, setView] = useState2(null);
4247
+ // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4248
+ var useActionHandler = (view) => {
4249
+ return (action) => {
4250
+ if (!view) {
4251
+ return;
4252
+ }
4253
+ let inlineType, listType;
4254
+ switch (action.type) {
4255
+ case "heading":
4256
+ setHeading(parseInt(action.data))(view);
4257
+ break;
4258
+ case "strong":
4259
+ case "emphasis":
4260
+ case "strikethrough":
4261
+ case "code":
4262
+ inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4263
+ (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4264
+ break;
4265
+ case "list-ordered":
4266
+ case "list-bullet":
4267
+ case "list-tasks":
4268
+ listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4269
+ (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4270
+ break;
4271
+ case "blockquote":
4272
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4273
+ break;
4274
+ case "codeblock":
4275
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
4276
+ break;
4277
+ case "table":
4278
+ insertTable(view);
4279
+ break;
4280
+ case "link":
4281
+ (action.data === false ? removeLink : addLink)(view);
4282
+ break;
4283
+ case "comment":
4284
+ createComment(view);
4285
+ break;
4286
+ }
4287
+ setTimeout(() => {
4288
+ if (!view.hasFocus) {
4289
+ view.focus();
4290
+ }
4291
+ });
4292
+ };
4293
+ };
4294
+
4295
+ // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
4296
+ import { useState as useState3 } from "react";
4297
+ var useEditorView = () => {
4298
+ const [view, setView] = useState3(null);
2865
4299
  return [
2866
4300
  (ref) => {
2867
4301
  setView(ref);
@@ -2870,29 +4304,144 @@ var useTextEditor = () => {
2870
4304
  ];
2871
4305
  };
2872
4306
 
4307
+ // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4308
+ import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4309
+ import { EditorView as EditorView15 } from "@codemirror/view";
4310
+ import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
4311
+ import { log as log4 } from "@dxos/log";
4312
+ import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
4313
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4314
+ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
4315
+ const onUpdate = useRef2();
4316
+ const [view, setView] = useState4();
4317
+ const parentRef = useRef2(null);
4318
+ useEffect3(() => {
4319
+ if (parentRef.current) {
4320
+ const state = EditorState3.create({
4321
+ doc,
4322
+ selection,
4323
+ extensions: [
4324
+ // TODO(burdon): Doesn't catch errors in keymap functions.
4325
+ EditorView15.exceptionSink.of((err) => {
4326
+ log4.catch(err, void 0, {
4327
+ F: __dxlog_file5,
4328
+ L: 57,
4329
+ S: void 0,
4330
+ C: (f, a) => f(...a)
4331
+ });
4332
+ }),
4333
+ extensions,
4334
+ EditorView15.updateListener.of(() => {
4335
+ onUpdate.current?.();
4336
+ })
4337
+ ].filter(isNotFalsy4)
4338
+ });
4339
+ const view2 = new EditorView15({
4340
+ parent: parentRef.current,
4341
+ scrollTo,
4342
+ state,
4343
+ // NOTE: Uncomment to debug/monitor all transactions.
4344
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
4345
+ dispatchTransactions: (trs, view3) => {
4346
+ if (debug) {
4347
+ logChanges(trs);
4348
+ }
4349
+ view3.update(trs);
4350
+ }
4351
+ });
4352
+ setView(view2);
4353
+ }
4354
+ return () => {
4355
+ view?.destroy();
4356
+ };
4357
+ }, [
4358
+ parentRef
4359
+ ]);
4360
+ useEffect3(() => {
4361
+ if (view) {
4362
+ if (!selection && !view.state.selection.main.anchor) {
4363
+ selection = EditorSelection2.single(view.state.doc.line(1).to);
4364
+ }
4365
+ if (selection || scrollTo) {
4366
+ onUpdate.current = () => {
4367
+ onUpdate.current = void 0;
4368
+ view.dispatch({
4369
+ selection,
4370
+ effects: scrollTo && [
4371
+ scrollTo
4372
+ ],
4373
+ scrollIntoView: !scrollTo
4374
+ });
4375
+ };
4376
+ }
4377
+ if (autoFocus) {
4378
+ view.focus();
4379
+ }
4380
+ }
4381
+ }, [
4382
+ view,
4383
+ autoFocus,
4384
+ selection,
4385
+ scrollTo
4386
+ ]);
4387
+ return {
4388
+ parentRef,
4389
+ view
4390
+ };
4391
+ };
4392
+ var createDataExtensions = ({ readonly = false } = {}) => {
4393
+ return [
4394
+ //
4395
+ EditorState3.readOnly.of(readonly),
4396
+ EditorView15.editable.of(!readonly)
4397
+ ];
4398
+ };
4399
+ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4400
+ return [
4401
+ //
4402
+ EditorView15.baseTheme(defaultTheme),
4403
+ theme && EditorView15.theme(theme),
4404
+ EditorView15.darkTheme.of(themeMode === "dark"),
4405
+ EditorView15.editorAttributes.of({
4406
+ class: slots?.editor?.className ?? ""
4407
+ }),
4408
+ EditorView15.contentAttributes.of({
4409
+ class: slots?.content?.className ?? ""
4410
+ })
4411
+ ].filter(isNotFalsy4);
4412
+ };
4413
+
2873
4414
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
2874
4415
  import get4 from "lodash.get";
2875
- import { useEffect as useEffect3, useState as useState3 } from "react";
4416
+ import { useEffect as useEffect4, useState as useState5 } from "react";
2876
4417
  import { generateName } from "@dxos/display-name";
2877
4418
  import { getRawDoc } from "@dxos/echo-schema";
2878
- import { invariant as invariant5 } from "@dxos/invariant";
4419
+ import { invariant as invariant3 } from "@dxos/invariant";
2879
4420
  import { isAutomergeObject } from "@dxos/react-client/echo";
2880
- import { isNotNullOrUndefined } from "@dxos/util";
2881
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
4421
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
2882
4422
  var useTextModel = (props) => {
2883
4423
  const { identity, space, text } = props;
2884
- const [model, setModel] = useState3();
2885
- useEffect3(() => setModel(createModel(props)), [
4424
+ const [model, setModel] = useState5();
4425
+ useEffect4(() => setModel(createModel(props)), [
2886
4426
  identity,
2887
4427
  space,
2888
4428
  text
2889
4429
  ]);
2890
4430
  return model;
2891
4431
  };
4432
+ var useInMemoryTextModel = ({ id, defaultContent }) => {
4433
+ const [content, setContent] = useState5(defaultContent ?? "");
4434
+ return {
4435
+ id,
4436
+ content,
4437
+ setContent,
4438
+ text: () => content
4439
+ };
4440
+ };
2892
4441
  var createModel = ({ space, identity, text }) => {
2893
- invariant5(isAutomergeObject(text), void 0, {
2894
- F: __dxlog_file7,
2895
- L: 35,
4442
+ invariant3(isAutomergeObject(text), void 0, {
4443
+ F: __dxlog_file6,
4444
+ L: 50,
2896
4445
  S: void 0,
2897
4446
  A: [
2898
4447
  "isAutomergeObject(text)",
@@ -2903,7 +4452,7 @@ var createModel = ({ space, identity, text }) => {
2903
4452
  const doc = getRawDoc(obj, [
2904
4453
  obj.field
2905
4454
  ]);
2906
- const awarenessProvider = space && new SpaceAwarenessProvider({
4455
+ const awarenessProvider2 = space && new SpaceAwarenessProvider({
2907
4456
  space,
2908
4457
  channel: `automerge.awareness.${obj.id}`,
2909
4458
  info: {
@@ -2913,19 +4462,22 @@ var createModel = ({ space, identity, text }) => {
2913
4462
  },
2914
4463
  peerId: identity?.identityKey.toHex() ?? "Anonymous"
2915
4464
  });
4465
+ const extensions = [
4466
+ //
4467
+ modelState.init(() => model),
4468
+ automerge({
4469
+ handle: doc.handle,
4470
+ path: doc.path
4471
+ })
4472
+ ];
4473
+ if (awarenessProvider2) {
4474
+ extensions.push(awareness(awarenessProvider2));
4475
+ }
2916
4476
  const model = {
2917
4477
  id: obj.id,
2918
4478
  content: doc,
2919
4479
  text: () => get4(doc.handle.docSync(), doc.path),
2920
- extension: [
2921
- modelState.init(() => model),
2922
- automerge3({
2923
- handle: doc.handle,
2924
- path: doc.path
2925
- }),
2926
- awarenessProvider && AwarenessProvider.of(awarenessProvider),
2927
- awareness()
2928
- ].filter(isNotNullOrUndefined),
4480
+ extension: extensions,
2929
4481
  peer: identity ? {
2930
4482
  id: identity.identityKey.toHex(),
2931
4483
  name: identity.profile?.displayName
@@ -2934,40 +4486,57 @@ var createModel = ({ space, identity, text }) => {
2934
4486
  return model;
2935
4487
  };
2936
4488
  export {
2937
- AwarenessProvider,
2938
4489
  BaseTextEditor,
2939
4490
  Cursor,
2940
4491
  Doc,
2941
4492
  EditorModes,
4493
+ Inline,
4494
+ List,
2942
4495
  MarkdownEditor,
2943
4496
  RemoteSelectionsDecorator,
2944
4497
  SpaceAwarenessProvider,
2945
4498
  TextEditor,
2946
4499
  TextKind,
4500
+ Toolbar,
2947
4501
  YText,
2948
4502
  YXmlFragment,
4503
+ addBlockquote,
4504
+ addCodeblock,
4505
+ addLink,
4506
+ addList,
4507
+ addStyle,
2949
4508
  autocomplete,
2950
- automerge3 as automerge,
4509
+ automerge,
2951
4510
  awareness,
2952
- basicBundle,
4511
+ awarenessProvider,
2953
4512
  blast,
2954
4513
  callbackWrapper,
2955
4514
  code2 as code,
2956
4515
  codeTheme,
2957
4516
  comments,
4517
+ compareFormatting,
4518
+ createBasicBundle,
4519
+ createComment,
4520
+ createDataExtensions,
4521
+ createMarkdownExtensions,
4522
+ createThemeExtensions,
2958
4523
  defaultMarkdownSlots,
2959
4524
  defaultOptions,
2960
4525
  defaultSlots,
2961
4526
  defaultTextSlots,
2962
4527
  defaultTheme,
4528
+ focusComment,
4529
+ formatting,
4530
+ getFormatting,
2963
4531
  getToken,
2964
4532
  heading2 as heading,
2965
4533
  hr,
2966
4534
  image,
4535
+ insertTable,
4536
+ keymap7 as keymap,
2967
4537
  link,
2968
4538
  listener,
2969
4539
  logChanges,
2970
- markdownBundle,
2971
4540
  markdownHighlightStyle,
2972
4541
  markdownTags,
2973
4542
  markdownTagsExtensions,
@@ -2975,16 +4544,36 @@ export {
2975
4544
  mention,
2976
4545
  modelState,
2977
4546
  outliner,
4547
+ removeBlockquote,
4548
+ removeCodeblock,
4549
+ removeLink,
4550
+ removeList,
4551
+ removeStyle,
4552
+ setBlockquote,
2978
4553
  setComments,
2979
- setFocus,
4554
+ setHeading,
2980
4555
  setSelection,
4556
+ setStyle,
2981
4557
  table,
2982
4558
  tags2 as tags,
2983
4559
  tasklist,
2984
4560
  textTheme,
4561
+ toggleBlockquote,
4562
+ toggleCodeblock,
4563
+ toggleEmphasis,
4564
+ toggleInlineCode,
4565
+ toggleList,
4566
+ toggleStrikethrough,
4567
+ toggleStrong,
4568
+ toggleStyle,
2985
4569
  typewriter,
4570
+ useActionHandler,
2986
4571
  useComments,
4572
+ useEditorView,
4573
+ useFormattingState,
4574
+ useInMemoryTextModel,
2987
4575
  useTextEditor,
2988
- useTextModel
4576
+ useTextModel,
4577
+ useToolbarContext
2989
4578
  };
2990
4579
  //# sourceMappingURL=index.mjs.map