@dxos/react-ui-editor 0.4.8-next.fff1521 → 0.4.9-main.2d20855

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