@dxos/react-ui-editor 0.4.8-main.4d33a73 → 0.4.8-main.4f9d99e

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