@dxos/react-ui-editor 0.4.8-next.fff1521 → 0.4.8

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