@dxos/react-ui-editor 0.4.8-next.fff1521 → 0.4.9-main.1057b49

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 (58) hide show
  1. package/dist/lib/browser/index.mjs +1687 -1694
  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/TextEditor.stories.d.ts +3 -0
  5. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/Toolbar/Toolbar.d.ts +1 -6
  7. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  8. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +3 -0
  9. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  10. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +3 -0
  11. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  12. package/dist/types/src/extensions/dnd.d.ts +7 -1
  13. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  14. package/dist/types/src/extensions/factories.d.ts +5 -1
  15. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  16. package/dist/types/src/extensions/index.d.ts +0 -1
  17. package/dist/types/src/extensions/index.d.ts.map +1 -1
  18. package/dist/types/src/extensions/markdown/action.d.ts +9 -0
  19. package/dist/types/src/extensions/markdown/action.d.ts.map +1 -0
  20. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  21. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  22. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  23. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  24. package/dist/types/src/extensions/modes.d.ts +1 -1
  25. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  26. package/dist/types/src/hooks/useActionHandler.d.ts +2 -2
  27. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  28. package/dist/types/src/hooks/useDocAccessor.d.ts +4 -2
  29. package/dist/types/src/hooks/useDocAccessor.d.ts.map +1 -1
  30. package/dist/types/src/hooks/useTextEditor.stories.d.ts +3 -0
  31. package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -1
  32. package/dist/types/src/index.d.ts +0 -1
  33. package/dist/types/src/index.d.ts.map +1 -1
  34. package/dist/types/src/themes/default.d.ts.map +1 -1
  35. package/dist/types/src/translations.d.ts +3 -0
  36. package/dist/types/src/translations.d.ts.map +1 -1
  37. package/package.json +24 -24
  38. package/src/components/TextEditor/TextEditor.stories.tsx +14 -2
  39. package/src/components/Toolbar/Toolbar.stories.tsx +0 -1
  40. package/src/components/Toolbar/Toolbar.tsx +46 -59
  41. package/src/extensions/automerge/automerge.ts +4 -4
  42. package/src/extensions/command/state.ts +2 -2
  43. package/src/extensions/dnd.ts +31 -9
  44. package/src/extensions/factories.ts +41 -15
  45. package/src/extensions/index.ts +0 -1
  46. package/src/extensions/markdown/action.ts +112 -0
  47. package/src/extensions/markdown/bundle.ts +8 -11
  48. package/src/extensions/markdown/formatting.ts +11 -11
  49. package/src/extensions/markdown/index.ts +1 -0
  50. package/src/extensions/modes.ts +8 -1
  51. package/src/hooks/useActionHandler.ts +4 -89
  52. package/src/hooks/useDocAccessor.ts +8 -2
  53. package/src/index.ts +0 -1
  54. package/src/themes/default.ts +24 -8
  55. package/src/translations.ts +3 -0
  56. package/dist/types/src/extensions/cursor-line-margin.d.ts +0 -7
  57. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +0 -1
  58. package/src/extensions/cursor-line-margin.ts +0 -94
@@ -17,24 +17,26 @@ var translations_default = [
17
17
  "comment label": "Create comment",
18
18
  "image label": "Insert image",
19
19
  "heading label": "Heading level",
20
- "table label": "Create table"
20
+ "table label": "Create table",
21
+ "heading level label_zero": "Paragraph",
22
+ "heading level label_one": "Heading level {{count}}",
23
+ "heading level label_other": "Heading level {{count}}"
21
24
  }
22
25
  }
23
26
  }
24
27
  ];
25
28
 
26
29
  // packages/ui/react-ui-editor/src/index.ts
27
- import { keymap as keymap10 } from "@codemirror/view";
30
+ import { keymap as keymap11 } from "@codemirror/view";
28
31
  import { tags as tags2 } from "@lezer/highlight";
29
32
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
30
- import { Doc, YText, YXmlFragment } from "@dxos/text-model";
31
33
 
32
34
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
33
35
  import { EditorState as EditorState2 } from "@codemirror/state";
34
- import { EditorView as EditorView17 } from "@codemirror/view";
36
+ import { EditorView as EditorView16 } from "@codemirror/view";
35
37
  import { useFocusableGroup } from "@fluentui/react-tabster";
36
- import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
37
- import { log as log9 } from "@dxos/log";
38
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2 } from "react";
39
+ import { log as log7 } from "@dxos/log";
38
40
  import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
39
41
 
40
42
  // packages/ui/react-ui-editor/src/extensions/annotations.ts
@@ -122,8 +124,8 @@ var annotations = (options = {}) => {
122
124
  annotationsState
123
125
  ], (state2) => {
124
126
  const annotations2 = state2.field(annotationsState);
125
- const decorations = annotations2.map((annotation2) => {
126
- const range = Cursor.getRangeFromCursor(state2, annotation2.cursor);
127
+ const decorations = annotations2.map((annotation) => {
128
+ const range = Cursor.getRangeFromCursor(state2, annotation.cursor);
127
129
  return range && annotationMark.range(range.from, range.to);
128
130
  }).filter(isNotFalsy);
129
131
  return Decoration.set(decorations);
@@ -475,13 +477,13 @@ var automerge = (accessor) => {
475
477
  ViewPlugin.fromClass(class {
476
478
  constructor(_view) {
477
479
  this._view = _view;
478
- accessor.handle.addListener("change", this._handleChange.bind(this));
480
+ this._handleChange = () => {
481
+ syncer.reconcile(this._view, false);
482
+ };
483
+ accessor.handle.addListener("change", this._handleChange);
479
484
  }
480
485
  destroy() {
481
- accessor.handle.removeListener("change", this._handleChange.bind(this));
482
- }
483
- _handleChange() {
484
- syncer.reconcile(this._view, false);
486
+ accessor.handle.removeListener("change", this._handleChange);
485
487
  }
486
488
  }),
487
489
  // Reconcile local updates.
@@ -1196,7 +1198,7 @@ var commandState = StateField3.define({
1196
1198
  }
1197
1199
  });
1198
1200
  }
1199
- setTimeout(() => view2.focus());
1201
+ requestAnimationFrame(() => view2.focus());
1200
1202
  });
1201
1203
  }
1202
1204
  };
@@ -1851,76 +1853,14 @@ var useComments = (view, id, comments2) => {
1851
1853
  ]);
1852
1854
  };
1853
1855
 
1854
- // packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts
1855
- import { Transaction } from "@codemirror/state";
1856
- import { EditorView as EditorView8 } from "@codemirror/view";
1857
- import { log as log5 } from "@dxos/log";
1858
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts";
1859
- var annotation = "cursorLineMargin";
1860
- var lineAtPos = (s, pos) => s.doc.lineAt(pos).number;
1861
- var cursorLineMargin = (options = {
1862
- lines: 3
1863
- }) => {
1864
- const { lines } = options;
1865
- if (!lines || lines <= 0) {
1866
- return [];
1867
- }
1868
- return EditorView8.updateListener.of(({ transactions, state: state2, selectionSet, startState, view }) => {
1869
- if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1870
- return;
1871
- }
1872
- const s = state2;
1873
- const { main } = s.selection;
1874
- const rect = view.dom.getBoundingClientRect();
1875
- const viewportTop = rect.top - view.documentTop;
1876
- const viewportBottom = rect.bottom - view.documentTop;
1877
- const mainLine = lineAtPos(s, main.head);
1878
- const _visTopLine = lineAtPos(s, view.lineBlockAtHeight(viewportTop).from);
1879
- const botLineBlk = view.lineBlockAtHeight(viewportBottom);
1880
- const visBotLineDoc = lineAtPos(s, Math.min(botLineBlk.to, s.doc.length));
1881
- const visBotLine = botLineBlk.bottom >= viewportBottom ? visBotLineDoc : visBotLineDoc + Math.floor((viewportBottom - botLineBlk.bottom) / view.defaultLineHeight);
1882
- const needsScrollTop = false;
1883
- const needsScrollBot = mainLine >= visBotLine - lines;
1884
- if (needsScrollTop && needsScrollBot) {
1885
- log5.warn("is cursorScrollMargin too large for the editor size?", void 0, {
1886
- F: __dxlog_file6,
1887
- L: 65,
1888
- S: void 0,
1889
- C: (f, a) => f(...a)
1890
- });
1891
- return;
1892
- }
1893
- if (!needsScrollTop && !needsScrollBot) {
1894
- return;
1895
- }
1896
- if (selectionSet) {
1897
- const { head } = startState.selection.main;
1898
- const oldMainLine = lineAtPos(startState, head);
1899
- if (needsScrollTop && oldMainLine < mainLine) {
1900
- return;
1901
- }
1902
- if (needsScrollBot && oldMainLine > mainLine) {
1903
- return;
1904
- }
1905
- }
1906
- view.dispatch({
1907
- annotations: Transaction.userEvent.of(annotation),
1908
- effects: EditorView8.scrollIntoView(s.selection.main.head, {
1909
- y: needsScrollTop ? "start" : "end",
1910
- yMargin: view.defaultLineHeight * lines
1911
- })
1912
- });
1913
- });
1914
- };
1915
-
1916
1856
  // packages/ui/react-ui-editor/src/extensions/doc.ts
1917
1857
  import { Facet as Facet5 } from "@codemirror/state";
1918
1858
  import { invariant as invariant3 } from "@dxos/invariant";
1919
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/doc.ts";
1859
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/doc.ts";
1920
1860
  var documentId2 = Facet5.define({
1921
1861
  combine: (providers) => {
1922
1862
  invariant3(providers.length <= 1, void 0, {
1923
- F: __dxlog_file7,
1863
+ F: __dxlog_file6,
1924
1864
  L: 11,
1925
1865
  S: void 0,
1926
1866
  A: [
@@ -1933,33 +1873,51 @@ var documentId2 = Facet5.define({
1933
1873
  });
1934
1874
 
1935
1875
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
1936
- import { EditorView as EditorView9 } from "@codemirror/view";
1937
- import { log as log6 } from "@dxos/log";
1938
- var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/dnd.ts";
1939
- var dnd = () => {
1940
- return EditorView9.domEventHandlers({
1941
- drop: (event, view) => {
1942
- log6.info("domEventHandlersDrop", {
1943
- event
1944
- }, {
1945
- F: __dxlog_file8,
1946
- L: 14,
1947
- S: void 0,
1948
- C: (f, a) => f(...a)
1949
- });
1950
- }
1951
- });
1876
+ import { dropCursor, EditorView as EditorView8 } from "@codemirror/view";
1877
+ var styles4 = EditorView8.baseTheme({
1878
+ ".cm-dropCursor": {
1879
+ borderLeft: `2px solid ${getToken("extend.colors.primary.500")}`,
1880
+ color: getToken("extend.colors.primary.500"),
1881
+ padding: "0 4px"
1882
+ },
1883
+ ".cm-dropCursor:after": {
1884
+ content: '"\u2190"'
1885
+ }
1886
+ });
1887
+ var dropFile = (options = {}) => {
1888
+ return [
1889
+ styles4,
1890
+ dropCursor(),
1891
+ EditorView8.domEventHandlers({
1892
+ drop: (event, view) => {
1893
+ event.preventDefault();
1894
+ const files = event.dataTransfer?.files;
1895
+ const pos = view.posAtCoords(event);
1896
+ if (files?.length && pos !== null) {
1897
+ view.dispatch({
1898
+ selection: {
1899
+ anchor: pos
1900
+ }
1901
+ });
1902
+ options.onDrop?.(view, {
1903
+ files
1904
+ });
1905
+ }
1906
+ }
1907
+ })
1908
+ ];
1952
1909
  };
1953
1910
 
1954
1911
  // packages/ui/react-ui-editor/src/extensions/factories.ts
1955
- import { closeBrackets } from "@codemirror/autocomplete";
1956
- import { history } from "@codemirror/commands";
1912
+ import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
1913
+ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1957
1914
  import { bracketMatching } from "@codemirror/language";
1915
+ import { searchKeymap } from "@codemirror/search";
1958
1916
  import { EditorState } from "@codemirror/state";
1959
- import { EditorView as EditorView10, crosshairCursor, drawSelection, dropCursor, highlightActiveLine, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
1917
+ import { EditorView as EditorView9, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap5, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
1960
1918
  import defaultsDeep2 from "lodash.defaultsdeep";
1961
1919
  import { generateName } from "@dxos/display-name";
1962
- import { log as log7 } from "@dxos/log";
1920
+ import { log as log5 } from "@dxos/log";
1963
1921
  import { hueTokens } from "@dxos/react-ui-theme";
1964
1922
  import { hexToHue, isNotFalsy as isNotFalsy2 } from "@dxos/util";
1965
1923
 
@@ -2164,50 +2122,75 @@ var defaultTheme = {
2164
2122
  * </div>
2165
2123
  * </div
2166
2124
  */
2167
- ".cm-panels": {
2168
- border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
2169
- },
2125
+ ".cm-panels": {},
2170
2126
  ".cm-panel": {
2171
- background: get3(tokens, "extend.colors.neutral.50"),
2172
2127
  fontFamily: get3(tokens, "fontFamily.body", []).join(",")
2173
2128
  },
2129
+ ".cm-panel input[type=checkbox]": {
2130
+ marginRight: "0.4rem !important"
2131
+ },
2132
+ "&light .cm-panel": {
2133
+ background: get3(tokens, "extend.colors.neutral.50")
2134
+ },
2135
+ "&dark .cm-panel": {
2136
+ background: get3(tokens, "extend.colors.neutral.850")
2137
+ },
2174
2138
  ".cm-button": {
2175
2139
  margin: "4px",
2176
2140
  fontFamily: get3(tokens, "fontFamily.body", []).join(","),
2177
- background: get3(tokens, "extend.colors.neutral.100"),
2178
2141
  backgroundImage: "none",
2179
2142
  border: "none",
2143
+ "&:active": {
2144
+ backgroundImage: "none"
2145
+ }
2146
+ },
2147
+ "&light .cm-button": {
2148
+ background: get3(tokens, "extend.colors.neutral.100"),
2180
2149
  "&:hover": {
2181
2150
  background: get3(tokens, "extend.colors.neutral.200")
2182
2151
  },
2183
2152
  "&:active": {
2184
- background: get3(tokens, "extend.colors.neutral.300"),
2185
- backgroundImage: "none"
2153
+ background: get3(tokens, "extend.colors.neutral.300")
2186
2154
  }
2187
2155
  },
2188
- ".cm-panel input[type=checkbox]": {
2189
- marginRight: "0.4rem !important"
2156
+ "&dark .cm-button": {
2157
+ background: get3(tokens, "extend.colors.neutral.800"),
2158
+ "&:hover": {
2159
+ background: get3(tokens, "extend.colors.neutral.700")
2160
+ },
2161
+ "&:active": {
2162
+ background: get3(tokens, "extend.colors.neutral.600")
2163
+ }
2190
2164
  }
2191
2165
  };
2192
2166
 
2193
2167
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2194
- var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
2195
- var defaults = {
2168
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
2169
+ var defaultBasicOptions = {
2170
+ allowMultipleSelections: true,
2196
2171
  bracketMatching: true,
2197
2172
  closeBrackets: true,
2198
2173
  drawSelection: true,
2199
2174
  editable: true,
2200
2175
  history: true,
2201
- lineWrapping: true
2176
+ keymap: "standard",
2177
+ lineWrapping: true,
2178
+ search: true
2179
+ };
2180
+ var keymaps = {
2181
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
2182
+ standard: standardKeymap,
2183
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
2184
+ default: defaultKeymap
2202
2185
  };
2203
2186
  var createBasicExtensions = (_props) => {
2204
- const props = defaultsDeep2({}, _props, defaults);
2187
+ const props = defaultsDeep2({}, _props, defaultBasicOptions);
2205
2188
  return [
2206
- // TODO(burdon): Doesn't catch errors in keymap functions.
2207
- EditorView10.exceptionSink.of((err) => {
2208
- log7.catch(err, void 0, {
2209
- F: __dxlog_file9,
2210
- L: 78,
2189
+ // NOTE: Doesn't catch errors in keymap functions.
2190
+ EditorView9.exceptionSink.of((err) => {
2191
+ log5.catch(err, void 0, {
2192
+ F: __dxlog_file7,
2193
+ L: 91,
2211
2194
  S: void 0,
2212
2195
  C: (f, a) => f(...a)
2213
2196
  });
@@ -2215,37 +2198,50 @@ var createBasicExtensions = (_props) => {
2215
2198
  props.allowMultipleSelections && EditorState.allowMultipleSelections.of(true),
2216
2199
  props.bracketMatching && bracketMatching(),
2217
2200
  props.closeBrackets && closeBrackets(),
2218
- props.crosshairCursor && crosshairCursor(),
2219
- props.dropCursor && dropCursor(),
2201
+ props.dropCursor && dropCursor2(),
2220
2202
  props.drawSelection && drawSelection(),
2221
2203
  props.highlightActiveLine && highlightActiveLine(),
2222
2204
  props.history && history(),
2223
2205
  props.lineNumbers && lineNumbers(),
2224
- props.lineWrapping && EditorView10.lineWrapping,
2206
+ props.lineWrapping && EditorView9.lineWrapping,
2225
2207
  props.placeholder && placeholder(props.placeholder),
2226
2208
  props.readonly && [
2227
2209
  EditorState.readOnly.of(true),
2228
- EditorView10.editable.of(false)
2210
+ EditorView9.editable.of(false)
2229
2211
  ],
2230
2212
  props.scrollPastEnd && scrollPastEnd(),
2231
- props.tabSize && EditorState.tabSize.of(props.tabSize)
2213
+ props.tabSize && EditorState.tabSize.of(props.tabSize),
2214
+ // https://codemirror.net/docs/ref/#view.KeyBinding
2215
+ keymap5.of([
2216
+ ...(props.keymap && keymaps[props.keymap]) ?? [],
2217
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
2218
+ ...props.indentWithTab ? [
2219
+ indentWithTab
2220
+ ] : [],
2221
+ // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2222
+ ...props.closeBrackets ? closeBracketsKeymap : [],
2223
+ // https://codemirror.net/docs/ref/#commands.historyKeymap
2224
+ ...props.history ? historyKeymap : [],
2225
+ // https://codemirror.net/docs/ref/#search.searchKeymap
2226
+ ...props.search ? searchKeymap : []
2227
+ ].filter(isNotFalsy2))
2232
2228
  ].filter(isNotFalsy2);
2233
2229
  };
2234
- var defaultSlots = {
2230
+ var defaultThemeSlots = {
2235
2231
  editor: {
2236
2232
  className: "w-full bs-full"
2237
2233
  }
2238
2234
  };
2239
2235
  var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2240
- const slots = defaultsDeep2({}, _slots, defaultSlots);
2236
+ const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2241
2237
  return [
2242
- EditorView10.baseTheme(defaultTheme),
2243
- EditorView10.darkTheme.of(themeMode === "dark"),
2244
- theme && EditorView10.theme(theme),
2245
- slots.editor?.className && EditorView10.editorAttributes.of({
2238
+ EditorView9.baseTheme(defaultTheme),
2239
+ EditorView9.darkTheme.of(themeMode === "dark"),
2240
+ theme && EditorView9.theme(theme),
2241
+ slots.editor?.className && EditorView9.editorAttributes.of({
2246
2242
  class: slots.editor.className
2247
2243
  }),
2248
- slots.content?.className && EditorView10.contentAttributes.of({
2244
+ slots.content?.className && EditorView9.contentAttributes.of({
2249
2245
  class: slots.content.className
2250
2246
  })
2251
2247
  ].filter(isNotFalsy2);
@@ -2273,1618 +2269,1667 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2273
2269
  };
2274
2270
 
2275
2271
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2276
- import { EditorView as EditorView11 } from "@codemirror/view";
2272
+ import { EditorView as EditorView10 } from "@codemirror/view";
2277
2273
  var listener = ({ onFocus, onChange }) => {
2278
2274
  const extensions = [];
2279
- onFocus && extensions.push(EditorView11.focusChangeEffect.of((_, focusing) => {
2275
+ onFocus && extensions.push(EditorView10.focusChangeEffect.of((_, focusing) => {
2280
2276
  onFocus(focusing);
2281
2277
  return null;
2282
2278
  }));
2283
- onChange && extensions.push(EditorView11.updateListener.of((update2) => {
2279
+ onChange && extensions.push(EditorView10.updateListener.of((update2) => {
2284
2280
  onChange(update2.state.doc.toString());
2285
2281
  }));
2286
2282
  return extensions;
2287
2283
  };
2288
2284
 
2289
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2290
- import { closeBracketsKeymap } from "@codemirror/autocomplete";
2291
- import { historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
2292
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
2293
- import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
2294
- import { languages } from "@codemirror/language-data";
2295
- import { searchKeymap } from "@codemirror/search";
2296
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2297
- import { keymap as keymap5 } from "@codemirror/view";
2298
-
2299
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
2300
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
2301
- import { HighlightStyle } from "@codemirror/language";
2302
- import { tags, styleTags, Tag } from "@lezer/highlight";
2303
- import { Table } from "@lezer/markdown";
2304
- var markdownTags = {
2305
- Blockquote: Tag.define(),
2306
- CodeMark: Tag.define(),
2307
- CodeText: Tag.define(),
2308
- EmphasisMark: Tag.define(),
2309
- HeaderMark: Tag.define(),
2310
- InlineCode: Tag.define(),
2311
- LinkLabel: Tag.define(),
2312
- LinkReference: Tag.define(),
2313
- ListMark: Tag.define(),
2314
- QuoteMark: Tag.define(),
2315
- URL: Tag.define(),
2316
- // Custom.
2317
- TableCell: Tag.define()
2318
- };
2319
- Table.defineNodes?.forEach((node) => {
2320
- switch (node?.name) {
2321
- case "TableCell": {
2322
- node.style = markdownTags.TableCell;
2323
- break;
2285
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2286
+ import { snippet } from "@codemirror/autocomplete";
2287
+ import { syntaxTree } from "@codemirror/language";
2288
+ import { EditorSelection } from "@codemirror/state";
2289
+ import { EditorView as EditorView11, keymap as keymap6 } from "@codemirror/view";
2290
+ import { useMemo, useState } from "react";
2291
+ var formattingEquals = (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;
2292
+ var Inline;
2293
+ (function(Inline2) {
2294
+ Inline2[Inline2["Strong"] = 0] = "Strong";
2295
+ Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2296
+ Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2297
+ Inline2[Inline2["Code"] = 3] = "Code";
2298
+ })(Inline || (Inline = {}));
2299
+ var List;
2300
+ (function(List2) {
2301
+ List2[List2["Ordered"] = 0] = "Ordered";
2302
+ List2[List2["Bullet"] = 1] = "Bullet";
2303
+ List2[List2["Task"] = 2] = "Task";
2304
+ })(List || (List = {}));
2305
+ var setHeading = (level) => ({ state: state2, dispatch }) => {
2306
+ const { selection: { ranges }, doc } = state2;
2307
+ const changes = [];
2308
+ let prevBlock = -1;
2309
+ for (const range of ranges) {
2310
+ let sawBlock = false;
2311
+ syntaxTree(state2).iterate({
2312
+ from: range.from,
2313
+ to: range.to,
2314
+ enter: (node) => {
2315
+ if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
2316
+ return;
2317
+ }
2318
+ sawBlock = true;
2319
+ prevBlock = node.from;
2320
+ const blockType = Textblocks[node.name];
2321
+ const isHeading = /heading(\d)/.exec(blockType);
2322
+ const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
2323
+ if (curLevel < 0 || curLevel === level) {
2324
+ return;
2325
+ }
2326
+ if (curLevel === 0) {
2327
+ changes.push({
2328
+ from: node.from,
2329
+ insert: "#".repeat(level) + " "
2330
+ });
2331
+ } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
2332
+ const nextLine = doc.lineAt(node.to);
2333
+ if (level) {
2334
+ changes.push({
2335
+ from: node.from,
2336
+ insert: "#".repeat(level) + " "
2337
+ });
2338
+ }
2339
+ changes.push({
2340
+ from: nextLine.from - 1,
2341
+ to: nextLine.to
2342
+ });
2343
+ } else {
2344
+ if (level === 0) {
2345
+ changes.push({
2346
+ from: node.from,
2347
+ to: Math.min(node.to, node.from + curLevel + 1)
2348
+ });
2349
+ } else if (level < curLevel) {
2350
+ changes.push({
2351
+ from: node.from,
2352
+ to: node.from + (curLevel - level)
2353
+ });
2354
+ } else {
2355
+ changes.push({
2356
+ from: node.from,
2357
+ insert: "#".repeat(level - curLevel)
2358
+ });
2359
+ }
2360
+ }
2361
+ }
2362
+ });
2363
+ let line;
2364
+ if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state2.doc.lineAt(range.from)).text)) {
2365
+ changes.push({
2366
+ from: line.from,
2367
+ to: line.to,
2368
+ insert: "#".repeat(level) + " "
2369
+ });
2324
2370
  }
2325
2371
  }
2326
- });
2327
- var markdownTagsExtensions = [
2328
- Table,
2329
- {
2330
- props: [
2331
- styleTags(markdownTags)
2332
- ]
2372
+ if (!changes.length) {
2373
+ return false;
2333
2374
  }
2334
- ];
2335
- var markdownHighlightStyle = (readonly) => {
2336
- return HighlightStyle.define([
2337
- {
2338
- tag: [
2339
- tags.keyword,
2340
- tags.name,
2341
- tags.deleted,
2342
- tags.character,
2343
- tags.propertyName,
2344
- tags.macroName,
2345
- tags.color,
2346
- tags.constant(tags.name),
2347
- tags.standard(tags.name),
2348
- tags.definition(tags.name),
2349
- tags.separator,
2350
- tags.typeName,
2351
- tags.className,
2352
- tags.number,
2353
- tags.changed,
2354
- tags.annotation,
2355
- tags.modifier,
2356
- tags.self,
2357
- tags.namespace,
2358
- tags.operator,
2359
- tags.operatorKeyword,
2360
- tags.escape,
2361
- tags.regexp,
2362
- tags.special(tags.string),
2363
- tags.meta,
2364
- tags.comment,
2365
- tags.atom,
2366
- tags.bool,
2367
- tags.special(tags.variableName),
2368
- tags.processingInstruction,
2369
- tags.string,
2370
- tags.inserted,
2371
- tags.invalid
2372
- ],
2373
- color: "inherit !important"
2374
- },
2375
- // Markdown marks.
2376
- {
2377
- tag: [
2378
- tags.meta,
2379
- tags.processingInstruction,
2380
- markdownTags.LinkLabel,
2381
- markdownTags.LinkReference,
2382
- markdownTags.ListMark
2383
- ],
2384
- class: mark
2385
- },
2386
- // Markdown marks.
2387
- {
2388
- tag: [
2389
- markdownTags.CodeMark,
2390
- markdownTags.HeaderMark,
2391
- markdownTags.QuoteMark,
2392
- markdownTags.EmphasisMark
2393
- ],
2394
- class: mark
2395
- },
2396
- // E.g., code block language (after ```).
2397
- {
2398
- tag: [
2399
- tags.function(tags.variableName),
2400
- tags.labelName
2401
- ],
2402
- class: codeMark
2403
- },
2404
- {
2405
- tag: [
2406
- tags.monospace
2407
- ],
2408
- class: "font-mono"
2409
- },
2410
- // Headings.
2411
- {
2412
- tag: tags.heading1,
2413
- class: heading(1)
2414
- },
2415
- {
2416
- tag: tags.heading2,
2417
- class: heading(2)
2418
- },
2419
- {
2420
- tag: tags.heading3,
2421
- class: heading(3)
2422
- },
2423
- {
2424
- tag: tags.heading4,
2425
- class: heading(4)
2426
- },
2427
- {
2428
- tag: tags.heading5,
2429
- class: heading(5)
2430
- },
2431
- {
2432
- tag: tags.heading6,
2433
- class: heading(6)
2434
- },
2435
- // Emphasis.
2436
- {
2437
- tag: tags.emphasis,
2438
- class: italic
2439
- },
2440
- {
2441
- tag: tags.strong,
2442
- class: bold
2443
- },
2444
- {
2445
- tag: tags.strikethrough,
2446
- class: strikethrough
2447
- },
2448
- // Naked URLs.
2449
- {
2450
- tag: [
2451
- markdownTags.URL
2452
- ],
2453
- class: inlineUrl
2454
- },
2455
- // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
2456
- // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
2457
- // when a language is specified. In this case, the syntax highlighting extensions will colorize
2458
- // the code, but all other CSS properties will be inherited.
2459
- // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
2460
- {
2461
- tag: [
2462
- markdownTags.CodeText,
2463
- markdownTags.InlineCode
2464
- ],
2465
- class: code
2466
- },
2467
- {
2468
- tag: [
2469
- markdownTags.QuoteMark
2470
- ],
2471
- class: blockquote
2472
- },
2473
- {
2474
- tag: [
2475
- markdownTags.TableCell
2476
- ],
2477
- class: "font-mono"
2375
+ const changeSet = state2.changes(changes);
2376
+ dispatch(state2.update({
2377
+ changes: changeSet,
2378
+ selection: state2.selection.map(changeSet, 1),
2379
+ userEvent: "format.setHeading",
2380
+ scrollIntoView: true
2381
+ }));
2382
+ return true;
2383
+ };
2384
+ var setStyle = (type, enable) => ({ state: state2, dispatch }) => {
2385
+ const marker = inlineMarkerText(type);
2386
+ const changes = state2.changeByRange((range) => {
2387
+ if (!enable && range.empty) {
2388
+ const after = state2.doc.sliceString(range.head, range.head + 6);
2389
+ const found = after.indexOf(marker);
2390
+ if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2391
+ const before = state2.doc.sliceString(range.head - 6, range.head);
2392
+ if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2393
+ ...before.slice(before.length - found)
2394
+ ].reverse().join("") === after.slice(0, found)) {
2395
+ return {
2396
+ changes: [
2397
+ {
2398
+ from: range.head - marker.length - found,
2399
+ to: range.head - found
2400
+ },
2401
+ {
2402
+ from: range.head + found,
2403
+ to: range.head + found + marker.length
2404
+ }
2405
+ ],
2406
+ range: EditorSelection.cursor(range.from - marker.length)
2407
+ };
2408
+ }
2409
+ }
2478
2410
  }
2479
- ], {
2480
- scope: markdownLanguage2,
2481
- all: {
2482
- fontFamily: getToken("fontFamily.body", []).join(",")
2411
+ const changes2 = [];
2412
+ const changesAtEnd = [];
2413
+ let blockStart = -1;
2414
+ let blockEnd = -1;
2415
+ let startCovered = false;
2416
+ let endCovered = false;
2417
+ let { from, to } = range;
2418
+ syntaxTree(state2).iterate({
2419
+ from,
2420
+ to,
2421
+ enter: (node) => {
2422
+ const { name } = node;
2423
+ if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2424
+ blockStart = blockContentStart(node);
2425
+ blockEnd = blockContentEnd(node, state2.doc);
2426
+ startCovered = endCovered = false;
2427
+ } else if (name === "Link" || name === "Image" && enable) {
2428
+ if (from < node.from && to > node.from && to <= node.to) {
2429
+ to = node.to;
2430
+ } else if (to > node.to && from >= node.from && from < node.to) {
2431
+ from = node.from;
2432
+ }
2433
+ } else if (IgnoreInline.has(name) && enable) {
2434
+ if (node.from < from && node.to > from) {
2435
+ if (to === from) {
2436
+ to = node.to;
2437
+ }
2438
+ from = node.to;
2439
+ }
2440
+ if (node.from < to && node.to > to) {
2441
+ to = node.from;
2442
+ }
2443
+ } else if (Object.hasOwn(InlineMarker, name)) {
2444
+ const markType = InlineMarker[name];
2445
+ const size = inlineMarkerText(markType).length;
2446
+ const openEnd = node.from + size;
2447
+ const closeStart = node.to - size;
2448
+ if (markType === type) {
2449
+ if (openEnd <= from && closeStart >= from) {
2450
+ startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2451
+ from: node.from,
2452
+ to: openEnd
2453
+ } : true;
2454
+ }
2455
+ if (openEnd <= to && closeStart >= to) {
2456
+ endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2457
+ from: closeStart,
2458
+ to: node.to
2459
+ } : true;
2460
+ }
2461
+ }
2462
+ if (markType === type || type === 3 && enable) {
2463
+ if (node.from >= from && openEnd <= to) {
2464
+ changes2.push({
2465
+ from: node.from,
2466
+ to: openEnd
2467
+ });
2468
+ if (markType !== type && closeStart >= to) {
2469
+ changesAtEnd.push({
2470
+ from: skipSpaces(Math.min(to, blockEnd), state2.doc, 1, blockEnd),
2471
+ insert: inlineMarkerText(markType)
2472
+ });
2473
+ }
2474
+ }
2475
+ if (closeStart >= from && node.to <= to) {
2476
+ changes2.push({
2477
+ from: closeStart,
2478
+ to: node.to
2479
+ });
2480
+ if (markType !== type && openEnd <= from) {
2481
+ changes2.push({
2482
+ from: skipSpaces(Math.max(from, blockStart), state2.doc, -1, blockStart),
2483
+ insert: inlineMarkerText(markType)
2484
+ });
2485
+ }
2486
+ }
2487
+ }
2488
+ }
2489
+ },
2490
+ leave: (node) => {
2491
+ if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
2492
+ const rangeStart = Math.max(from, blockStart);
2493
+ const rangeEnd = Math.min(to, blockEnd);
2494
+ if (enable) {
2495
+ if (!startCovered) {
2496
+ changes2.push({
2497
+ from: rangeStart,
2498
+ insert: marker
2499
+ });
2500
+ }
2501
+ if (!endCovered) {
2502
+ changes2.push({
2503
+ from: rangeEnd,
2504
+ insert: marker
2505
+ });
2506
+ }
2507
+ } else {
2508
+ if (typeof startCovered === "object") {
2509
+ changes2.push(startCovered);
2510
+ } else if (startCovered) {
2511
+ changes2.push({
2512
+ from: skipSpaces(rangeStart, state2.doc, -1, blockStart),
2513
+ insert: marker
2514
+ });
2515
+ }
2516
+ if (typeof endCovered === "object") {
2517
+ changes2.push(endCovered);
2518
+ } else if (endCovered) {
2519
+ changes2.push({
2520
+ from: skipSpaces(rangeEnd, state2.doc, 1, blockEnd),
2521
+ insert: marker
2522
+ });
2523
+ }
2524
+ }
2525
+ }
2526
+ }
2527
+ });
2528
+ if (blockStart < 0 && range.empty && enable && !/\S/.test(state2.doc.lineAt(range.from).text)) {
2529
+ return {
2530
+ changes: {
2531
+ from: range.head,
2532
+ insert: marker + marker
2533
+ },
2534
+ range: EditorSelection.cursor(range.head + marker.length)
2535
+ };
2483
2536
  }
2537
+ const changeSet = state2.changes(changes2.concat(changesAtEnd));
2538
+ return {
2539
+ changes: changeSet,
2540
+ range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
2541
+ };
2484
2542
  });
2543
+ dispatch(state2.update(changes, {
2544
+ userEvent: enable ? "format.style.add" : "format.style.remove",
2545
+ scrollIntoView: true
2546
+ }));
2547
+ return true;
2485
2548
  };
2486
-
2487
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2488
- var createMarkdownExtensions = ({ themeMode } = {}) => {
2489
- return [
2490
- // Main extension.
2491
- // https://github.com/codemirror/lang-markdown
2492
- // https://codemirror.net/5/mode/markdown/index.html (demo).
2493
- markdown({
2494
- // GRM by default (vs strict CommonMark):
2495
- // Table, TaskList, Strikethrough, and Autolink.
2496
- // NOTE: This extends the parser; it doesn't affect rendering.
2497
- // https://github.github.com/gfm
2498
- // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
2499
- base: markdownLanguage3,
2500
- // Languages for syntax highlighting fenced code blocks.
2501
- codeLanguages: languages,
2502
- // Parser extensions.
2503
- extensions: [
2504
- // GFM provided by default.
2505
- markdownTagsExtensions
2506
- ]
2507
- }),
2508
- // https://github.com/codemirror/theme-one-dark
2509
- themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
2510
- // Custom styles.
2511
- syntaxHighlighting(markdownHighlightStyle()),
2512
- keymap5.of([
2513
- // https://codemirror.net/docs/ref/#commands.indentWithTab
2514
- indentWithTab,
2515
- // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2516
- ...closeBracketsKeymap,
2517
- // https://codemirror.net/docs/ref/#commands.historyKeymap
2518
- ...historyKeymap,
2519
- // https://codemirror.net/docs/ref/#search.searchKeymap
2520
- ...searchKeymap,
2521
- // https://codemirror.net/docs/ref/#commands.standardKeymap
2522
- ...standardKeymap
2523
- ])
2524
- ];
2549
+ var addStyle = (style) => setStyle(style, true);
2550
+ var removeStyle = (style) => setStyle(style, false);
2551
+ var toggleStyle = (style) => (arg) => {
2552
+ const form = getFormatting(arg.state);
2553
+ return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
2525
2554
  };
2526
-
2527
- // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
2528
- import { syntaxTree } from "@codemirror/language";
2529
- import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
2530
- import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2531
- import { mx as mx2 } from "@dxos/react-ui-theme";
2532
- var HorizontalRuleWidget = class extends WidgetType3 {
2533
- toDOM() {
2534
- const el = document.createElement("span");
2535
- el.className = "cm-hr";
2536
- return el;
2555
+ var toggleStrong = toggleStyle(0);
2556
+ var toggleEmphasis = toggleStyle(1);
2557
+ var toggleStrikethrough = toggleStyle(2);
2558
+ var toggleInlineCode = toggleStyle(3);
2559
+ var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
2560
+ var blockContentStart = (node) => {
2561
+ const atx = /^ATXHeading(\d)/.exec(node.name);
2562
+ if (atx) {
2563
+ return Math.min(node.to, node.from + +atx[1] + 1);
2537
2564
  }
2565
+ return node.from;
2566
+ };
2567
+ var blockContentEnd = (node, doc) => {
2568
+ const setext = /^SetextHeading(\d)/.exec(node.name);
2569
+ const lastLine = doc.lineAt(node.to);
2570
+ if (setext || /^[\s>]*$/.exec(lastLine.text)) {
2571
+ return lastLine.from - 1;
2572
+ }
2573
+ return node.to;
2574
+ };
2575
+ var skipSpaces = (pos, doc, dir, limit) => {
2576
+ const line = doc.lineAt(pos);
2577
+ while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
2578
+ pos += dir;
2579
+ }
2580
+ return pos;
2581
+ };
2582
+ var skipMarkers = (pos, tree, dir, limit) => {
2583
+ for (; ; ) {
2584
+ const next = tree.resolve(pos, dir);
2585
+ if (!IgnoreInline.has(next.name)) {
2586
+ return pos;
2587
+ }
2588
+ const moveTo = dir < 0 ? next.from : next.to;
2589
+ if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
2590
+ return pos;
2591
+ }
2592
+ pos = moveTo;
2593
+ }
2594
+ };
2595
+ var snippets = {
2596
+ codeblock: snippet([
2597
+ //
2598
+ "```#{}",
2599
+ "",
2600
+ "```"
2601
+ ].join("\n")),
2602
+ table: snippet([
2603
+ //
2604
+ "| #{col1} | #{col2} |",
2605
+ "| ---- | ---- |",
2606
+ "| #{val1} | #{val2} |",
2607
+ "| #{val3} | #{val4} |",
2608
+ ""
2609
+ ].join("\n"))
2538
2610
  };
2539
- var LinkButton = class extends WidgetType3 {
2540
- constructor(url, render) {
2541
- super();
2542
- this.url = url;
2543
- this.render = render;
2544
- }
2545
- eq(other) {
2546
- return this.url === other.url;
2547
- }
2548
- toDOM(view) {
2549
- const el = document.createElement("span");
2550
- this.render(el, this.url);
2551
- return el;
2552
- }
2611
+ var insertTable = (view) => {
2612
+ const { selection: { main }, doc } = view.state;
2613
+ const { number } = doc.lineAt(main.anchor);
2614
+ const { from } = doc.line(number);
2615
+ snippets.table(view, null, from, from);
2553
2616
  };
2554
- var CheckboxWidget = class extends WidgetType3 {
2555
- constructor(_checked) {
2556
- super();
2557
- this._checked = _checked;
2617
+ var removeLinkInner = (from, to, changes, state2) => {
2618
+ syntaxTree(state2).iterate({
2619
+ from,
2620
+ to,
2621
+ enter: (node) => {
2622
+ if (node.name === "Link" && node.from < to && node.to > from) {
2623
+ node.node.cursor().iterate((node2) => {
2624
+ const { name } = node2;
2625
+ if (name === "LinkMark" || name === "LinkLabel") {
2626
+ changes.push({
2627
+ from: node2.from,
2628
+ to: node2.to
2629
+ });
2630
+ } else if (name === "LinkTitle" || name === "URL") {
2631
+ changes.push({
2632
+ from: skipSpaces(node2.from, state2.doc, -1),
2633
+ to: skipSpaces(node2.to, state2.doc, 1)
2634
+ });
2635
+ }
2636
+ });
2637
+ return false;
2638
+ }
2639
+ }
2640
+ });
2641
+ };
2642
+ var removeLink = ({ state: state2, dispatch }) => {
2643
+ const changes = [];
2644
+ for (const { from, to } of state2.selection.ranges) {
2645
+ removeLinkInner(from, to, changes, state2);
2558
2646
  }
2559
- eq(other) {
2560
- return this._checked === other._checked;
2647
+ if (!changes) {
2648
+ return false;
2561
2649
  }
2562
- toDOM(view) {
2563
- const input = document.createElement("input");
2564
- input.className = "cm-task-checkbox ch-checkbox ch-focus-ring -mbs-0.5";
2565
- input.type = "checkbox";
2566
- input.checked = this._checked;
2567
- if (view.state.readOnly) {
2568
- input.setAttribute("disabled", "true");
2569
- } else {
2570
- input.onmousedown = (event) => {
2571
- const pos = view.posAtDOM(input);
2572
- const text = view.state.sliceDoc(pos, pos + 3);
2573
- if (text === (this._checked ? "[x]" : "[ ]")) {
2574
- view.dispatch({
2575
- changes: {
2576
- from: pos + 1,
2577
- to: pos + 2,
2578
- insert: this._checked ? " " : "x"
2650
+ dispatch(state2.update({
2651
+ changes,
2652
+ userEvent: "format.link.remove",
2653
+ scrollIntoView: true
2654
+ }));
2655
+ return true;
2656
+ };
2657
+ var addLink = ({ url, image: image2 } = {}) => ({ state: state2, dispatch }) => {
2658
+ const changes = state2.changeByRange((range) => {
2659
+ let { from, to } = range;
2660
+ const cutStyles = [];
2661
+ let okay = null;
2662
+ syntaxTree(state2).iterate({
2663
+ from,
2664
+ to,
2665
+ enter: (node) => {
2666
+ if (Object.hasOwn(Textblocks, node.name)) {
2667
+ okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state2.doc);
2668
+ } else if (Object.hasOwn(InlineMarker, node.name)) {
2669
+ const sNode = node.node;
2670
+ if (node.from < from && node.to <= to) {
2671
+ if (sNode.firstChild.to === from) {
2672
+ from = node.from;
2673
+ } else {
2674
+ cutStyles.push(sNode);
2579
2675
  }
2580
- });
2581
- event.preventDefault();
2676
+ } else if (node.from >= from && node.to > to) {
2677
+ if (sNode.lastChild.from === to) {
2678
+ to = node.to;
2679
+ } else {
2680
+ cutStyles.push(sNode);
2681
+ }
2682
+ }
2582
2683
  }
2684
+ }
2685
+ });
2686
+ if (okay === null) {
2687
+ const line = state2.doc.lineAt(from);
2688
+ okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
2689
+ }
2690
+ if (!okay) {
2691
+ return {
2692
+ range
2583
2693
  };
2584
2694
  }
2585
- return input;
2586
- }
2587
- ignoreEvent() {
2695
+ const changes2 = [];
2696
+ const changesAfter = [];
2697
+ removeLinkInner(from, to, changesAfter, state2);
2698
+ let cursorOffset = 1;
2699
+ for (const style of cutStyles) {
2700
+ const type = InlineMarker[style.name];
2701
+ const mark2 = inlineMarkerText(type);
2702
+ if (style.from < from) {
2703
+ changes2.push({
2704
+ from: skipSpaces(from, state2.doc, -1),
2705
+ insert: mark2
2706
+ });
2707
+ changesAfter.push({
2708
+ from: skipSpaces(from, state2.doc, 1, to),
2709
+ insert: mark2
2710
+ });
2711
+ } else {
2712
+ changes2.push({
2713
+ from: skipSpaces(to, state2.doc, -1, from),
2714
+ insert: mark2
2715
+ });
2716
+ const after = skipSpaces(to, state2.doc, 1);
2717
+ if (after === to) {
2718
+ cursorOffset += mark2.length;
2719
+ }
2720
+ changesAfter.push({
2721
+ from: after,
2722
+ insert: mark2
2723
+ });
2724
+ }
2725
+ }
2726
+ changes2.push({
2727
+ from,
2728
+ insert: image2 ? "![" : "["
2729
+ }, {
2730
+ from: to,
2731
+ insert: `](${url ?? ""})`
2732
+ });
2733
+ const changeSet = state2.changes(changes2.concat(changesAfter));
2734
+ return {
2735
+ changes: changeSet,
2736
+ range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0))
2737
+ };
2738
+ });
2739
+ if (changes.changes.empty) {
2588
2740
  return false;
2589
2741
  }
2742
+ dispatch(state2.update(changes, {
2743
+ userEvent: "format.link.add",
2744
+ scrollIntoView: true
2745
+ }));
2746
+ return true;
2590
2747
  };
2591
- var hide = Decoration5.replace({});
2592
- var fencedCodeLine = Decoration5.line({
2593
- class: mx2("cm-code cm-codeblock-line")
2594
- });
2595
- var fencedCodeLineFirst = Decoration5.line({
2596
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
2597
- });
2598
- var fencedCodeLineLast = Decoration5.line({
2599
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
2600
- });
2601
- var horizontalRule = Decoration5.replace({
2602
- widget: new HorizontalRuleWidget()
2603
- });
2604
- var checkedTask = Decoration5.replace({
2605
- widget: new CheckboxWidget(true)
2606
- });
2607
- var uncheckedTask = Decoration5.replace({
2608
- widget: new CheckboxWidget(false)
2609
- });
2610
- var editingRange = (state2, range, focus) => {
2611
- const { readOnly, selection: { main: { head } } } = state2;
2612
- return focus && !readOnly && head >= range.from && head <= range.to;
2613
- };
2614
- var MarksByParent = /* @__PURE__ */ new Set([
2615
- "CodeMark",
2616
- "EmphasisMark",
2617
- "StrikethroughMark",
2618
- "SubscriptMark",
2619
- "SuperscriptMark"
2620
- ]);
2621
- var buildDecorations = (view, options, focus) => {
2622
- const deco = new RangeSetBuilder2();
2623
- const atomicDeco = new RangeSetBuilder2();
2624
- const { state: state2 } = view;
2625
- for (const { from, to } of view.visibleRanges) {
2748
+ var addList = (type) => ({ state: state2, dispatch }) => {
2749
+ let lastBlock = -1;
2750
+ let counter = 1;
2751
+ let first = true;
2752
+ let parentColumn = null;
2753
+ const blocks = [];
2754
+ for (const { from, to } of state2.selection.ranges) {
2626
2755
  syntaxTree(state2).iterate({
2627
2756
  from,
2628
2757
  to,
2629
2758
  enter: (node) => {
2630
- if (node.name === "FencedCode") {
2631
- const editing = editingRange(state2, node, focus);
2632
- for (const block of view.viewportLineBlocks) {
2633
- if (block.to < node.from) {
2634
- continue;
2635
- }
2636
- if (block.from > node.to) {
2637
- break;
2638
- }
2639
- const first = block.from <= node.from;
2640
- const last = block.to >= node.to && /^(\s>)*```$/.test(state2.doc.sliceString(block.from, block.to));
2641
- deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2642
- if (!editing && (first || last)) {
2643
- atomicDeco.add(block.from, block.to, hide);
2644
- }
2645
- }
2646
- return false;
2647
- } else if (node.name === "Link") {
2648
- const marks = node.node.getChildren("LinkMark");
2649
- const urlNode = node.node.getChild("URL");
2650
- const editing = editingRange(state2, node, focus);
2651
- if (urlNode && marks.length >= 2) {
2652
- const url = state2.sliceDoc(urlNode.from, urlNode.to);
2653
- if (!editing) {
2654
- atomicDeco.add(node.from, marks[0].to, hide);
2759
+ if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
2760
+ if (first) {
2761
+ let before = node.node.prevSibling;
2762
+ while (before && /Mark$/.test(before.name)) {
2763
+ before = before.prevSibling;
2655
2764
  }
2656
- deco.add(marks[0].to, marks[1].from, Decoration5.mark({
2657
- tagName: "a",
2658
- attributes: {
2659
- class: "cm-link",
2660
- href: url,
2661
- rel: "noreferrer",
2662
- target: "_blank"
2765
+ if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
2766
+ const item = before.lastChild;
2767
+ const itemLine = state2.doc.lineAt(item.from);
2768
+ const itemText = itemLine.text.slice(item.from - itemLine.from);
2769
+ parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
2770
+ if (type === 0) {
2771
+ const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
2772
+ if (mark2) {
2773
+ parentColumn += mark2[1].length;
2774
+ counter = +mark2[1] + 1;
2775
+ }
2663
2776
  }
2664
- }));
2665
- if (!editing) {
2666
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
2667
- widget: new LinkButton(url, options.renderLinkButton)
2668
- }) : hide);
2669
2777
  }
2778
+ first = false;
2670
2779
  }
2671
- } else if (node.name === "HeaderMark") {
2672
- const parent = node.node.parent;
2673
- if (/^ATX/.test(parent.name) && !editingRange(state2, state2.doc.lineAt(node.from), focus)) {
2674
- const next = state2.doc.sliceString(node.to, node.to + 1);
2675
- atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2676
- }
2677
- } else if (node.name === "HorizontalRule") {
2678
- if (!editingRange(state2, node, focus)) {
2679
- deco.add(node.from, node.to, horizontalRule);
2680
- }
2681
- } else if (node.name === "TaskMarker") {
2682
- if (!editingRange(state2, node, focus)) {
2683
- const checked = state2.doc.sliceString(node.from + 1, node.to - 1) === "x";
2684
- atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
2685
- class: "cm-task"
2686
- }));
2687
- atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2688
- }
2689
- } else if (MarksByParent.has(node.name)) {
2690
- if (!editingRange(state2, node.node.parent, focus)) {
2691
- atomicDeco.add(node.from, node.to, hide);
2780
+ if (node.from === lastBlock) {
2781
+ return;
2692
2782
  }
2783
+ lastBlock = node.from;
2784
+ blocks.push({
2785
+ node: node.node,
2786
+ counter,
2787
+ parentColumn
2788
+ });
2789
+ counter++;
2790
+ return false;
2791
+ }
2792
+ },
2793
+ leave: (node) => {
2794
+ if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
2795
+ counter = 1;
2796
+ parentColumn = null;
2693
2797
  }
2694
2798
  }
2695
2799
  });
2696
2800
  }
2697
- return {
2698
- deco: deco.finish(),
2699
- atomicDeco: atomicDeco.finish()
2700
- };
2701
- };
2702
- var forceUpdate = StateEffect4.define();
2703
- var decorateMarkdown = (options = {}) => {
2704
- return [
2705
- ViewPlugin4.fromClass(class {
2706
- constructor(view) {
2707
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
2708
- }
2709
- update(update2) {
2710
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
2711
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
2712
- this.clearUpdate();
2713
- } else if (update2.selectionSet) {
2714
- this.scheduleUpdate(update2.view);
2715
- }
2716
- }
2717
- // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
2718
- // the cursor will float in space or be in the wrong position after the decoration is applied.
2719
- scheduleUpdate(view) {
2720
- this.clearUpdate();
2721
- this.pendingUpdate = setTimeout(() => {
2722
- view.dispatch({
2723
- effects: forceUpdate.of(null)
2724
- });
2725
- }, options.selectionChangeDelay);
2726
- }
2727
- clearUpdate() {
2728
- if (this.pendingUpdate) {
2729
- clearTimeout(this.pendingUpdate);
2730
- this.pendingUpdate = void 0;
2801
+ if (!blocks.length) {
2802
+ const { from, to } = state2.doc.lineAt(state2.selection.main.anchor);
2803
+ if (from === to) {
2804
+ const insert = type === 1 ? "- " : type === 0 ? "1. " : "- [ ] ";
2805
+ dispatch(state2.update({
2806
+ changes: [
2807
+ {
2808
+ from,
2809
+ insert
2810
+ }
2811
+ ],
2812
+ selection: {
2813
+ anchor: from + insert.length
2814
+ },
2815
+ userEvent: "format.list.add",
2816
+ scrollIntoView: true
2817
+ }));
2818
+ return true;
2819
+ }
2820
+ return false;
2821
+ }
2822
+ const changes = [];
2823
+ for (let i = 0; i < blocks.length; i++) {
2824
+ const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
2825
+ const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
2826
+ let padding = nodeFrom > 0 && !/\s/.test(state2.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
2827
+ if (type === 0) {
2828
+ padding += String(counter2).length;
2829
+ }
2830
+ let line = state2.doc.lineAt(nodeFrom);
2831
+ const column = nodeFrom - line.from;
2832
+ if (parentColumn2 !== null && parentColumn2 > column) {
2833
+ padding = Math.max(padding, parentColumn2 - column);
2834
+ }
2835
+ let mark2;
2836
+ if (type === 0) {
2837
+ let max = counter2;
2838
+ for (let j = i + 1; j < blocks.length; j++) {
2839
+ if (blocks[j].counter !== max + 1) {
2840
+ break;
2731
2841
  }
2842
+ max++;
2732
2843
  }
2733
- destroy() {
2734
- this.clearUpdate();
2735
- }
2736
- }, {
2737
- provide: (plugin) => [
2738
- EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2739
- EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2740
- EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
2741
- ]
2742
- }),
2743
- formattingStyles
2744
- ];
2745
- };
2746
- var formattingStyles = EditorView12.baseTheme({
2747
- "& .cm-code": {
2748
- fontFamily: getToken("fontFamily.mono", []).join(",")
2749
- },
2750
- "& .cm-codeblock-line": {
2751
- paddingInline: "1rem !important"
2752
- },
2753
- "& .cm-codeblock-end": {
2754
- display: "inline-block",
2755
- width: "100%",
2756
- position: "relative",
2757
- "&::after": {
2758
- position: "absolute",
2759
- inset: 0,
2760
- content: '""'
2844
+ const num = String(counter2);
2845
+ padding = Math.max(String(max).length, padding);
2846
+ mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
2847
+ } else {
2848
+ mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
2849
+ }
2850
+ changes.push({
2851
+ from: nodeFrom,
2852
+ insert: mark2
2853
+ });
2854
+ while (line.to < node.to) {
2855
+ line = state2.doc.lineAt(line.to + 1);
2856
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2857
+ changes.push({
2858
+ from: line.from + Math.min(open, column),
2859
+ insert: " ".repeat(mark2.length)
2860
+ });
2761
2861
  }
2762
- },
2763
- "& .cm-codeblock-first": {
2764
- borderTopLeftRadius: ".5rem",
2765
- borderTopRightRadius: ".5rem"
2766
- },
2767
- "& .cm-codeblock-last": {
2768
- borderBottomLeftRadius: ".5rem",
2769
- borderBottomRightRadius: ".5rem"
2770
- },
2771
- "&light .cm-codeblock-line, &light .cm-activeLine.cm-codeblock-line": {
2772
- background: getToken("extend.semanticColors.input.light"),
2773
- mixBlendMode: "darken"
2774
- },
2775
- "&dark .cm-codeblock-line, &dark .cm-activeLine.cm-codeblock-line": {
2776
- background: getToken("extend.semanticColors.input.dark"),
2777
- mixBlendMode: "lighten"
2778
- },
2779
- "& .cm-hr": {
2780
- display: "inline-block",
2781
- width: "100%",
2782
- height: "0",
2783
- verticalAlign: "middle",
2784
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2785
- },
2786
- "& .cm-task": {
2787
- color: getToken("extend.colors.blue.500")
2788
- },
2789
- "& .cm-task-checkbox": {
2790
- marginLeft: "4px",
2791
- marginRight: "4px"
2792
2862
  }
2793
- });
2794
-
2795
- // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2796
- import { snippet } from "@codemirror/autocomplete";
2797
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2798
- import { EditorSelection } from "@codemirror/state";
2799
- import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
2800
- import { useMemo } from "react";
2801
- import { useStateRef } from "@dxos/react-async";
2802
- var formattingEquals = (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;
2803
- var Inline;
2804
- (function(Inline2) {
2805
- Inline2[Inline2["Strong"] = 0] = "Strong";
2806
- Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2807
- Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2808
- Inline2[Inline2["Code"] = 3] = "Code";
2809
- })(Inline || (Inline = {}));
2810
- var List;
2811
- (function(List2) {
2812
- List2[List2["Ordered"] = 0] = "Ordered";
2813
- List2[List2["Bullet"] = 1] = "Bullet";
2814
- List2[List2["Task"] = 2] = "Task";
2815
- })(List || (List = {}));
2816
- var setHeading = (level) => ({ state: state2, dispatch }) => {
2817
- const { selection: { ranges }, doc } = state2;
2863
+ if (type === 0) {
2864
+ const last = blocks[blocks.length - 1];
2865
+ let next = last.node.nextSibling;
2866
+ while (next && /Mark$/.test(next.name)) {
2867
+ next = next.nextSibling;
2868
+ }
2869
+ if (next?.name === "OrderedList") {
2870
+ renumberListItems(next.firstChild, last.counter + 1, changes, state2.doc);
2871
+ }
2872
+ }
2873
+ const changeSet = state2.changes(changes);
2874
+ dispatch(state2.update({
2875
+ changes: changeSet,
2876
+ selection: state2.selection.map(changeSet, 1),
2877
+ userEvent: "format.list.add",
2878
+ scrollIntoView: true
2879
+ }));
2880
+ return true;
2881
+ };
2882
+ var removeList = (type) => ({ state: state2, dispatch }) => {
2883
+ let lastBlock = -1;
2818
2884
  const changes = [];
2819
- let prevBlock = -1;
2820
- for (const range of ranges) {
2821
- let sawBlock = false;
2822
- syntaxTree2(state2).iterate({
2823
- from: range.from,
2824
- to: range.to,
2825
- enter: (node) => {
2826
- if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
2827
- return;
2828
- }
2829
- sawBlock = true;
2830
- prevBlock = node.from;
2831
- const blockType = Textblocks[node.name];
2832
- const isHeading = /heading(\d)/.exec(blockType);
2833
- const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
2834
- if (curLevel < 0 || curLevel === level) {
2835
- return;
2885
+ const stack = [];
2886
+ const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
2887
+ for (const { from, to } of state2.selection.ranges) {
2888
+ syntaxTree(state2).iterate({
2889
+ from,
2890
+ to,
2891
+ enter: (node) => {
2892
+ const { name } = node;
2893
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2894
+ stack.push(name);
2895
+ } else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
2896
+ stack[stack.length - 1] = "TaskList";
2836
2897
  }
2837
- if (curLevel === 0) {
2898
+ },
2899
+ leave: (node) => {
2900
+ const { name } = node;
2901
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2902
+ stack.pop();
2903
+ } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
2904
+ lastBlock = node.from;
2905
+ let line = state2.doc.lineAt(node.from);
2906
+ const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
2907
+ if (!mark2) {
2908
+ return false;
2909
+ }
2910
+ const column = node.from - line.from;
2838
2911
  changes.push({
2839
2912
  from: node.from,
2840
- insert: "#".repeat(level) + " "
2913
+ to: node.from + mark2[0].length
2841
2914
  });
2842
- } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
2843
- const nextLine = doc.lineAt(node.to);
2844
- if (level) {
2845
- changes.push({
2846
- from: node.from,
2847
- insert: "#".repeat(level) + " "
2848
- });
2915
+ while (line.to < node.to) {
2916
+ line = state2.doc.lineAt(line.to + 1);
2917
+ const open = /^[\s>]*/.exec(line.text)[0].length;
2918
+ if (open > column) {
2919
+ changes.push({
2920
+ from: line.from + column,
2921
+ to: line.from + Math.min(column + mark2[0].length, open)
2922
+ });
2923
+ }
2849
2924
  }
2850
- changes.push({
2851
- from: nextLine.from - 1,
2852
- to: nextLine.to
2853
- });
2854
- } else {
2855
- if (level === 0) {
2856
- changes.push({
2857
- from: node.from,
2858
- to: Math.min(node.to, node.from + curLevel + 1)
2859
- });
2860
- } else if (level < curLevel) {
2861
- changes.push({
2862
- from: node.from,
2863
- to: node.from + (curLevel - level)
2864
- });
2865
- } else {
2866
- changes.push({
2867
- from: node.from,
2868
- insert: "#".repeat(level - curLevel)
2869
- });
2925
+ if (node.to >= to) {
2926
+ renumberListItems(node.node.nextSibling, 1, changes, state2.doc);
2870
2927
  }
2928
+ return false;
2871
2929
  }
2872
2930
  }
2873
2931
  });
2874
- let line;
2875
- if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state2.doc.lineAt(range.from)).text)) {
2876
- changes.push({
2877
- from: line.from,
2878
- to: line.to,
2879
- insert: "#".repeat(level) + " "
2880
- });
2881
- }
2882
2932
  }
2883
2933
  if (!changes.length) {
2884
2934
  return false;
2885
2935
  }
2886
- const changeSet = state2.changes(changes);
2887
2936
  dispatch(state2.update({
2888
- changes: changeSet,
2889
- selection: state2.selection.map(changeSet, 1),
2890
- userEvent: "format.setHeading",
2937
+ changes,
2938
+ userEvent: "format.list.remove",
2891
2939
  scrollIntoView: true
2892
2940
  }));
2893
2941
  return true;
2894
2942
  };
2895
- var setStyle = (type, enable) => ({ state: state2, dispatch }) => {
2896
- const marker = inlineMarkerText(type);
2897
- const changes = state2.changeByRange((range) => {
2898
- if (!enable && range.empty) {
2899
- const after = state2.doc.sliceString(range.head, range.head + 6);
2900
- const found = after.indexOf(marker);
2901
- if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2902
- const before = state2.doc.sliceString(range.head - 6, range.head);
2903
- if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2904
- ...before.slice(before.length - found)
2905
- ].reverse().join("") === after.slice(0, found)) {
2906
- return {
2907
- changes: [
2908
- {
2909
- from: range.head - marker.length - found,
2910
- to: range.head - found
2911
- },
2912
- {
2913
- from: range.head + found,
2914
- to: range.head + found + marker.length
2915
- }
2916
- ],
2917
- range: EditorSelection.cursor(range.from - marker.length)
2918
- };
2919
- }
2943
+ var toggleList = (type) => (target) => {
2944
+ const formatting = getFormatting(target.state);
2945
+ const active = formatting.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
2946
+ return (active ? removeList(type) : addList(type))(target);
2947
+ };
2948
+ var renumberListItems = (item, counter, changes, doc) => {
2949
+ for (; item; item = item.nextSibling) {
2950
+ if (item.name === "ListItem") {
2951
+ const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
2952
+ if (!number || +number[2] === counter) {
2953
+ break;
2920
2954
  }
2955
+ const size = number[1].length + number[2].length;
2956
+ const newNum = String(counter);
2957
+ changes.push({
2958
+ from: item.from + Math.max(0, size - newNum.length),
2959
+ to: item.from + size,
2960
+ insert: newNum
2961
+ });
2962
+ counter++;
2921
2963
  }
2922
- const changes2 = [];
2923
- const changesAtEnd = [];
2924
- let blockStart = -1;
2925
- let blockEnd = -1;
2926
- let startCovered = false;
2927
- let endCovered = false;
2928
- let { from, to } = range;
2929
- syntaxTree2(state2).iterate({
2964
+ }
2965
+ };
2966
+ var setBlockquote = (enable) => ({ state: state2, dispatch }) => {
2967
+ const lines = [];
2968
+ let lastBlock = -1;
2969
+ for (const { from, to } of state2.selection.ranges) {
2970
+ const sawBlock = false;
2971
+ syntaxTree(state2).iterate({
2930
2972
  from,
2931
2973
  to,
2932
2974
  enter: (node) => {
2933
- const { name } = node;
2934
- if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2935
- blockStart = blockContentStart(node);
2936
- blockEnd = blockContentEnd(node, state2.doc);
2937
- startCovered = endCovered = false;
2938
- } else if (name === "Link" || name === "Image" && enable) {
2939
- if (from < node.from && to > node.from && to <= node.to) {
2940
- to = node.to;
2941
- } else if (to > node.to && from >= node.from && from < node.to) {
2942
- from = node.from;
2943
- }
2944
- } else if (IgnoreInline.has(name) && enable) {
2945
- if (node.from < from && node.to > from) {
2946
- if (to === from) {
2947
- to = node.to;
2948
- }
2949
- from = node.to;
2950
- }
2951
- if (node.from < to && node.to > to) {
2952
- to = node.from;
2953
- }
2954
- } else if (Object.hasOwn(InlineMarker, name)) {
2955
- const markType = InlineMarker[name];
2956
- const size = inlineMarkerText(markType).length;
2957
- const openEnd = node.from + size;
2958
- const closeStart = node.to - size;
2959
- if (markType === type) {
2960
- if (openEnd <= from && closeStart >= from) {
2961
- startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2962
- from: node.from,
2963
- to: openEnd
2964
- } : true;
2965
- }
2966
- if (openEnd <= to && closeStart >= to) {
2967
- endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2968
- from: closeStart,
2969
- to: node.to
2970
- } : true;
2971
- }
2975
+ if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
2976
+ if (node.from === lastBlock) {
2977
+ return false;
2972
2978
  }
2973
- if (markType === type || type === 3 && enable) {
2974
- if (node.from >= from && openEnd <= to) {
2975
- changes2.push({
2976
- from: node.from,
2977
- to: openEnd
2978
- });
2979
- if (markType !== type && closeStart >= to) {
2980
- changesAtEnd.push({
2981
- from: skipSpaces(Math.min(to, blockEnd), state2.doc, 1, blockEnd),
2982
- insert: inlineMarkerText(markType)
2983
- });
2984
- }
2985
- }
2986
- if (closeStart >= from && node.to <= to) {
2987
- changes2.push({
2988
- from: closeStart,
2989
- to: node.to
2990
- });
2991
- if (markType !== type && openEnd <= from) {
2992
- changes2.push({
2993
- from: skipSpaces(Math.max(from, blockStart), state2.doc, -1, blockStart),
2994
- insert: inlineMarkerText(markType)
2995
- });
2979
+ lastBlock = node.from;
2980
+ let line2 = state2.doc.lineAt(node.from);
2981
+ if (line2.number > 1) {
2982
+ const prevLine = state2.doc.line(line2.number - 1);
2983
+ if (/^[>\s]*$/.test(prevLine.text)) {
2984
+ if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
2985
+ lines.push(prevLine);
2996
2986
  }
2997
2987
  }
2998
2988
  }
2999
- }
3000
- },
3001
- leave: (node) => {
3002
- if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
3003
- const rangeStart = Math.max(from, blockStart);
3004
- const rangeEnd = Math.min(to, blockEnd);
3005
- if (enable) {
3006
- if (!startCovered) {
3007
- changes2.push({
3008
- from: rangeStart,
3009
- insert: marker
3010
- });
3011
- }
3012
- if (!endCovered) {
3013
- changes2.push({
3014
- from: rangeEnd,
3015
- insert: marker
3016
- });
3017
- }
3018
- } else {
3019
- if (typeof startCovered === "object") {
3020
- changes2.push(startCovered);
3021
- } else if (startCovered) {
3022
- changes2.push({
3023
- from: skipSpaces(rangeStart, state2.doc, -1, blockStart),
3024
- insert: marker
3025
- });
2989
+ for (; ; ) {
2990
+ lines.push(line2);
2991
+ if (line2.to >= node.to) {
2992
+ break;
3026
2993
  }
3027
- if (typeof endCovered === "object") {
3028
- changes2.push(endCovered);
3029
- } else if (endCovered) {
3030
- changes2.push({
3031
- from: skipSpaces(rangeEnd, state2.doc, 1, blockEnd),
3032
- insert: marker
3033
- });
2994
+ line2 = state2.doc.line(line2.number + 1);
2995
+ }
2996
+ if (!enable && line2.number < state2.doc.lines) {
2997
+ const nextLine = state2.doc.line(line2.number + 1);
2998
+ if (/^[>\s]*$/.test(nextLine.text)) {
2999
+ lines.push(nextLine);
3034
3000
  }
3035
3001
  }
3002
+ return false;
3036
3003
  }
3037
3004
  }
3038
3005
  });
3039
- if (blockStart < 0 && range.empty && enable && !/\S/.test(state2.doc.lineAt(range.from).text)) {
3040
- return {
3041
- changes: {
3042
- from: range.head,
3043
- insert: marker + marker
3044
- },
3045
- range: EditorSelection.cursor(range.head + marker.length)
3046
- };
3047
- }
3048
- const changeSet = state2.changes(changes2.concat(changesAtEnd));
3049
- return {
3050
- changes: changeSet,
3051
- range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
3052
- };
3053
- });
3054
- dispatch(state2.update(changes, {
3055
- userEvent: enable ? "format.style.add" : "format.style.remove",
3056
- scrollIntoView: true
3057
- }));
3058
- return true;
3059
- };
3060
- var addStyle = (style) => setStyle(style, true);
3061
- var removeStyle = (style) => setStyle(style, false);
3062
- var toggleStyle = (style) => (arg) => {
3063
- const form = getFormatting(arg.state);
3064
- return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
3065
- };
3066
- var toggleStrong = toggleStyle(0);
3067
- var toggleEmphasis = toggleStyle(1);
3068
- var toggleStrikethrough = toggleStyle(2);
3069
- var toggleInlineCode = toggleStyle(3);
3070
- var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
3071
- var blockContentStart = (node) => {
3072
- const atx = /^ATXHeading(\d)/.exec(node.name);
3073
- if (atx) {
3074
- return Math.min(node.to, node.from + +atx[1] + 1);
3075
- }
3076
- return node.from;
3077
- };
3078
- var blockContentEnd = (node, doc) => {
3079
- const setext = /^SetextHeading(\d)/.exec(node.name);
3080
- const lastLine = doc.lineAt(node.to);
3081
- if (setext || /^[\s>]*$/.exec(lastLine.text)) {
3082
- return lastLine.from - 1;
3083
- }
3084
- return node.to;
3085
- };
3086
- var skipSpaces = (pos, doc, dir, limit) => {
3087
- const line = doc.lineAt(pos);
3088
- while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
3089
- pos += dir;
3090
- }
3091
- return pos;
3092
- };
3093
- var skipMarkers = (pos, tree, dir, limit) => {
3094
- for (; ; ) {
3095
- const next = tree.resolve(pos, dir);
3096
- if (!IgnoreInline.has(next.name)) {
3097
- return pos;
3098
- }
3099
- const moveTo = dir < 0 ? next.from : next.to;
3100
- if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
3101
- return pos;
3006
+ let line;
3007
+ if (!sawBlock && enable && from === to && !/\S/.test((line = state2.doc.lineAt(from)).text)) {
3008
+ lines.push(line);
3102
3009
  }
3103
- pos = moveTo;
3104
3010
  }
3105
- };
3106
- var snippets = {
3107
- codeblock: snippet([
3108
- //
3109
- "```#{}",
3110
- "",
3111
- "```"
3112
- ].join("\n")),
3113
- table: snippet([
3114
- //
3115
- "| #{col1} | #{col2} |",
3116
- "| ---- | ---- |",
3117
- "| #{val1} | #{val2} |",
3118
- "| #{val3} | #{val4} |",
3119
- ""
3120
- ].join("\n"))
3121
- };
3122
- var insertTable = (view) => {
3123
- const { selection: { main }, doc } = view.state;
3124
- const { number } = doc.lineAt(main.anchor);
3125
- const { from } = doc.line(number);
3126
- snippets.table(view, null, from, from);
3127
- };
3128
- var removeLinkInner = (from, to, changes, state2) => {
3129
- syntaxTree2(state2).iterate({
3130
- from,
3131
- to,
3132
- enter: (node) => {
3133
- if (node.name === "Link" && node.from < to && node.to > from) {
3134
- node.node.cursor().iterate((node2) => {
3135
- const { name } = node2;
3136
- if (name === "LinkMark" || name === "LinkLabel") {
3137
- changes.push({
3138
- from: node2.from,
3139
- to: node2.to
3140
- });
3141
- } else if (name === "LinkTitle" || name === "URL") {
3142
- changes.push({
3143
- from: skipSpaces(node2.from, state2.doc, -1),
3144
- to: skipSpaces(node2.to, state2.doc, 1)
3145
- });
3146
- }
3011
+ const changes = [];
3012
+ for (const line of lines) {
3013
+ if (enable) {
3014
+ changes.push({
3015
+ from: line.from,
3016
+ insert: /\S/.test(line.text) ? "> " : ">"
3017
+ });
3018
+ } else {
3019
+ const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
3020
+ if (quote) {
3021
+ changes.push({
3022
+ from: line.from + quote[1].length,
3023
+ to: line.from + quote[0].length
3147
3024
  });
3148
- return false;
3149
3025
  }
3150
3026
  }
3151
- });
3152
- };
3153
- var removeLink = ({ state: state2, dispatch }) => {
3154
- const changes = [];
3155
- for (const { from, to } of state2.selection.ranges) {
3156
- removeLinkInner(from, to, changes, state2);
3157
3027
  }
3158
- if (!changes) {
3028
+ if (!changes.length) {
3159
3029
  return false;
3160
3030
  }
3031
+ const changeSet = state2.changes(changes);
3161
3032
  dispatch(state2.update({
3162
- changes,
3163
- userEvent: "format.link.remove",
3033
+ changes: changeSet,
3034
+ selection: state2.selection.map(changeSet, 1),
3035
+ userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
3164
3036
  scrollIntoView: true
3165
3037
  }));
3166
3038
  return true;
3167
3039
  };
3168
- var addLink = ({ url, image: image2 } = {}) => ({ state: state2, dispatch }) => {
3169
- const changes = state2.changeByRange((range) => {
3170
- let { from, to } = range;
3171
- const cutStyles = [];
3172
- let okay = null;
3173
- syntaxTree2(state2).iterate({
3040
+ var addBlockquote = setBlockquote(true);
3041
+ var removeBlockquote = setBlockquote(false);
3042
+ var toggleBlockquote = (target) => {
3043
+ return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
3044
+ };
3045
+ var addCodeblock = (target) => {
3046
+ const { state: state2, dispatch } = target;
3047
+ const { selection } = state2;
3048
+ if (selection.ranges.length === 1 && selection.main.empty) {
3049
+ const { head } = selection.main;
3050
+ const line = state2.doc.lineAt(head);
3051
+ if (!/\S/.test(line.text) && head === line.from) {
3052
+ snippets.codeblock(target, null, line.from, line.to);
3053
+ return true;
3054
+ }
3055
+ }
3056
+ const ranges = [];
3057
+ for (const { from, to } of selection.ranges) {
3058
+ let blockFrom = from;
3059
+ let blockTo = to;
3060
+ syntaxTree(state2).iterate({
3174
3061
  from,
3175
3062
  to,
3176
3063
  enter: (node) => {
3177
3064
  if (Object.hasOwn(Textblocks, node.name)) {
3178
- okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state2.doc);
3179
- } else if (Object.hasOwn(InlineMarker, node.name)) {
3180
- const sNode = node.node;
3181
- if (node.from < from && node.to <= to) {
3182
- if (sNode.firstChild.to === from) {
3183
- from = node.from;
3184
- } else {
3185
- cutStyles.push(sNode);
3186
- }
3187
- } else if (node.from >= from && node.to > to) {
3188
- if (sNode.lastChild.from === to) {
3189
- to = node.to;
3190
- } else {
3191
- cutStyles.push(sNode);
3192
- }
3065
+ if (from >= node.from && to <= node.to) {
3066
+ blockFrom = node.from;
3067
+ blockTo = node.to;
3068
+ } else {
3069
+ blockFrom = Math.min(blockFrom, state2.doc.lineAt(node.from).from);
3070
+ blockTo = Math.max(blockTo, state2.doc.lineAt(node.to).to);
3193
3071
  }
3194
3072
  }
3195
3073
  }
3196
3074
  });
3197
- if (okay === null) {
3198
- const line = state2.doc.lineAt(from);
3199
- okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
3200
- }
3201
- if (!okay) {
3202
- return {
3203
- range
3204
- };
3205
- }
3206
- const changes2 = [];
3207
- const changesAfter = [];
3208
- removeLinkInner(from, to, changesAfter, state2);
3209
- let cursorOffset = 1;
3210
- for (const style of cutStyles) {
3211
- const type = InlineMarker[style.name];
3212
- const mark2 = inlineMarkerText(type);
3213
- if (style.from < from) {
3214
- changes2.push({
3215
- from: skipSpaces(from, state2.doc, -1),
3216
- insert: mark2
3217
- });
3218
- changesAfter.push({
3219
- from: skipSpaces(from, state2.doc, 1, to),
3220
- insert: mark2
3221
- });
3222
- } else {
3223
- changes2.push({
3224
- from: skipSpaces(to, state2.doc, -1, from),
3225
- insert: mark2
3226
- });
3227
- const after = skipSpaces(to, state2.doc, 1);
3228
- if (after === to) {
3229
- cursorOffset += mark2.length;
3230
- }
3231
- changesAfter.push({
3232
- from: after,
3233
- insert: mark2
3234
- });
3235
- }
3075
+ if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
3076
+ ranges[ranges.length - 1].to = blockTo;
3077
+ } else {
3078
+ ranges.push({
3079
+ from: blockFrom,
3080
+ to: blockTo
3081
+ });
3236
3082
  }
3237
- changes2.push({
3238
- from,
3239
- insert: image2 ? "![" : "["
3240
- }, {
3241
- from: to,
3242
- insert: `](${url ?? ""})`
3243
- });
3244
- const changeSet = state2.changes(changes2.concat(changesAfter));
3245
- return {
3246
- changes: changeSet,
3247
- range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0))
3248
- };
3083
+ }
3084
+ if (!ranges.length) {
3085
+ return false;
3086
+ }
3087
+ const changes = ranges.map(({ from, to }) => {
3088
+ const column = from - state2.doc.lineAt(from).from;
3089
+ return [
3090
+ {
3091
+ from,
3092
+ insert: "```\n" + " ".repeat(column)
3093
+ },
3094
+ {
3095
+ from: to,
3096
+ insert: "\n" + " ".repeat(column) + "```"
3097
+ }
3098
+ ];
3249
3099
  });
3250
- if (changes.changes.empty) {
3251
- return false;
3252
- }
3253
- dispatch(state2.update(changes, {
3254
- userEvent: "format.link.add",
3100
+ dispatch(state2.update({
3101
+ changes,
3102
+ userEvent: "format.codeblock.add",
3255
3103
  scrollIntoView: true
3256
3104
  }));
3257
3105
  return true;
3258
3106
  };
3259
- var addList = (type) => ({ state: state2, dispatch }) => {
3107
+ var removeCodeblock = ({ state: state2, dispatch }) => {
3108
+ const changes = [];
3260
3109
  let lastBlock = -1;
3261
- let counter = 1;
3262
- let first = true;
3263
- let parentColumn = null;
3264
- const blocks = [];
3265
3110
  for (const { from, to } of state2.selection.ranges) {
3266
- syntaxTree2(state2).iterate({
3111
+ syntaxTree(state2).iterate({
3267
3112
  from,
3268
3113
  to,
3269
3114
  enter: (node) => {
3270
- if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
3271
- if (first) {
3272
- let before = node.node.prevSibling;
3273
- while (before && /Mark$/.test(before.name)) {
3274
- before = before.prevSibling;
3115
+ if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3116
+ lastBlock = node.from;
3117
+ const firstLine = state2.doc.lineAt(node.from);
3118
+ if (node.name === "FencedCode") {
3119
+ changes.push({
3120
+ from: node.from,
3121
+ to: firstLine.to + 1 + node.from - firstLine.from
3122
+ });
3123
+ const lastLine = state2.doc.lineAt(node.to);
3124
+ if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3125
+ changes.push({
3126
+ from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
3127
+ to: lastLine.to
3128
+ });
3275
3129
  }
3276
- if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
3277
- const item = before.lastChild;
3278
- const itemLine = state2.doc.lineAt(item.from);
3279
- const itemText = itemLine.text.slice(item.from - itemLine.from);
3280
- parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
3281
- if (type === 0) {
3282
- const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
3283
- if (mark2) {
3284
- parentColumn += mark2[1].length;
3285
- counter = +mark2[1] + 1;
3286
- }
3130
+ } else {
3131
+ const column = node.from - firstLine.from;
3132
+ for (let line = firstLine; ; line = state2.doc.line(line.number + 1)) {
3133
+ changes.push({
3134
+ from: line.from + column - 4,
3135
+ to: line.from + column
3136
+ });
3137
+ if (line.to >= node.to) {
3138
+ break;
3287
3139
  }
3288
3140
  }
3289
- first = false;
3290
3141
  }
3291
- if (node.from === lastBlock) {
3292
- return;
3293
- }
3294
- lastBlock = node.from;
3295
- blocks.push({
3296
- node: node.node,
3297
- counter,
3298
- parentColumn
3299
- });
3300
- counter++;
3301
- return false;
3302
- }
3303
- },
3304
- leave: (node) => {
3305
- if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
3306
- counter = 1;
3307
- parentColumn = null;
3308
3142
  }
3309
3143
  }
3310
3144
  });
3311
3145
  }
3312
- if (!blocks.length) {
3313
- const { from, to } = state2.doc.lineAt(state2.selection.main.anchor);
3314
- if (from === to) {
3315
- const insert = type === 1 ? "- " : type === 0 ? "1. " : "- [ ] ";
3316
- dispatch(state2.update({
3317
- changes: [
3318
- {
3319
- from,
3320
- insert
3321
- }
3322
- ],
3323
- selection: {
3324
- anchor: from + insert.length
3325
- },
3326
- userEvent: "format.list.add",
3327
- scrollIntoView: true
3328
- }));
3329
- return true;
3330
- }
3146
+ if (!changes.length) {
3331
3147
  return false;
3332
3148
  }
3333
- const changes = [];
3334
- for (let i = 0; i < blocks.length; i++) {
3335
- const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
3336
- const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
3337
- let padding = nodeFrom > 0 && !/\s/.test(state2.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
3338
- if (type === 0) {
3339
- padding += String(counter2).length;
3340
- }
3341
- let line = state2.doc.lineAt(nodeFrom);
3342
- const column = nodeFrom - line.from;
3343
- if (parentColumn2 !== null && parentColumn2 > column) {
3344
- padding = Math.max(padding, parentColumn2 - column);
3345
- }
3346
- let mark2;
3347
- if (type === 0) {
3348
- let max = counter2;
3349
- for (let j = i + 1; j < blocks.length; j++) {
3350
- if (blocks[j].counter !== max + 1) {
3351
- break;
3352
- }
3353
- max++;
3354
- }
3355
- const num = String(counter2);
3356
- padding = Math.max(String(max).length, padding);
3357
- mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
3358
- } else {
3359
- mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
3360
- }
3361
- changes.push({
3362
- from: nodeFrom,
3363
- insert: mark2
3364
- });
3365
- while (line.to < node.to) {
3366
- line = state2.doc.lineAt(line.to + 1);
3367
- const open = /^[\s>]*/.exec(line.text)[0].length;
3368
- changes.push({
3369
- from: line.from + Math.min(open, column),
3370
- insert: " ".repeat(mark2.length)
3371
- });
3372
- }
3373
- }
3374
- if (type === 0) {
3375
- const last = blocks[blocks.length - 1];
3376
- let next = last.node.nextSibling;
3377
- while (next && /Mark$/.test(next.name)) {
3378
- next = next.nextSibling;
3379
- }
3380
- if (next?.name === "OrderedList") {
3381
- renumberListItems(next.firstChild, last.counter + 1, changes, state2.doc);
3382
- }
3383
- }
3384
- const changeSet = state2.changes(changes);
3385
3149
  dispatch(state2.update({
3386
- changes: changeSet,
3387
- selection: state2.selection.map(changeSet, 1),
3388
- userEvent: "format.list.add",
3150
+ changes,
3151
+ userEvent: "format.codeblock.remove",
3389
3152
  scrollIntoView: true
3390
3153
  }));
3391
3154
  return true;
3392
3155
  };
3393
- var removeList = (type) => ({ state: state2, dispatch }) => {
3394
- let lastBlock = -1;
3395
- const changes = [];
3156
+ var toggleCodeblock = (target) => {
3157
+ return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3158
+ };
3159
+ var formattingKeymap = (options = {}) => {
3160
+ return [
3161
+ keymap6.of([
3162
+ {
3163
+ key: "meta-b",
3164
+ run: toggleStrong
3165
+ }
3166
+ ])
3167
+ ];
3168
+ };
3169
+ var InlineMarker = {
3170
+ Emphasis: 1,
3171
+ StrongEmphasis: 0,
3172
+ InlineCode: 3,
3173
+ Strikethrough: 2
3174
+ };
3175
+ var IgnoreInline = /* @__PURE__ */ new Set([
3176
+ "Autolink",
3177
+ "CodeMark",
3178
+ "CodeText",
3179
+ "Comment",
3180
+ "EmphasisMark",
3181
+ "Hardbreak",
3182
+ "HeaderMark",
3183
+ "HTMLTag",
3184
+ "LinkMark",
3185
+ "ListMark",
3186
+ "ProcessingInstruction",
3187
+ "QuoteMark",
3188
+ "StrikethroughMark",
3189
+ "SubscriptMark",
3190
+ "SuperscriptMark",
3191
+ "TaskMarker"
3192
+ ]);
3193
+ var Textblocks = {
3194
+ ATXHeading1: "heading1",
3195
+ ATXHeading2: "heading2",
3196
+ ATXHeading3: "heading3",
3197
+ ATXHeading4: "heading4",
3198
+ ATXHeading5: "heading5",
3199
+ ATXHeading6: "heading6",
3200
+ CodeBlock: "codeblock",
3201
+ FencedCode: "codeblock",
3202
+ Paragraph: "paragraph",
3203
+ SetextHeading1: "heading1",
3204
+ SetextHeading2: "heading2",
3205
+ TableCell: "tablecell",
3206
+ Task: "paragraph"
3207
+ };
3208
+ var getFormatting = (state2) => {
3209
+ let blockType = null;
3210
+ const inline = [
3211
+ null,
3212
+ null,
3213
+ null,
3214
+ null
3215
+ ];
3216
+ let link = false;
3217
+ let blockQuote = null;
3218
+ let listStyle = null;
3396
3219
  const stack = [];
3397
- const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
3398
- for (const { from, to } of state2.selection.ranges) {
3399
- syntaxTree2(state2).iterate({
3400
- from,
3401
- to,
3220
+ let currentBlock = null;
3221
+ const advanceInline = (upto) => {
3222
+ if (!currentBlock) {
3223
+ return;
3224
+ }
3225
+ upto = Math.min(upto, currentBlock.end);
3226
+ if (upto <= currentBlock.pos) {
3227
+ return;
3228
+ }
3229
+ for (let i = 0; i < currentBlock.active.length; i++) {
3230
+ if (inline[i] === false) {
3231
+ continue;
3232
+ } else if (currentBlock.active[i]) {
3233
+ inline[i] = true;
3234
+ } else if (/\S/.test(state2.doc.sliceString(currentBlock.pos, upto))) {
3235
+ inline[i] = false;
3236
+ }
3237
+ }
3238
+ currentBlock.pos = upto;
3239
+ };
3240
+ const skipInline = (upto) => {
3241
+ if (currentBlock && upto > currentBlock.pos) {
3242
+ currentBlock.pos = Math.min(upto, currentBlock.end);
3243
+ }
3244
+ };
3245
+ const { selection } = state2;
3246
+ for (const range of selection.ranges) {
3247
+ if (range.empty && inline.some((v) => v === null)) {
3248
+ const contextSize = Math.min(range.head, 6);
3249
+ const contextBefore = state2.doc.sliceString(range.head - contextSize, range.head);
3250
+ let contextAfter = state2.doc.sliceString(range.head, range.head + contextSize);
3251
+ for (let i = 0; i < contextSize; i++) {
3252
+ const ch = contextAfter[i];
3253
+ if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
3254
+ contextAfter = contextAfter.slice(0, i);
3255
+ break;
3256
+ }
3257
+ }
3258
+ for (let i = 0; i < inline.length; i++) {
3259
+ const mark2 = inlineMarkerText(i);
3260
+ const found = contextAfter.indexOf(mark2);
3261
+ if (found > -1) {
3262
+ contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3263
+ if (inline[i] === null) {
3264
+ inline[i] = true;
3265
+ }
3266
+ }
3267
+ }
3268
+ }
3269
+ syntaxTree(state2).iterate({
3270
+ from: range.from,
3271
+ to: range.to,
3402
3272
  enter: (node) => {
3273
+ advanceInline(node.from);
3403
3274
  const { name } = node;
3404
3275
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3405
3276
  stack.push(name);
3406
- } else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3407
- stack[stack.length - 1] = "TaskList";
3277
+ } else if (name === "Link") {
3278
+ link = true;
3279
+ } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3280
+ if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3281
+ stack[stack.length - 1] = "TaskList";
3282
+ }
3283
+ const blockCode = Textblocks[name];
3284
+ if (blockType === null) {
3285
+ blockType = blockCode;
3286
+ } else if (blockType !== blockCode) {
3287
+ blockType = false;
3288
+ }
3289
+ if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
3290
+ currentBlock = {
3291
+ pos: Math.max(range.from, node.from),
3292
+ end: Math.min(range.to, node.to),
3293
+ active: [
3294
+ false,
3295
+ false,
3296
+ false,
3297
+ false
3298
+ ]
3299
+ };
3300
+ }
3301
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3302
+ const index = InlineMarker[name];
3303
+ if (range.empty && inline[index] === null) {
3304
+ inline[index] = true;
3305
+ }
3306
+ currentBlock.active[index] = true;
3307
+ } else if (IgnoreInline.has(name)) {
3308
+ skipInline(node.to);
3408
3309
  }
3409
3310
  },
3410
3311
  leave: (node) => {
3312
+ advanceInline(node.to);
3411
3313
  const { name } = node;
3412
3314
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3413
3315
  stack.pop();
3414
- } else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
3415
- lastBlock = node.from;
3416
- let line = state2.doc.lineAt(node.from);
3417
- const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
3418
- if (!mark2) {
3419
- return false;
3420
- }
3421
- const column = node.from - line.from;
3422
- changes.push({
3423
- from: node.from,
3424
- to: node.from + mark2[0].length
3425
- });
3426
- while (line.to < node.to) {
3427
- line = state2.doc.lineAt(line.to + 1);
3428
- const open = /^[\s>]*/.exec(line.text)[0].length;
3429
- if (open > column) {
3430
- changes.push({
3431
- from: line.from + column,
3432
- to: line.from + Math.min(column + mark2[0].length, open)
3433
- });
3316
+ } else if (Object.hasOwn(Textblocks, name)) {
3317
+ let hasList = false;
3318
+ let hasQuote = false;
3319
+ for (let i = stack.length - 1; i >= 0; i--) {
3320
+ if (stack[i] === "Blockquote") {
3321
+ hasQuote = true;
3322
+ } else if (!hasList) {
3323
+ hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
3434
3324
  }
3435
3325
  }
3436
- if (node.to >= to) {
3437
- renumberListItems(node.node.nextSibling, 1, changes, state2.doc);
3326
+ if (blockQuote === null) {
3327
+ blockQuote = hasQuote;
3328
+ } else if (!hasQuote && blockQuote) {
3329
+ blockQuote = false;
3438
3330
  }
3439
- return false;
3331
+ if (listStyle === null) {
3332
+ listStyle = hasList;
3333
+ } else if (listStyle !== hasList) {
3334
+ listStyle = false;
3335
+ }
3336
+ currentBlock = null;
3337
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3338
+ currentBlock.active[InlineMarker[name]] = false;
3440
3339
  }
3441
3340
  }
3442
3341
  });
3443
3342
  }
3444
- if (!changes.length) {
3445
- return false;
3446
- }
3447
- dispatch(state2.update({
3448
- changes,
3449
- userEvent: "format.list.remove",
3450
- scrollIntoView: true
3451
- }));
3452
- return true;
3453
- };
3454
- var toggleList = (type) => (target) => {
3455
- const formatting = getFormatting(target.state);
3456
- const active = formatting.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
3457
- return (active ? removeList(type) : addList(type))(target);
3343
+ const { from, to } = state2.doc.lineAt(selection.main.anchor);
3344
+ const blankLine = from === to;
3345
+ return {
3346
+ blankLine,
3347
+ blockType: blockType || null,
3348
+ blockQuote: blockQuote ?? false,
3349
+ code: inline[3] ?? false,
3350
+ emphasis: inline[1] ?? false,
3351
+ strong: inline[0] ?? false,
3352
+ strikethrough: inline[2] ?? false,
3353
+ link,
3354
+ listStyle: listStyle || null
3355
+ };
3458
3356
  };
3459
- var renumberListItems = (item, counter, changes, doc) => {
3460
- for (; item; item = item.nextSibling) {
3461
- if (item.name === "ListItem") {
3462
- const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
3463
- if (!number || +number[2] === counter) {
3464
- break;
3465
- }
3466
- const size = number[1].length + number[2].length;
3467
- const newNum = String(counter);
3468
- changes.push({
3469
- from: item.from + Math.max(0, size - newNum.length),
3470
- to: item.from + size,
3471
- insert: newNum
3357
+ var useFormattingState = () => {
3358
+ const [state2, setState] = useState();
3359
+ const observer = useMemo(() => EditorView11.updateListener.of((update2) => {
3360
+ if (update2.docChanged || update2.selectionSet) {
3361
+ setState((prevState) => {
3362
+ const newState = getFormatting(update2.state);
3363
+ if (!prevState || !formattingEquals(prevState, newState)) {
3364
+ return newState;
3365
+ }
3366
+ return prevState;
3472
3367
  });
3473
- counter++;
3474
3368
  }
3475
- }
3369
+ }), [
3370
+ setState
3371
+ ]);
3372
+ return [
3373
+ state2,
3374
+ observer
3375
+ ];
3476
3376
  };
3477
- var setBlockquote = (enable) => ({ state: state2, dispatch }) => {
3478
- const lines = [];
3479
- let lastBlock = -1;
3480
- for (const { from, to } of state2.selection.ranges) {
3481
- const sawBlock = false;
3482
- syntaxTree2(state2).iterate({
3483
- from,
3484
- to,
3485
- enter: (node) => {
3486
- if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
3487
- if (node.from === lastBlock) {
3488
- return false;
3489
- }
3490
- lastBlock = node.from;
3491
- let line2 = state2.doc.lineAt(node.from);
3492
- if (line2.number > 1) {
3493
- const prevLine = state2.doc.line(line2.number - 1);
3494
- if (/^[>\s]*$/.test(prevLine.text)) {
3495
- if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
3496
- lines.push(prevLine);
3497
- }
3498
- }
3499
- }
3500
- for (; ; ) {
3501
- lines.push(line2);
3502
- if (line2.to >= node.to) {
3503
- break;
3504
- }
3505
- line2 = state2.doc.line(line2.number + 1);
3506
- }
3507
- if (!enable && line2.number < state2.doc.lines) {
3508
- const nextLine = state2.doc.line(line2.number + 1);
3509
- if (/^[>\s]*$/.test(nextLine.text)) {
3510
- lines.push(nextLine);
3511
- }
3512
- }
3513
- return false;
3514
- }
3515
- }
3516
- });
3517
- let line;
3518
- if (!sawBlock && enable && from === to && !/\S/.test((line = state2.doc.lineAt(from)).text)) {
3519
- lines.push(line);
3520
- }
3377
+
3378
+ // packages/ui/react-ui-editor/src/extensions/markdown/action.ts
3379
+ var processAction = (view, action) => {
3380
+ let inlineType, listType;
3381
+ switch (action.type) {
3382
+ case "heading":
3383
+ setHeading(parseInt(action.data))(view);
3384
+ break;
3385
+ case "strong":
3386
+ case "emphasis":
3387
+ case "strikethrough":
3388
+ case "code":
3389
+ inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
3390
+ (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
3391
+ break;
3392
+ case "list-ordered":
3393
+ case "list-bullet":
3394
+ case "list-task":
3395
+ listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
3396
+ (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
3397
+ break;
3398
+ case "blockquote":
3399
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
3400
+ break;
3401
+ case "codeblock":
3402
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
3403
+ break;
3404
+ case "table":
3405
+ insertTable(view);
3406
+ break;
3407
+ case "link":
3408
+ (action.data === false ? removeLink : addLink())(view);
3409
+ break;
3410
+ case "image":
3411
+ addLink({
3412
+ url: action.data,
3413
+ image: true
3414
+ })(view);
3415
+ break;
3416
+ case "comment":
3417
+ createComment(view);
3418
+ break;
3521
3419
  }
3522
- const changes = [];
3523
- for (const line of lines) {
3524
- if (enable) {
3525
- changes.push({
3526
- from: line.from,
3527
- insert: /\S/.test(line.text) ? "> " : ">"
3528
- });
3529
- } else {
3530
- const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
3531
- if (quote) {
3532
- changes.push({
3533
- from: line.from + quote[1].length,
3534
- to: line.from + quote[0].length
3535
- });
3536
- }
3420
+ requestAnimationFrame(() => {
3421
+ if (!view.hasFocus) {
3422
+ view.focus();
3423
+ }
3424
+ });
3425
+ };
3426
+
3427
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3428
+ import { completionKeymap as completionKeymap2 } from "@codemirror/autocomplete";
3429
+ import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
3430
+ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
3431
+ import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
3432
+ import { languages } from "@codemirror/language-data";
3433
+ import { lintKeymap } from "@codemirror/lint";
3434
+ import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
3435
+ import { keymap as keymap7 } from "@codemirror/view";
3436
+
3437
+ // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
3438
+ import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
3439
+ import { HighlightStyle } from "@codemirror/language";
3440
+ import { tags, styleTags, Tag } from "@lezer/highlight";
3441
+ import { Table } from "@lezer/markdown";
3442
+ var markdownTags = {
3443
+ Blockquote: Tag.define(),
3444
+ CodeMark: Tag.define(),
3445
+ CodeText: Tag.define(),
3446
+ EmphasisMark: Tag.define(),
3447
+ HeaderMark: Tag.define(),
3448
+ InlineCode: Tag.define(),
3449
+ LinkLabel: Tag.define(),
3450
+ LinkReference: Tag.define(),
3451
+ ListMark: Tag.define(),
3452
+ QuoteMark: Tag.define(),
3453
+ URL: Tag.define(),
3454
+ // Custom.
3455
+ TableCell: Tag.define()
3456
+ };
3457
+ Table.defineNodes?.forEach((node) => {
3458
+ switch (node?.name) {
3459
+ case "TableCell": {
3460
+ node.style = markdownTags.TableCell;
3461
+ break;
3537
3462
  }
3538
3463
  }
3539
- if (!changes.length) {
3540
- return false;
3464
+ });
3465
+ var markdownTagsExtensions = [
3466
+ Table,
3467
+ {
3468
+ props: [
3469
+ styleTags(markdownTags)
3470
+ ]
3541
3471
  }
3542
- const changeSet = state2.changes(changes);
3543
- dispatch(state2.update({
3544
- changes: changeSet,
3545
- selection: state2.selection.map(changeSet, 1),
3546
- userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
3547
- scrollIntoView: true
3548
- }));
3549
- return true;
3472
+ ];
3473
+ var markdownHighlightStyle = (readonly) => {
3474
+ return HighlightStyle.define([
3475
+ {
3476
+ tag: [
3477
+ tags.keyword,
3478
+ tags.name,
3479
+ tags.deleted,
3480
+ tags.character,
3481
+ tags.propertyName,
3482
+ tags.macroName,
3483
+ tags.color,
3484
+ tags.constant(tags.name),
3485
+ tags.standard(tags.name),
3486
+ tags.definition(tags.name),
3487
+ tags.separator,
3488
+ tags.typeName,
3489
+ tags.className,
3490
+ tags.number,
3491
+ tags.changed,
3492
+ tags.annotation,
3493
+ tags.modifier,
3494
+ tags.self,
3495
+ tags.namespace,
3496
+ tags.operator,
3497
+ tags.operatorKeyword,
3498
+ tags.escape,
3499
+ tags.regexp,
3500
+ tags.special(tags.string),
3501
+ tags.meta,
3502
+ tags.comment,
3503
+ tags.atom,
3504
+ tags.bool,
3505
+ tags.special(tags.variableName),
3506
+ tags.processingInstruction,
3507
+ tags.string,
3508
+ tags.inserted,
3509
+ tags.invalid
3510
+ ],
3511
+ color: "inherit !important"
3512
+ },
3513
+ // Markdown marks.
3514
+ {
3515
+ tag: [
3516
+ tags.meta,
3517
+ tags.processingInstruction,
3518
+ markdownTags.LinkLabel,
3519
+ markdownTags.LinkReference,
3520
+ markdownTags.ListMark
3521
+ ],
3522
+ class: mark
3523
+ },
3524
+ // Markdown marks.
3525
+ {
3526
+ tag: [
3527
+ markdownTags.CodeMark,
3528
+ markdownTags.HeaderMark,
3529
+ markdownTags.QuoteMark,
3530
+ markdownTags.EmphasisMark
3531
+ ],
3532
+ class: mark
3533
+ },
3534
+ // E.g., code block language (after ```).
3535
+ {
3536
+ tag: [
3537
+ tags.function(tags.variableName),
3538
+ tags.labelName
3539
+ ],
3540
+ class: codeMark
3541
+ },
3542
+ {
3543
+ tag: [
3544
+ tags.monospace
3545
+ ],
3546
+ class: "font-mono"
3547
+ },
3548
+ // Headings.
3549
+ {
3550
+ tag: tags.heading1,
3551
+ class: heading(1)
3552
+ },
3553
+ {
3554
+ tag: tags.heading2,
3555
+ class: heading(2)
3556
+ },
3557
+ {
3558
+ tag: tags.heading3,
3559
+ class: heading(3)
3560
+ },
3561
+ {
3562
+ tag: tags.heading4,
3563
+ class: heading(4)
3564
+ },
3565
+ {
3566
+ tag: tags.heading5,
3567
+ class: heading(5)
3568
+ },
3569
+ {
3570
+ tag: tags.heading6,
3571
+ class: heading(6)
3572
+ },
3573
+ // Emphasis.
3574
+ {
3575
+ tag: tags.emphasis,
3576
+ class: italic
3577
+ },
3578
+ {
3579
+ tag: tags.strong,
3580
+ class: bold
3581
+ },
3582
+ {
3583
+ tag: tags.strikethrough,
3584
+ class: strikethrough
3585
+ },
3586
+ // Naked URLs.
3587
+ {
3588
+ tag: [
3589
+ markdownTags.URL
3590
+ ],
3591
+ class: inlineUrl
3592
+ },
3593
+ // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
3594
+ // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
3595
+ // when a language is specified. In this case, the syntax highlighting extensions will colorize
3596
+ // the code, but all other CSS properties will be inherited.
3597
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
3598
+ {
3599
+ tag: [
3600
+ markdownTags.CodeText,
3601
+ markdownTags.InlineCode
3602
+ ],
3603
+ class: code
3604
+ },
3605
+ {
3606
+ tag: [
3607
+ markdownTags.QuoteMark
3608
+ ],
3609
+ class: blockquote
3610
+ },
3611
+ {
3612
+ tag: [
3613
+ markdownTags.TableCell
3614
+ ],
3615
+ class: "font-mono"
3616
+ }
3617
+ ], {
3618
+ scope: markdownLanguage2,
3619
+ all: {
3620
+ fontFamily: getToken("fontFamily.body", []).join(",")
3621
+ }
3622
+ });
3550
3623
  };
3551
- var addBlockquote = setBlockquote(true);
3552
- var removeBlockquote = setBlockquote(false);
3553
- var toggleBlockquote = (target) => {
3554
- return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
3624
+
3625
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3626
+ var createMarkdownExtensions = ({ themeMode } = {}) => {
3627
+ return [
3628
+ // Main extension.
3629
+ // https://github.com/codemirror/lang-markdown
3630
+ // https://codemirror.net/5/mode/markdown/index.html (demo).
3631
+ markdown({
3632
+ // GRM by default (vs strict CommonMark):
3633
+ // Table, TaskList, Strikethrough, and Autolink.
3634
+ // NOTE: This extends the parser; it doesn't affect rendering.
3635
+ // https://github.github.com/gfm
3636
+ // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
3637
+ base: markdownLanguage3,
3638
+ // Languages for syntax highlighting fenced code blocks.
3639
+ codeLanguages: languages,
3640
+ // Parser extensions.
3641
+ extensions: [
3642
+ // GFM provided by default.
3643
+ markdownTagsExtensions
3644
+ ]
3645
+ }),
3646
+ // https://github.com/codemirror/theme-one-dark
3647
+ themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
3648
+ // Custom styles.
3649
+ syntaxHighlighting(markdownHighlightStyle()),
3650
+ keymap7.of([
3651
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
3652
+ indentWithTab2,
3653
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
3654
+ ...defaultKeymap2,
3655
+ ...completionKeymap2,
3656
+ ...lintKeymap
3657
+ ])
3658
+ ];
3555
3659
  };
3556
- var addCodeblock = (target) => {
3557
- const { state: state2, dispatch } = target;
3558
- const { selection } = state2;
3559
- if (selection.ranges.length === 1 && selection.main.empty) {
3560
- const { head } = selection.main;
3561
- const line = state2.doc.lineAt(head);
3562
- if (!/\S/.test(line.text) && head === line.from) {
3563
- snippets.codeblock(target, null, line.from, line.to);
3564
- return true;
3565
- }
3660
+
3661
+ // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
3662
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3663
+ import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
3664
+ import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
3665
+ import { mx as mx2 } from "@dxos/react-ui-theme";
3666
+ var HorizontalRuleWidget = class extends WidgetType3 {
3667
+ toDOM() {
3668
+ const el = document.createElement("span");
3669
+ el.className = "cm-hr";
3670
+ return el;
3566
3671
  }
3567
- const ranges = [];
3568
- for (const { from, to } of selection.ranges) {
3569
- let blockFrom = from;
3570
- let blockTo = to;
3571
- syntaxTree2(state2).iterate({
3572
- from,
3573
- to,
3574
- enter: (node) => {
3575
- if (Object.hasOwn(Textblocks, node.name)) {
3576
- if (from >= node.from && to <= node.to) {
3577
- blockFrom = node.from;
3578
- blockTo = node.to;
3579
- } else {
3580
- blockFrom = Math.min(blockFrom, state2.doc.lineAt(node.from).from);
3581
- blockTo = Math.max(blockTo, state2.doc.lineAt(node.to).to);
3582
- }
3583
- }
3584
- }
3585
- });
3586
- if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
3587
- ranges[ranges.length - 1].to = blockTo;
3588
- } else {
3589
- ranges.push({
3590
- from: blockFrom,
3591
- to: blockTo
3592
- });
3593
- }
3672
+ };
3673
+ var LinkButton = class extends WidgetType3 {
3674
+ constructor(url, render) {
3675
+ super();
3676
+ this.url = url;
3677
+ this.render = render;
3594
3678
  }
3595
- if (!ranges.length) {
3596
- return false;
3679
+ eq(other) {
3680
+ return this.url === other.url;
3681
+ }
3682
+ toDOM(view) {
3683
+ const el = document.createElement("span");
3684
+ this.render(el, this.url);
3685
+ return el;
3597
3686
  }
3598
- const changes = ranges.map(({ from, to }) => {
3599
- const column = from - state2.doc.lineAt(from).from;
3600
- return [
3601
- {
3602
- from,
3603
- insert: "```\n" + " ".repeat(column)
3604
- },
3605
- {
3606
- from: to,
3607
- insert: "\n" + " ".repeat(column) + "```"
3608
- }
3609
- ];
3610
- });
3611
- dispatch(state2.update({
3612
- changes,
3613
- userEvent: "format.codeblock.add",
3614
- scrollIntoView: true
3615
- }));
3616
- return true;
3617
3687
  };
3618
- var removeCodeblock = ({ state: state2, dispatch }) => {
3619
- const changes = [];
3620
- let lastBlock = -1;
3621
- for (const { from, to } of state2.selection.ranges) {
3622
- syntaxTree2(state2).iterate({
3623
- from,
3624
- to,
3625
- enter: (node) => {
3626
- if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3627
- lastBlock = node.from;
3628
- const firstLine = state2.doc.lineAt(node.from);
3629
- if (node.name === "FencedCode") {
3630
- changes.push({
3631
- from: node.from,
3632
- to: firstLine.to + 1 + node.from - firstLine.from
3633
- });
3634
- const lastLine = state2.doc.lineAt(node.to);
3635
- if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3636
- changes.push({
3637
- from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
3638
- to: lastLine.to
3639
- });
3640
- }
3641
- } else {
3642
- const column = node.from - firstLine.from;
3643
- for (let line = firstLine; ; line = state2.doc.line(line.number + 1)) {
3644
- changes.push({
3645
- from: line.from + column - 4,
3646
- to: line.from + column
3647
- });
3648
- if (line.to >= node.to) {
3649
- break;
3650
- }
3688
+ var CheckboxWidget = class extends WidgetType3 {
3689
+ constructor(_checked) {
3690
+ super();
3691
+ this._checked = _checked;
3692
+ }
3693
+ eq(other) {
3694
+ return this._checked === other._checked;
3695
+ }
3696
+ toDOM(view) {
3697
+ const input = document.createElement("input");
3698
+ input.className = "cm-task-checkbox ch-checkbox ch-focus-ring -mbs-0.5";
3699
+ input.type = "checkbox";
3700
+ input.checked = this._checked;
3701
+ if (view.state.readOnly) {
3702
+ input.setAttribute("disabled", "true");
3703
+ } else {
3704
+ input.onmousedown = (event) => {
3705
+ const pos = view.posAtDOM(input);
3706
+ const text = view.state.sliceDoc(pos, pos + 3);
3707
+ if (text === (this._checked ? "[x]" : "[ ]")) {
3708
+ view.dispatch({
3709
+ changes: {
3710
+ from: pos + 1,
3711
+ to: pos + 2,
3712
+ insert: this._checked ? " " : "x"
3651
3713
  }
3652
- }
3714
+ });
3715
+ event.preventDefault();
3653
3716
  }
3654
- }
3655
- });
3717
+ };
3718
+ }
3719
+ return input;
3656
3720
  }
3657
- if (!changes.length) {
3721
+ ignoreEvent() {
3658
3722
  return false;
3659
3723
  }
3660
- dispatch(state2.update({
3661
- changes,
3662
- userEvent: "format.codeblock.remove",
3663
- scrollIntoView: true
3664
- }));
3665
- return true;
3666
- };
3667
- var toggleCodeblock = (target) => {
3668
- return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3669
- };
3670
- var formattingKeymap = (options = {}) => {
3671
- return [
3672
- keymap6.of([
3673
- {
3674
- key: "meta-b",
3675
- run: toggleStrong
3676
- }
3677
- ])
3678
- ];
3679
3724
  };
3680
- var InlineMarker = {
3681
- Emphasis: 1,
3682
- StrongEmphasis: 0,
3683
- InlineCode: 3,
3684
- Strikethrough: 2
3725
+ var hide = Decoration5.replace({});
3726
+ var fencedCodeLine = Decoration5.line({
3727
+ class: mx2("cm-code cm-codeblock-line")
3728
+ });
3729
+ var fencedCodeLineFirst = Decoration5.line({
3730
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
3731
+ });
3732
+ var fencedCodeLineLast = Decoration5.line({
3733
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
3734
+ });
3735
+ var horizontalRule = Decoration5.replace({
3736
+ widget: new HorizontalRuleWidget()
3737
+ });
3738
+ var checkedTask = Decoration5.replace({
3739
+ widget: new CheckboxWidget(true)
3740
+ });
3741
+ var uncheckedTask = Decoration5.replace({
3742
+ widget: new CheckboxWidget(false)
3743
+ });
3744
+ var editingRange = (state2, range, focus) => {
3745
+ const { readOnly, selection: { main: { head } } } = state2;
3746
+ return focus && !readOnly && head >= range.from && head <= range.to;
3685
3747
  };
3686
- var IgnoreInline = /* @__PURE__ */ new Set([
3687
- "Autolink",
3748
+ var MarksByParent = /* @__PURE__ */ new Set([
3688
3749
  "CodeMark",
3689
- "CodeText",
3690
- "Comment",
3691
3750
  "EmphasisMark",
3692
- "Hardbreak",
3693
- "HeaderMark",
3694
- "HTMLTag",
3695
- "LinkMark",
3696
- "ListMark",
3697
- "ProcessingInstruction",
3698
- "QuoteMark",
3699
3751
  "StrikethroughMark",
3700
3752
  "SubscriptMark",
3701
- "SuperscriptMark",
3702
- "TaskMarker"
3753
+ "SuperscriptMark"
3703
3754
  ]);
3704
- var Textblocks = {
3705
- ATXHeading1: "heading1",
3706
- ATXHeading2: "heading2",
3707
- ATXHeading3: "heading3",
3708
- ATXHeading4: "heading4",
3709
- ATXHeading5: "heading5",
3710
- ATXHeading6: "heading6",
3711
- CodeBlock: "codeblock",
3712
- FencedCode: "codeblock",
3713
- Paragraph: "paragraph",
3714
- SetextHeading1: "heading1",
3715
- SetextHeading2: "heading2",
3716
- TableCell: "tablecell",
3717
- Task: "paragraph"
3718
- };
3719
- var getFormatting = (state2) => {
3720
- let blockType = null;
3721
- const inline = [
3722
- null,
3723
- null,
3724
- null,
3725
- null
3726
- ];
3727
- let link = false;
3728
- let blockQuote = null;
3729
- let listStyle = null;
3730
- const stack = [];
3731
- let currentBlock = null;
3732
- const advanceInline = (upto) => {
3733
- if (!currentBlock) {
3734
- return;
3735
- }
3736
- upto = Math.min(upto, currentBlock.end);
3737
- if (upto <= currentBlock.pos) {
3738
- return;
3739
- }
3740
- for (let i = 0; i < currentBlock.active.length; i++) {
3741
- if (inline[i] === false) {
3742
- continue;
3743
- } else if (currentBlock.active[i]) {
3744
- inline[i] = true;
3745
- } else if (/\S/.test(state2.doc.sliceString(currentBlock.pos, upto))) {
3746
- inline[i] = false;
3747
- }
3748
- }
3749
- currentBlock.pos = upto;
3750
- };
3751
- const skipInline = (upto) => {
3752
- if (currentBlock && upto > currentBlock.pos) {
3753
- currentBlock.pos = Math.min(upto, currentBlock.end);
3754
- }
3755
- };
3756
- const { selection } = state2;
3757
- for (const range of selection.ranges) {
3758
- if (range.empty && inline.some((v) => v === null)) {
3759
- const contextSize = Math.min(range.head, 6);
3760
- const contextBefore = state2.doc.sliceString(range.head - contextSize, range.head);
3761
- let contextAfter = state2.doc.sliceString(range.head, range.head + contextSize);
3762
- for (let i = 0; i < contextSize; i++) {
3763
- const ch = contextAfter[i];
3764
- if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
3765
- contextAfter = contextAfter.slice(0, i);
3766
- break;
3767
- }
3768
- }
3769
- for (let i = 0; i < inline.length; i++) {
3770
- const mark2 = inlineMarkerText(i);
3771
- const found = contextAfter.indexOf(mark2);
3772
- if (found > -1) {
3773
- contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3774
- if (inline[i] === null) {
3775
- inline[i] = true;
3776
- }
3777
- }
3778
- }
3779
- }
3780
- syntaxTree2(state2).iterate({
3781
- from: range.from,
3782
- to: range.to,
3783
- enter: (node) => {
3784
- advanceInline(node.from);
3785
- const { name } = node;
3786
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3787
- stack.push(name);
3788
- } else if (name === "Link") {
3789
- link = true;
3790
- } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3791
- if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3792
- stack[stack.length - 1] = "TaskList";
3793
- }
3794
- const blockCode = Textblocks[name];
3795
- if (blockType === null) {
3796
- blockType = blockCode;
3797
- } else if (blockType !== blockCode) {
3798
- blockType = false;
3755
+ var buildDecorations = (view, options, focus) => {
3756
+ const deco = new RangeSetBuilder2();
3757
+ const atomicDeco = new RangeSetBuilder2();
3758
+ const { state: state2 } = view;
3759
+ for (const { from, to } of view.visibleRanges) {
3760
+ syntaxTree2(state2).iterate({
3761
+ from,
3762
+ to,
3763
+ enter: (node) => {
3764
+ if (node.name === "FencedCode") {
3765
+ const editing = editingRange(state2, node, focus);
3766
+ for (const block of view.viewportLineBlocks) {
3767
+ if (block.to < node.from) {
3768
+ continue;
3769
+ }
3770
+ if (block.from > node.to) {
3771
+ break;
3772
+ }
3773
+ const first = block.from <= node.from;
3774
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state2.doc.sliceString(block.from, block.to));
3775
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
3776
+ if (!editing && (first || last)) {
3777
+ atomicDeco.add(block.from, block.to, hide);
3778
+ }
3799
3779
  }
3800
- if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
3801
- currentBlock = {
3802
- pos: Math.max(range.from, node.from),
3803
- end: Math.min(range.to, node.to),
3804
- active: [
3805
- false,
3806
- false,
3807
- false,
3808
- false
3809
- ]
3810
- };
3780
+ return false;
3781
+ } else if (node.name === "Link") {
3782
+ const marks = node.node.getChildren("LinkMark");
3783
+ const urlNode = node.node.getChild("URL");
3784
+ const editing = editingRange(state2, node, focus);
3785
+ if (urlNode && marks.length >= 2) {
3786
+ const url = state2.sliceDoc(urlNode.from, urlNode.to);
3787
+ if (!editing) {
3788
+ atomicDeco.add(node.from, marks[0].to, hide);
3789
+ }
3790
+ deco.add(marks[0].to, marks[1].from, Decoration5.mark({
3791
+ tagName: "a",
3792
+ attributes: {
3793
+ class: "cm-link",
3794
+ href: url,
3795
+ rel: "noreferrer",
3796
+ target: "_blank"
3797
+ }
3798
+ }));
3799
+ if (!editing) {
3800
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
3801
+ widget: new LinkButton(url, options.renderLinkButton)
3802
+ }) : hide);
3803
+ }
3811
3804
  }
3812
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3813
- const index = InlineMarker[name];
3814
- if (range.empty && inline[index] === null) {
3815
- inline[index] = true;
3805
+ } else if (node.name === "HeaderMark") {
3806
+ const parent = node.node.parent;
3807
+ if (/^ATX/.test(parent.name) && !editingRange(state2, state2.doc.lineAt(node.from), focus)) {
3808
+ const next = state2.doc.sliceString(node.to, node.to + 1);
3809
+ atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
3816
3810
  }
3817
- currentBlock.active[index] = true;
3818
- } else if (IgnoreInline.has(name)) {
3819
- skipInline(node.to);
3820
- }
3821
- },
3822
- leave: (node) => {
3823
- advanceInline(node.to);
3824
- const { name } = node;
3825
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3826
- stack.pop();
3827
- } else if (Object.hasOwn(Textblocks, name)) {
3828
- let hasList = false;
3829
- let hasQuote = false;
3830
- for (let i = stack.length - 1; i >= 0; i--) {
3831
- if (stack[i] === "Blockquote") {
3832
- hasQuote = true;
3833
- } else if (!hasList) {
3834
- hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
3835
- }
3811
+ } else if (node.name === "HorizontalRule") {
3812
+ if (!editingRange(state2, node, focus)) {
3813
+ deco.add(node.from, node.to, horizontalRule);
3836
3814
  }
3837
- if (blockQuote === null) {
3838
- blockQuote = hasQuote;
3839
- } else if (!hasQuote && blockQuote) {
3840
- blockQuote = false;
3815
+ } else if (node.name === "TaskMarker") {
3816
+ if (!editingRange(state2, node, focus)) {
3817
+ const checked = state2.doc.sliceString(node.from + 1, node.to - 1) === "x";
3818
+ atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
3819
+ class: "cm-task"
3820
+ }));
3821
+ atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
3841
3822
  }
3842
- if (listStyle === null) {
3843
- listStyle = hasList;
3844
- } else if (listStyle !== hasList) {
3845
- listStyle = false;
3823
+ } else if (MarksByParent.has(node.name)) {
3824
+ if (!editingRange(state2, node.node.parent, focus)) {
3825
+ atomicDeco.add(node.from, node.to, hide);
3846
3826
  }
3847
- currentBlock = null;
3848
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3849
- currentBlock.active[InlineMarker[name]] = false;
3850
3827
  }
3851
3828
  }
3852
3829
  });
3853
3830
  }
3854
- const { from, to } = state2.doc.lineAt(selection.main.anchor);
3855
- const blankLine = from === to;
3856
3831
  return {
3857
- blankLine,
3858
- blockType: blockType || null,
3859
- blockQuote: blockQuote ?? false,
3860
- code: inline[3] ?? false,
3861
- emphasis: inline[1] ?? false,
3862
- strong: inline[0] ?? false,
3863
- strikethrough: inline[2] ?? false,
3864
- link,
3865
- listStyle: listStyle || null
3832
+ deco: deco.finish(),
3833
+ atomicDeco: atomicDeco.finish()
3866
3834
  };
3867
3835
  };
3868
- var useFormattingState = () => {
3869
- const [state2, setState, stateRef] = useStateRef();
3870
- const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3871
- if (update2.docChanged || update2.selectionSet) {
3872
- const newState = getFormatting(update2.state);
3873
- if (!stateRef.current || !formattingEquals(stateRef.current, newState)) {
3874
- setState(newState);
3875
- }
3876
- }
3877
- }), []);
3836
+ var forceUpdate = StateEffect4.define();
3837
+ var decorateMarkdown = (options = {}) => {
3878
3838
  return [
3879
- state2,
3880
- observer
3839
+ ViewPlugin4.fromClass(class {
3840
+ constructor(view) {
3841
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
3842
+ }
3843
+ update(update2) {
3844
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
3845
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
3846
+ this.clearUpdate();
3847
+ } else if (update2.selectionSet) {
3848
+ this.scheduleUpdate(update2.view);
3849
+ }
3850
+ }
3851
+ // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
3852
+ // the cursor will float in space or be in the wrong position after the decoration is applied.
3853
+ scheduleUpdate(view) {
3854
+ this.clearUpdate();
3855
+ this.pendingUpdate = setTimeout(() => {
3856
+ view.dispatch({
3857
+ effects: forceUpdate.of(null)
3858
+ });
3859
+ }, options.selectionChangeDelay);
3860
+ }
3861
+ clearUpdate() {
3862
+ if (this.pendingUpdate) {
3863
+ clearTimeout(this.pendingUpdate);
3864
+ this.pendingUpdate = void 0;
3865
+ }
3866
+ }
3867
+ destroy() {
3868
+ this.clearUpdate();
3869
+ }
3870
+ }, {
3871
+ provide: (plugin) => [
3872
+ EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
3873
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
3874
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
3875
+ ]
3876
+ }),
3877
+ formattingStyles
3881
3878
  ];
3882
3879
  };
3880
+ var formattingStyles = EditorView12.baseTheme({
3881
+ "& .cm-code": {
3882
+ fontFamily: getToken("fontFamily.mono", []).join(",")
3883
+ },
3884
+ "& .cm-codeblock-line": {
3885
+ paddingInline: "1rem !important"
3886
+ },
3887
+ "& .cm-codeblock-end": {
3888
+ display: "inline-block",
3889
+ width: "100%",
3890
+ position: "relative",
3891
+ "&::after": {
3892
+ position: "absolute",
3893
+ inset: 0,
3894
+ content: '""'
3895
+ }
3896
+ },
3897
+ "& .cm-codeblock-first": {
3898
+ borderTopLeftRadius: ".5rem",
3899
+ borderTopRightRadius: ".5rem"
3900
+ },
3901
+ "& .cm-codeblock-last": {
3902
+ borderBottomLeftRadius: ".5rem",
3903
+ borderBottomRightRadius: ".5rem"
3904
+ },
3905
+ "&light .cm-codeblock-line, &light .cm-activeLine.cm-codeblock-line": {
3906
+ background: getToken("extend.semanticColors.input.light"),
3907
+ mixBlendMode: "darken"
3908
+ },
3909
+ "&dark .cm-codeblock-line, &dark .cm-activeLine.cm-codeblock-line": {
3910
+ background: getToken("extend.semanticColors.input.dark"),
3911
+ mixBlendMode: "lighten"
3912
+ },
3913
+ "& .cm-hr": {
3914
+ display: "inline-block",
3915
+ width: "100%",
3916
+ height: "0",
3917
+ verticalAlign: "middle",
3918
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
3919
+ },
3920
+ "& .cm-task": {
3921
+ color: getToken("extend.colors.blue.500")
3922
+ },
3923
+ "& .cm-task-checkbox": {
3924
+ marginLeft: "4px",
3925
+ marginRight: "4px"
3926
+ }
3927
+ });
3883
3928
 
3884
3929
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3885
3930
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3886
3931
  import { StateField as StateField5 } from "@codemirror/state";
3887
- import { Decoration as Decoration6, EditorView as EditorView14, WidgetType as WidgetType4 } from "@codemirror/view";
3932
+ import { Decoration as Decoration6, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
3888
3933
  var image = (options = {}) => {
3889
3934
  return StateField5.define({
3890
3935
  create: (state2) => {
@@ -3911,7 +3956,7 @@ var image = (options = {}) => {
3911
3956
  add: buildDecorations2(from, to, tr.state)
3912
3957
  });
3913
3958
  },
3914
- provide: (field) => EditorView14.decorations.from(field)
3959
+ provide: (field) => EditorView13.decorations.from(field)
3915
3960
  });
3916
3961
  };
3917
3962
  var preloaded = /* @__PURE__ */ new Set();
@@ -4001,12 +4046,12 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
4001
4046
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4002
4047
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4003
4048
  import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
4004
- import { Decoration as Decoration7, EditorView as EditorView15, WidgetType as WidgetType5 } from "@codemirror/view";
4049
+ import { Decoration as Decoration7, EditorView as EditorView14, WidgetType as WidgetType5 } from "@codemirror/view";
4005
4050
  var table = (options = {}) => {
4006
4051
  return StateField6.define({
4007
4052
  create: (state2) => update(state2, options),
4008
4053
  update: (_, tr) => update(tr.state, options),
4009
- provide: (field) => EditorView15.decorations.from(field)
4054
+ provide: (field) => EditorView14.decorations.from(field)
4010
4055
  });
4011
4056
  };
4012
4057
  var update = (state2, options) => {
@@ -4100,8 +4145,8 @@ var TableWidget = class extends WidgetType5 {
4100
4145
 
4101
4146
  // packages/ui/react-ui-editor/src/extensions/mention.ts
4102
4147
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
4103
- import { log as log8 } from "@dxos/log";
4104
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4148
+ import { log as log6 } from "@dxos/log";
4149
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4105
4150
  var mention = ({ onSearch }) => {
4106
4151
  return autocompletion2({
4107
4152
  // TODO(burdon): Not working.
@@ -4113,10 +4158,10 @@ var mention = ({ onSearch }) => {
4113
4158
  icons: false,
4114
4159
  override: [
4115
4160
  (context) => {
4116
- log8.info("completion context", {
4161
+ log6.info("completion context", {
4117
4162
  context
4118
4163
  }, {
4119
- F: __dxlog_file10,
4164
+ F: __dxlog_file8,
4120
4165
  L: 26,
4121
4166
  S: void 0,
4122
4167
  C: (f, a) => f(...a)
@@ -4138,21 +4183,30 @@ var mention = ({ onSearch }) => {
4138
4183
 
4139
4184
  // packages/ui/react-ui-editor/src/extensions/modes.ts
4140
4185
  import { Facet as Facet6 } from "@codemirror/state";
4141
- import { keymap as keymap7 } from "@codemirror/view";
4186
+ import { keymap as keymap8 } from "@codemirror/view";
4142
4187
  import { vim } from "@replit/codemirror-vim";
4188
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
4143
4189
  var focusEvent = "focus.container";
4144
4190
  var editorMode = Facet6.define({
4145
4191
  combine: (modes) => modes[0] ?? {}
4146
4192
  });
4147
4193
  var EditorModes = {
4148
4194
  default: [],
4195
+ vscode: [
4196
+ // https://github.com/replit/codemirror-vscode-keymap
4197
+ editorMode.of({
4198
+ type: "vscode"
4199
+ }),
4200
+ keymap8.of(vscodeKeymap)
4201
+ ],
4149
4202
  vim: [
4203
+ // https://github.com/replit/codemirror-vim
4150
4204
  vim(),
4151
4205
  editorMode.of({
4152
4206
  type: "vim",
4153
4207
  noTabster: true
4154
4208
  }),
4155
- keymap7.of([
4209
+ keymap8.of([
4156
4210
  {
4157
4211
  key: "Alt-Escape",
4158
4212
  run: (view) => {
@@ -4167,18 +4221,18 @@ var EditorModes = {
4167
4221
  };
4168
4222
 
4169
4223
  // packages/ui/react-ui-editor/src/extensions/state.ts
4170
- import { Transaction as Transaction2 } from "@codemirror/state";
4171
- import { EditorView as EditorView16, keymap as keymap8 } from "@codemirror/view";
4224
+ import { Transaction } from "@codemirror/state";
4225
+ import { EditorView as EditorView15, keymap as keymap9 } from "@codemirror/view";
4172
4226
  import { debounce as debounce2 } from "@dxos/async";
4173
4227
  import { invariant as invariant4 } from "@dxos/invariant";
4174
4228
  import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
4175
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4229
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4176
4230
  var scrollAnnotation = "dxos.org/cm/scrolling";
4177
4231
  var keyPrefix = "dxos.org/react-ui-editor/state";
4178
4232
  var localStorageStateStoreAdapter = {
4179
4233
  setState: (id, state2) => {
4180
4234
  invariant4(id, void 0, {
4181
- F: __dxlog_file11,
4235
+ F: __dxlog_file9,
4182
4236
  L: 35,
4183
4237
  S: void 0,
4184
4238
  A: [
@@ -4190,7 +4244,7 @@ var localStorageStateStoreAdapter = {
4190
4244
  },
4191
4245
  getState: (id) => {
4192
4246
  invariant4(id, void 0, {
4193
- F: __dxlog_file11,
4247
+ F: __dxlog_file9,
4194
4248
  L: 39,
4195
4249
  S: void 0,
4196
4250
  A: [
@@ -4206,7 +4260,7 @@ var state = ({ getState, setState } = {}) => {
4206
4260
  const setStateDebounced = debounce2(setState, 1e3);
4207
4261
  return [
4208
4262
  // TODO(burdon): Track scrolling (currently only updates when cursor moves).
4209
- EditorView16.updateListener.of(({ view, changes, transactions }) => {
4263
+ EditorView15.updateListener.of(({ view, changes, transactions }) => {
4210
4264
  const id = view.state.facet(documentId2);
4211
4265
  if (!id || transactions.some((tr) => tr.isUserEvent(scrollAnnotation))) {
4212
4266
  return;
@@ -4232,18 +4286,18 @@ var state = ({ getState, setState } = {}) => {
4232
4286
  }
4233
4287
  }
4234
4288
  }),
4235
- getState && keymap8.of([
4289
+ getState && keymap9.of([
4236
4290
  {
4237
4291
  key: "ctrl-r",
4238
4292
  run: (view) => {
4239
4293
  const state2 = getState(view.state.facet(documentId2));
4240
4294
  if (state2) {
4241
4295
  view.dispatch({
4242
- effects: EditorView16.scrollIntoView(state2.scrollTo.from, {
4296
+ effects: EditorView15.scrollIntoView(state2.scrollTo.from, {
4243
4297
  yMargin: 0
4244
4298
  }),
4245
4299
  selection: state2.selection,
4246
- annotations: Transaction2.userEvent.of(scrollAnnotation)
4300
+ annotations: Transaction.userEvent.of(scrollAnnotation)
4247
4301
  });
4248
4302
  }
4249
4303
  return true;
@@ -4254,7 +4308,7 @@ var state = ({ getState, setState } = {}) => {
4254
4308
  };
4255
4309
 
4256
4310
  // packages/ui/react-ui-editor/src/extensions/typewriter.ts
4257
- import { keymap as keymap9 } from "@codemirror/view";
4311
+ import { keymap as keymap10 } from "@codemirror/view";
4258
4312
  var defaultItems = [
4259
4313
  "hello world!",
4260
4314
  "this is a test.",
@@ -4264,7 +4318,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4264
4318
  let t;
4265
4319
  let idx = 0;
4266
4320
  return [
4267
- keymap9.of([
4321
+ keymap10.of([
4268
4322
  {
4269
4323
  // Reset.
4270
4324
  key: "alt-meta-'",
@@ -4311,17 +4365,17 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4311
4365
  };
4312
4366
 
4313
4367
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4314
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4368
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4315
4369
  var instanceCount = 0;
4316
- var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView17.scrollIntoView(0, {
4370
+ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView16.scrollIntoView(0, {
4317
4371
  yMargin: 0
4318
4372
  }), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4319
- const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
4373
+ const [instanceId] = useState2(() => `text-editor-${++instanceCount}`);
4320
4374
  const tabsterDOMAttribute = useFocusableGroup({
4321
4375
  tabBehavior: "limited"
4322
4376
  });
4323
4377
  const rootRef = useRef(null);
4324
- const [view, setView] = useState(null);
4378
+ const [view, setView] = useState2(null);
4325
4379
  useImperativeHandle(forwardedRef, () => view, [
4326
4380
  view
4327
4381
  ]);
@@ -4334,11 +4388,11 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4334
4388
  autoFocus
4335
4389
  ]);
4336
4390
  useEffect2(() => {
4337
- log9("create", {
4391
+ log7("create", {
4338
4392
  id,
4339
4393
  instanceId
4340
4394
  }, {
4341
- F: __dxlog_file12,
4395
+ F: __dxlog_file10,
4342
4396
  L: 88,
4343
4397
  S: void 0,
4344
4398
  C: (f, a) => f(...a)
@@ -4349,16 +4403,16 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4349
4403
  extensions: [
4350
4404
  id && documentId2.of(id),
4351
4405
  // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4352
- EditorView17.exceptionSink.of((err) => {
4353
- log9.catch(err, void 0, {
4354
- F: __dxlog_file12,
4406
+ EditorView16.exceptionSink.of((err) => {
4407
+ log7.catch(err, void 0, {
4408
+ F: __dxlog_file10,
4355
4409
  L: 101,
4356
4410
  S: void 0,
4357
4411
  C: (f, a) => f(...a)
4358
4412
  });
4359
4413
  }),
4360
4414
  // Focus.
4361
- EditorView17.updateListener.of((update2) => {
4415
+ EditorView16.updateListener.of((update2) => {
4362
4416
  update2.transactions.forEach((transaction) => {
4363
4417
  if (transaction.isUserEvent(focusEvent)) {
4364
4418
  rootRef.current?.focus();
@@ -4368,7 +4422,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4368
4422
  extensions
4369
4423
  ].filter(isNotFalsy4)
4370
4424
  });
4371
- const view2 = new EditorView17({
4425
+ const view2 = new EditorView16({
4372
4426
  state: state2,
4373
4427
  parent: rootRef.current,
4374
4428
  scrollTo,
@@ -4394,11 +4448,11 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4394
4448
  }
4395
4449
  setView(view2);
4396
4450
  return () => {
4397
- log9("destroy", {
4451
+ log7("destroy", {
4398
4452
  id,
4399
4453
  instanceId
4400
4454
  }, {
4401
- F: __dxlog_file12,
4455
+ F: __dxlog_file10,
4402
4456
  L: 150,
4403
4457
  S: void 0,
4404
4458
  C: (f, a) => f(...a)
@@ -4433,11 +4487,11 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4433
4487
  });
4434
4488
 
4435
4489
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
4436
- import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic } from "@phosphor-icons/react";
4490
+ import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic, CaretDown, Check } from "@phosphor-icons/react";
4437
4491
  import { createContext } from "@radix-ui/react-context";
4438
- import React2, { useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
4492
+ import React2, { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
4439
4493
  import { useDropzone } from "react-dropzone";
4440
- import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
4494
+ import { DensityProvider, ElevationProvider, Toolbar as NaturalToolbar, Tooltip, useTranslation, DropdownMenu, Button } from "@dxos/react-ui";
4441
4495
  import { getSize } from "@dxos/react-ui-theme";
4442
4496
  var tooltipProps = {
4443
4497
  side: "top",
@@ -4508,8 +4562,8 @@ var MarkdownHeading = () => {
4508
4562
  const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
4509
4563
  const HeadingIcon = HeadingIcons[value ?? "0"];
4510
4564
  const suppressNextTooltip = useRef2(false);
4511
- const [tooltipOpen, setTooltipOpen] = useState2(false);
4512
- const [selectOpen, setSelectOpen] = useState2(false);
4565
+ const [tooltipOpen, setTooltipOpen] = useState3(false);
4566
+ const [selectOpen, setSelectOpen] = useState3(false);
4513
4567
  return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
4514
4568
  open: tooltipOpen,
4515
4569
  onOpenChange: (nextOpen) => {
@@ -4520,58 +4574,48 @@ var MarkdownHeading = () => {
4520
4574
  return setTooltipOpen(nextOpen);
4521
4575
  }
4522
4576
  }
4523
- }, /* @__PURE__ */ React2.createElement(Select.Root, {
4524
- disabled: value === null,
4525
- value: value ?? "0",
4577
+ }, /* @__PURE__ */ React2.createElement(DropdownMenu.Root, {
4526
4578
  open: selectOpen,
4527
4579
  onOpenChange: (nextOpen) => {
4528
4580
  if (!nextOpen) {
4529
4581
  suppressNextTooltip.current = true;
4530
4582
  }
4531
4583
  return setSelectOpen(nextOpen);
4532
- },
4533
- onValueChange: (value2) => onAction?.({
4534
- type: "heading",
4535
- data: parseInt(value2)
4536
- })
4584
+ }
4537
4585
  }, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
4538
4586
  asChild: true
4539
4587
  }, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
4540
4588
  asChild: true
4541
- }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
4589
+ }, /* @__PURE__ */ React2.createElement(DropdownMenu.Trigger, {
4590
+ asChild: true
4591
+ }, /* @__PURE__ */ React2.createElement(Button, {
4542
4592
  variant: "ghost",
4543
- classNames: buttonStyles
4593
+ classNames: buttonStyles,
4594
+ disabled: value === null
4544
4595
  }, /* @__PURE__ */ React2.createElement("span", {
4545
4596
  className: "sr-only"
4546
4597
  }, t("heading label")), /* @__PURE__ */ React2.createElement(HeadingIcon, {
4547
4598
  className: iconStyles
4548
- })))), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.ScrollUpButton, null), /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
4549
- value: "0"
4550
- }, /* @__PURE__ */ React2.createElement(Paragraph, {
4551
- className: iconStyles
4552
- })), [
4553
- 1,
4554
- 2,
4555
- 3,
4556
- 4,
4557
- 5,
4558
- 6
4559
- ].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
4560
- key: level,
4561
- value: String(level)
4562
- }, level === 1 && /* @__PURE__ */ React2.createElement(TextHOne, {
4563
- className: iconStyles
4564
- }), level === 2 && /* @__PURE__ */ React2.createElement(TextHTwo, {
4565
- className: iconStyles
4566
- }), level === 3 && /* @__PURE__ */ React2.createElement(TextHThree, {
4567
- className: iconStyles
4568
- }), level === 4 && /* @__PURE__ */ React2.createElement(TextHFour, {
4569
- className: iconStyles
4570
- }), level === 5 && /* @__PURE__ */ React2.createElement(TextHFive, {
4571
- className: iconStyles
4572
- }), level === 6 && /* @__PURE__ */ React2.createElement(TextHSix, {
4573
- className: iconStyles
4574
- })))), /* @__PURE__ */ React2.createElement(Select.ScrollDownButton, null), /* @__PURE__ */ React2.createElement(Select.Arrow, null)))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, t("heading label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4599
+ }), /* @__PURE__ */ React2.createElement(CaretDown, null))))), /* @__PURE__ */ React2.createElement(DropdownMenu.Portal, null, /* @__PURE__ */ React2.createElement(DropdownMenu.Content, {
4600
+ classNames: "is-min md:is-min",
4601
+ onCloseAutoFocus: (e) => e.preventDefault()
4602
+ }, /* @__PURE__ */ React2.createElement(DropdownMenu.Viewport, null, Object.keys(HeadingIcons).map((level) => {
4603
+ const Icon = HeadingIcons[level];
4604
+ return /* @__PURE__ */ React2.createElement(DropdownMenu.CheckboxItem, {
4605
+ key: level,
4606
+ checked: value === level,
4607
+ onClick: () => onAction?.({
4608
+ type: "heading",
4609
+ data: level
4610
+ })
4611
+ }, /* @__PURE__ */ React2.createElement("span", {
4612
+ className: "sr-only"
4613
+ }, t("heading level label", {
4614
+ count: parseInt(level)
4615
+ })), /* @__PURE__ */ React2.createElement(Icon, {
4616
+ className: iconStyles
4617
+ }), /* @__PURE__ */ React2.createElement(DropdownMenu.ItemIndicator, null, /* @__PURE__ */ React2.createElement(Check, null)));
4618
+ })), /* @__PURE__ */ React2.createElement(DropdownMenu.Arrow, null)))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, t("heading label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
4575
4619
  };
4576
4620
  var markdownStyles = [
4577
4621
  {
@@ -4704,7 +4748,7 @@ var MarkdownCustom = ({ onUpload } = {}) => {
4704
4748
  });
4705
4749
  useEffect3(() => {
4706
4750
  if (onUpload && acceptedFiles.length) {
4707
- setTimeout(async () => {
4751
+ requestAnimationFrame(async () => {
4708
4752
  const f = acceptedFiles[0];
4709
4753
  const file = new File([
4710
4754
  f
@@ -4736,6 +4780,7 @@ var MarkdownActions = () => {
4736
4780
  return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ToolbarButton, {
4737
4781
  value: "comment",
4738
4782
  Icon: ChatText,
4783
+ "data-testid": "editor.toolbar.comment",
4739
4784
  onClick: () => onAction?.({
4740
4785
  type: "comment"
4741
4786
  })
@@ -4752,56 +4797,7 @@ var Toolbar = {
4752
4797
 
4753
4798
  // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4754
4799
  var useActionHandler = (view) => {
4755
- return (action) => {
4756
- if (!view) {
4757
- return;
4758
- }
4759
- let inlineType, listType;
4760
- switch (action.type) {
4761
- case "heading":
4762
- setHeading(parseInt(action.data))(view);
4763
- break;
4764
- case "strong":
4765
- case "emphasis":
4766
- case "strikethrough":
4767
- case "code":
4768
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4769
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4770
- break;
4771
- case "list-ordered":
4772
- case "list-bullet":
4773
- case "list-task":
4774
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4775
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4776
- break;
4777
- case "blockquote":
4778
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4779
- break;
4780
- case "codeblock":
4781
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4782
- break;
4783
- case "table":
4784
- insertTable(view);
4785
- break;
4786
- case "link":
4787
- (action.data === false ? removeLink : addLink())(view);
4788
- break;
4789
- case "image":
4790
- addLink({
4791
- url: action.data,
4792
- image: true
4793
- })(view);
4794
- break;
4795
- case "comment":
4796
- createComment(view);
4797
- break;
4798
- }
4799
- setTimeout(() => {
4800
- if (!view.hasFocus) {
4801
- view.focus();
4802
- }
4803
- });
4804
- };
4800
+ return (action) => view && processAction(view, action);
4805
4801
  };
4806
4802
 
4807
4803
  // packages/ui/react-ui-editor/src/hooks/useDocAccessor.ts
@@ -4819,23 +4815,23 @@ var useDocAccessor = (text) => {
4819
4815
 
4820
4816
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4821
4817
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4822
- import { EditorView as EditorView18 } from "@codemirror/view";
4823
- import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
4824
- import { log as log10 } from "@dxos/log";
4818
+ import { EditorView as EditorView17 } from "@codemirror/view";
4819
+ import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4820
+ import { log as log8 } from "@dxos/log";
4825
4821
  import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
4826
- var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4822
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4827
4823
  var useTextEditor = (cb = () => ({}), deps = []) => {
4828
4824
  let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
4829
4825
  const onUpdate = useRef3();
4830
- const [view, setView] = useState3();
4826
+ const [view, setView] = useState4();
4831
4827
  const parentRef = useRef3(null);
4832
4828
  useEffect4(() => {
4833
4829
  let view2;
4834
4830
  if (parentRef.current) {
4835
- log10("create", {
4831
+ log8("create", {
4836
4832
  id
4837
4833
  }, {
4838
- F: __dxlog_file13,
4834
+ F: __dxlog_file11,
4839
4835
  L: 36,
4840
4836
  S: void 0,
4841
4837
  C: (f, a) => f(...a)
@@ -4846,21 +4842,21 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4846
4842
  extensions: [
4847
4843
  id && documentId2.of(id),
4848
4844
  // TODO(burdon): Doesn't catch errors in keymap functions.
4849
- EditorView18.exceptionSink.of((err) => {
4850
- log10.catch(err, void 0, {
4851
- F: __dxlog_file13,
4845
+ EditorView17.exceptionSink.of((err) => {
4846
+ log8.catch(err, void 0, {
4847
+ F: __dxlog_file11,
4852
4848
  L: 46,
4853
4849
  S: void 0,
4854
4850
  C: (f, a) => f(...a)
4855
4851
  });
4856
4852
  }),
4857
4853
  extensions,
4858
- EditorView18.updateListener.of(() => {
4854
+ EditorView17.updateListener.of(() => {
4859
4855
  onUpdate.current?.();
4860
4856
  })
4861
4857
  ].filter(isNotFalsy5)
4862
4858
  });
4863
- view2 = new EditorView18({
4859
+ view2 = new EditorView17({
4864
4860
  parent: parentRef.current,
4865
4861
  scrollTo,
4866
4862
  state: state2,
@@ -4876,10 +4872,10 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4876
4872
  setView(view2);
4877
4873
  }
4878
4874
  return () => {
4879
- log10("destroy", {
4875
+ log8("destroy", {
4880
4876
  id
4881
4877
  }, {
4882
- F: __dxlog_file13,
4878
+ F: __dxlog_file11,
4883
4879
  L: 74,
4884
4880
  S: void 0,
4885
4881
  C: (f, a) => f(...a)
@@ -4921,7 +4917,6 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4921
4917
  };
4922
4918
  export {
4923
4919
  Cursor,
4924
- Doc,
4925
4920
  EditorModes,
4926
4921
  Inline,
4927
4922
  List,
@@ -4930,8 +4925,6 @@ export {
4930
4925
  TextEditor,
4931
4926
  TextKind,
4932
4927
  Toolbar,
4933
- YText,
4934
- YXmlFragment,
4935
4928
  addBlockquote,
4936
4929
  addCodeblock,
4937
4930
  addLink,
@@ -4951,12 +4944,11 @@ export {
4951
4944
  createDataExtensions,
4952
4945
  createMarkdownExtensions,
4953
4946
  createThemeExtensions,
4954
- cursorLineMargin,
4955
4947
  decorateMarkdown,
4956
4948
  defaultOptions,
4957
4949
  defaultTheme,
4958
- dnd,
4959
4950
  documentId2 as documentId,
4951
+ dropFile,
4960
4952
  editorFillLayoutEditor,
4961
4953
  editorFillLayoutRoot,
4962
4954
  editorMode,
@@ -4970,7 +4962,7 @@ export {
4970
4962
  image,
4971
4963
  imageUpload,
4972
4964
  insertTable,
4973
- keymap10 as keymap,
4965
+ keymap11 as keymap,
4974
4966
  linkTooltip,
4975
4967
  listener,
4976
4968
  localStorageStateStoreAdapter,
@@ -4979,6 +4971,7 @@ export {
4979
4971
  markdownTags,
4980
4972
  markdownTagsExtensions,
4981
4973
  mention,
4974
+ processAction,
4982
4975
  removeBlockquote,
4983
4976
  removeCodeblock,
4984
4977
  removeLink,