@dxos/react-ui-editor 0.4.8-main.46f461d → 0.4.8-main.4ad174a

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 (31) hide show
  1. package/dist/lib/browser/index.mjs +1285 -1323
  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 +2 -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/index.d.ts +1 -0
  16. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  17. package/dist/types/src/hooks/useActionHandler.d.ts +2 -2
  18. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  19. package/package.json +24 -24
  20. package/src/components/TextEditor/TextEditor.stories.tsx +14 -2
  21. package/src/components/Toolbar/Toolbar.tsx +2 -26
  22. package/src/extensions/command/state.ts +2 -2
  23. package/src/extensions/dnd.ts +31 -9
  24. package/src/extensions/factories.ts +14 -8
  25. package/src/extensions/index.ts +0 -1
  26. package/src/extensions/markdown/action.ts +112 -0
  27. package/src/extensions/markdown/index.ts +1 -0
  28. package/src/hooks/useActionHandler.ts +4 -89
  29. package/dist/types/src/extensions/cursor-line-margin.d.ts +0 -7
  30. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +0 -1
  31. package/src/extensions/cursor-line-margin.ts +0 -94
@@ -31,10 +31,10 @@ 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
36
  import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2 } from "react";
37
- import { log as log9 } from "@dxos/log";
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,34 +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
1910
  import { closeBrackets, closeBracketsKeymap } from "@codemirror/autocomplete";
1956
- import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1911
+ import { defaultKeymap, history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
1957
1912
  import { bracketMatching } from "@codemirror/language";
1958
1913
  import { searchKeymap } from "@codemirror/search";
1959
1914
  import { EditorState } from "@codemirror/state";
1960
- import { EditorView as EditorView10, crosshairCursor, drawSelection, dropCursor, highlightActiveLine, highlightSpecialChars, keymap as keymap5, 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";
1961
1916
  import defaultsDeep2 from "lodash.defaultsdeep";
1962
1917
  import { generateName } from "@dxos/display-name";
1963
- import { log as log7 } from "@dxos/log";
1918
+ import { log as log5 } from "@dxos/log";
1964
1919
  import { hueTokens } from "@dxos/react-ui-theme";
1965
1920
  import { hexToHue, isNotFalsy as isNotFalsy2 } from "@dxos/util";
1966
1921
 
@@ -2208,7 +2163,7 @@ var defaultTheme = {
2208
2163
  };
2209
2164
 
2210
2165
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2211
- 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";
2212
2167
  var defaults = {
2213
2168
  allowMultipleSelections: true,
2214
2169
  bracketMatching: true,
@@ -2217,16 +2172,23 @@ var defaults = {
2217
2172
  editable: true,
2218
2173
  history: true,
2219
2174
  lineWrapping: true,
2220
- search: true
2175
+ search: true,
2176
+ keymap: "standard"
2177
+ };
2178
+ var keymaps = {
2179
+ // https://codemirror.net/docs/ref/#commands.standardKeymap
2180
+ standard: standardKeymap,
2181
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
2182
+ default: defaultKeymap
2221
2183
  };
2222
2184
  var createBasicExtensions = (_props) => {
2223
2185
  const props = defaultsDeep2({}, _props, defaults);
2224
2186
  return [
2225
2187
  // TODO(burdon): Doesn't catch errors in keymap functions.
2226
- EditorView10.exceptionSink.of((err) => {
2227
- log7.catch(err, void 0, {
2228
- F: __dxlog_file9,
2229
- L: 83,
2188
+ EditorView9.exceptionSink.of((err) => {
2189
+ log5.catch(err, void 0, {
2190
+ F: __dxlog_file7,
2191
+ L: 93,
2230
2192
  S: void 0,
2231
2193
  C: (f, a) => f(...a)
2232
2194
  });
@@ -2235,26 +2197,26 @@ var createBasicExtensions = (_props) => {
2235
2197
  props.bracketMatching && bracketMatching(),
2236
2198
  props.closeBrackets && closeBrackets(),
2237
2199
  props.crosshairCursor && crosshairCursor(),
2238
- props.dropCursor && dropCursor(),
2200
+ props.dropCursor && dropCursor2(),
2239
2201
  props.drawSelection && drawSelection(),
2240
2202
  props.highlightActiveLine && highlightActiveLine(),
2241
- highlightSpecialChars(),
2242
2203
  props.history && history(),
2243
2204
  props.lineNumbers && lineNumbers(),
2244
- props.lineWrapping && EditorView10.lineWrapping,
2205
+ props.lineWrapping && EditorView9.lineWrapping,
2245
2206
  props.placeholder && placeholder(props.placeholder),
2246
2207
  props.readonly && [
2247
2208
  EditorState.readOnly.of(true),
2248
- EditorView10.editable.of(false)
2209
+ EditorView9.editable.of(false)
2249
2210
  ],
2250
2211
  props.scrollPastEnd && scrollPastEnd(),
2251
2212
  props.tabSize && EditorState.tabSize.of(props.tabSize),
2252
2213
  // https://codemirror.net/docs/ref/#view.KeyBinding
2253
2214
  keymap5.of([
2254
- // https://codemirror.net/docs/ref/#commands.standardKeymap
2255
- ...standardKeymap,
2215
+ ...(props.keymap && keymaps[props.keymap]) ?? [],
2256
2216
  // https://codemirror.net/docs/ref/#commands.indentWithTab
2257
- props.indentWithTab && indentWithTab,
2217
+ ...props.indentWithTab ? [
2218
+ indentWithTab
2219
+ ] : [],
2258
2220
  // https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
2259
2221
  ...props.closeBrackets ? closeBracketsKeymap : [],
2260
2222
  // https://codemirror.net/docs/ref/#commands.historyKeymap
@@ -2272,13 +2234,13 @@ var defaultSlots = {
2272
2234
  var createThemeExtensions = ({ theme, themeMode, slots: _slots } = {}) => {
2273
2235
  const slots = defaultsDeep2({}, _slots, defaultSlots);
2274
2236
  return [
2275
- EditorView10.baseTheme(defaultTheme),
2276
- EditorView10.darkTheme.of(themeMode === "dark"),
2277
- theme && EditorView10.theme(theme),
2278
- slots.editor?.className && EditorView10.editorAttributes.of({
2237
+ EditorView9.baseTheme(defaultTheme),
2238
+ EditorView9.darkTheme.of(themeMode === "dark"),
2239
+ theme && EditorView9.theme(theme),
2240
+ slots.editor?.className && EditorView9.editorAttributes.of({
2279
2241
  class: slots.editor.className
2280
2242
  }),
2281
- slots.content?.className && EditorView10.contentAttributes.of({
2243
+ slots.content?.className && EditorView9.contentAttributes.of({
2282
2244
  class: slots.content.className
2283
2245
  })
2284
2246
  ].filter(isNotFalsy2);
@@ -2306,917 +2268,415 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2306
2268
  };
2307
2269
 
2308
2270
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2309
- import { EditorView as EditorView11 } from "@codemirror/view";
2271
+ import { EditorView as EditorView10 } from "@codemirror/view";
2310
2272
  var listener = ({ onFocus, onChange }) => {
2311
2273
  const extensions = [];
2312
- onFocus && extensions.push(EditorView11.focusChangeEffect.of((_, focusing) => {
2274
+ onFocus && extensions.push(EditorView10.focusChangeEffect.of((_, focusing) => {
2313
2275
  onFocus(focusing);
2314
2276
  return null;
2315
2277
  }));
2316
- onChange && extensions.push(EditorView11.updateListener.of((update2) => {
2278
+ onChange && extensions.push(EditorView10.updateListener.of((update2) => {
2317
2279
  onChange(update2.state.doc.toString());
2318
2280
  }));
2319
2281
  return extensions;
2320
2282
  };
2321
2283
 
2322
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2323
- import { completionKeymap as completionKeymap2 } from "@codemirror/autocomplete";
2324
- import { defaultKeymap, indentWithTab as indentWithTab2 } from "@codemirror/commands";
2325
- import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
2326
- import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
2327
- import { languages } from "@codemirror/language-data";
2328
- import { lintKeymap } from "@codemirror/lint";
2329
- import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2330
- import { keymap as keymap6 } from "@codemirror/view";
2331
-
2332
- // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
2333
- import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
2334
- import { HighlightStyle } from "@codemirror/language";
2335
- import { tags, styleTags, Tag } from "@lezer/highlight";
2336
- import { Table } from "@lezer/markdown";
2337
- var markdownTags = {
2338
- Blockquote: Tag.define(),
2339
- CodeMark: Tag.define(),
2340
- CodeText: Tag.define(),
2341
- EmphasisMark: Tag.define(),
2342
- HeaderMark: Tag.define(),
2343
- InlineCode: Tag.define(),
2344
- LinkLabel: Tag.define(),
2345
- LinkReference: Tag.define(),
2346
- ListMark: Tag.define(),
2347
- QuoteMark: Tag.define(),
2348
- URL: Tag.define(),
2349
- // Custom.
2350
- TableCell: Tag.define()
2351
- };
2352
- Table.defineNodes?.forEach((node) => {
2353
- switch (node?.name) {
2354
- case "TableCell": {
2355
- node.style = markdownTags.TableCell;
2356
- break;
2284
+ // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2285
+ import { snippet } from "@codemirror/autocomplete";
2286
+ import { syntaxTree } from "@codemirror/language";
2287
+ import { EditorSelection } from "@codemirror/state";
2288
+ import { EditorView as EditorView11, keymap as keymap6 } from "@codemirror/view";
2289
+ import { useMemo, useState } from "react";
2290
+ 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;
2291
+ var Inline;
2292
+ (function(Inline2) {
2293
+ Inline2[Inline2["Strong"] = 0] = "Strong";
2294
+ Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2295
+ Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2296
+ Inline2[Inline2["Code"] = 3] = "Code";
2297
+ })(Inline || (Inline = {}));
2298
+ var List;
2299
+ (function(List2) {
2300
+ List2[List2["Ordered"] = 0] = "Ordered";
2301
+ List2[List2["Bullet"] = 1] = "Bullet";
2302
+ List2[List2["Task"] = 2] = "Task";
2303
+ })(List || (List = {}));
2304
+ var setHeading = (level) => ({ state: state2, dispatch }) => {
2305
+ const { selection: { ranges }, doc } = state2;
2306
+ const changes = [];
2307
+ let prevBlock = -1;
2308
+ for (const range of ranges) {
2309
+ let sawBlock = false;
2310
+ syntaxTree(state2).iterate({
2311
+ from: range.from,
2312
+ to: range.to,
2313
+ enter: (node) => {
2314
+ if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
2315
+ return;
2316
+ }
2317
+ sawBlock = true;
2318
+ prevBlock = node.from;
2319
+ const blockType = Textblocks[node.name];
2320
+ const isHeading = /heading(\d)/.exec(blockType);
2321
+ const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
2322
+ if (curLevel < 0 || curLevel === level) {
2323
+ return;
2324
+ }
2325
+ if (curLevel === 0) {
2326
+ changes.push({
2327
+ from: node.from,
2328
+ insert: "#".repeat(level) + " "
2329
+ });
2330
+ } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
2331
+ const nextLine = doc.lineAt(node.to);
2332
+ if (level) {
2333
+ changes.push({
2334
+ from: node.from,
2335
+ insert: "#".repeat(level) + " "
2336
+ });
2337
+ }
2338
+ changes.push({
2339
+ from: nextLine.from - 1,
2340
+ to: nextLine.to
2341
+ });
2342
+ } else {
2343
+ if (level === 0) {
2344
+ changes.push({
2345
+ from: node.from,
2346
+ to: Math.min(node.to, node.from + curLevel + 1)
2347
+ });
2348
+ } else if (level < curLevel) {
2349
+ changes.push({
2350
+ from: node.from,
2351
+ to: node.from + (curLevel - level)
2352
+ });
2353
+ } else {
2354
+ changes.push({
2355
+ from: node.from,
2356
+ insert: "#".repeat(level - curLevel)
2357
+ });
2358
+ }
2359
+ }
2360
+ }
2361
+ });
2362
+ let line;
2363
+ if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state2.doc.lineAt(range.from)).text)) {
2364
+ changes.push({
2365
+ from: line.from,
2366
+ to: line.to,
2367
+ insert: "#".repeat(level) + " "
2368
+ });
2357
2369
  }
2358
2370
  }
2359
- });
2360
- var markdownTagsExtensions = [
2361
- Table,
2362
- {
2363
- props: [
2364
- styleTags(markdownTags)
2365
- ]
2366
- }
2367
- ];
2368
- var markdownHighlightStyle = (readonly) => {
2369
- return HighlightStyle.define([
2370
- {
2371
- tag: [
2372
- tags.keyword,
2373
- tags.name,
2374
- tags.deleted,
2375
- tags.character,
2376
- tags.propertyName,
2377
- tags.macroName,
2378
- tags.color,
2379
- tags.constant(tags.name),
2380
- tags.standard(tags.name),
2381
- tags.definition(tags.name),
2382
- tags.separator,
2383
- tags.typeName,
2384
- tags.className,
2385
- tags.number,
2386
- tags.changed,
2387
- tags.annotation,
2388
- tags.modifier,
2389
- tags.self,
2390
- tags.namespace,
2391
- tags.operator,
2392
- tags.operatorKeyword,
2393
- tags.escape,
2394
- tags.regexp,
2395
- tags.special(tags.string),
2396
- tags.meta,
2397
- tags.comment,
2398
- tags.atom,
2399
- tags.bool,
2400
- tags.special(tags.variableName),
2401
- tags.processingInstruction,
2402
- tags.string,
2403
- tags.inserted,
2404
- tags.invalid
2405
- ],
2406
- color: "inherit !important"
2407
- },
2408
- // Markdown marks.
2409
- {
2410
- tag: [
2411
- tags.meta,
2412
- tags.processingInstruction,
2413
- markdownTags.LinkLabel,
2414
- markdownTags.LinkReference,
2415
- markdownTags.ListMark
2416
- ],
2417
- class: mark
2418
- },
2419
- // Markdown marks.
2420
- {
2421
- tag: [
2422
- markdownTags.CodeMark,
2423
- markdownTags.HeaderMark,
2424
- markdownTags.QuoteMark,
2425
- markdownTags.EmphasisMark
2426
- ],
2427
- class: mark
2428
- },
2429
- // E.g., code block language (after ```).
2430
- {
2431
- tag: [
2432
- tags.function(tags.variableName),
2433
- tags.labelName
2434
- ],
2435
- class: codeMark
2436
- },
2437
- {
2438
- tag: [
2439
- tags.monospace
2440
- ],
2441
- class: "font-mono"
2442
- },
2443
- // Headings.
2444
- {
2445
- tag: tags.heading1,
2446
- class: heading(1)
2447
- },
2448
- {
2449
- tag: tags.heading2,
2450
- class: heading(2)
2451
- },
2452
- {
2453
- tag: tags.heading3,
2454
- class: heading(3)
2455
- },
2456
- {
2457
- tag: tags.heading4,
2458
- class: heading(4)
2459
- },
2460
- {
2461
- tag: tags.heading5,
2462
- class: heading(5)
2463
- },
2464
- {
2465
- tag: tags.heading6,
2466
- class: heading(6)
2467
- },
2468
- // Emphasis.
2469
- {
2470
- tag: tags.emphasis,
2471
- class: italic
2472
- },
2473
- {
2474
- tag: tags.strong,
2475
- class: bold
2476
- },
2477
- {
2478
- tag: tags.strikethrough,
2479
- class: strikethrough
2480
- },
2481
- // Naked URLs.
2482
- {
2483
- tag: [
2484
- markdownTags.URL
2485
- ],
2486
- class: inlineUrl
2487
- },
2488
- // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
2489
- // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
2490
- // when a language is specified. In this case, the syntax highlighting extensions will colorize
2491
- // the code, but all other CSS properties will be inherited.
2492
- // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
2493
- {
2494
- tag: [
2495
- markdownTags.CodeText,
2496
- markdownTags.InlineCode
2497
- ],
2498
- class: code
2499
- },
2500
- {
2501
- tag: [
2502
- markdownTags.QuoteMark
2503
- ],
2504
- class: blockquote
2505
- },
2506
- {
2507
- tag: [
2508
- markdownTags.TableCell
2509
- ],
2510
- class: "font-mono"
2511
- }
2512
- ], {
2513
- scope: markdownLanguage2,
2514
- all: {
2515
- fontFamily: getToken("fontFamily.body", []).join(",")
2516
- }
2517
- });
2518
- };
2519
-
2520
- // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
2521
- var createMarkdownExtensions = ({ themeMode } = {}) => {
2522
- return [
2523
- // Main extension.
2524
- // https://github.com/codemirror/lang-markdown
2525
- // https://codemirror.net/5/mode/markdown/index.html (demo).
2526
- markdown({
2527
- // GRM by default (vs strict CommonMark):
2528
- // Table, TaskList, Strikethrough, and Autolink.
2529
- // NOTE: This extends the parser; it doesn't affect rendering.
2530
- // https://github.github.com/gfm
2531
- // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
2532
- base: markdownLanguage3,
2533
- // Languages for syntax highlighting fenced code blocks.
2534
- codeLanguages: languages,
2535
- // Parser extensions.
2536
- extensions: [
2537
- // GFM provided by default.
2538
- markdownTagsExtensions
2539
- ]
2540
- }),
2541
- // https://github.com/codemirror/theme-one-dark
2542
- themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
2543
- // Custom styles.
2544
- syntaxHighlighting(markdownHighlightStyle()),
2545
- keymap6.of([
2546
- // https://codemirror.net/docs/ref/#commands.indentWithTab
2547
- indentWithTab2,
2548
- // https://codemirror.net/docs/ref/#commands.defaultKeymap
2549
- ...defaultKeymap,
2550
- ...completionKeymap2,
2551
- ...lintKeymap
2552
- ])
2553
- ];
2554
- };
2555
-
2556
- // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
2557
- import { syntaxTree } from "@codemirror/language";
2558
- import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
2559
- import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2560
- import { mx as mx2 } from "@dxos/react-ui-theme";
2561
- var HorizontalRuleWidget = class extends WidgetType3 {
2562
- toDOM() {
2563
- const el = document.createElement("span");
2564
- el.className = "cm-hr";
2565
- return el;
2566
- }
2567
- };
2568
- var LinkButton = class extends WidgetType3 {
2569
- constructor(url, render) {
2570
- super();
2571
- this.url = url;
2572
- this.render = render;
2573
- }
2574
- eq(other) {
2575
- return this.url === other.url;
2576
- }
2577
- toDOM(view) {
2578
- const el = document.createElement("span");
2579
- this.render(el, this.url);
2580
- return el;
2371
+ if (!changes.length) {
2372
+ return false;
2581
2373
  }
2374
+ const changeSet = state2.changes(changes);
2375
+ dispatch(state2.update({
2376
+ changes: changeSet,
2377
+ selection: state2.selection.map(changeSet, 1),
2378
+ userEvent: "format.setHeading",
2379
+ scrollIntoView: true
2380
+ }));
2381
+ return true;
2582
2382
  };
2583
- var CheckboxWidget = class extends WidgetType3 {
2584
- constructor(_checked) {
2585
- super();
2586
- this._checked = _checked;
2587
- }
2588
- eq(other) {
2589
- return this._checked === other._checked;
2590
- }
2591
- toDOM(view) {
2592
- const input = document.createElement("input");
2593
- input.className = "cm-task-checkbox ch-checkbox ch-focus-ring -mbs-0.5";
2594
- input.type = "checkbox";
2595
- input.checked = this._checked;
2596
- if (view.state.readOnly) {
2597
- input.setAttribute("disabled", "true");
2598
- } else {
2599
- input.onmousedown = (event) => {
2600
- const pos = view.posAtDOM(input);
2601
- const text = view.state.sliceDoc(pos, pos + 3);
2602
- if (text === (this._checked ? "[x]" : "[ ]")) {
2603
- view.dispatch({
2604
- changes: {
2605
- from: pos + 1,
2606
- to: pos + 2,
2607
- insert: this._checked ? " " : "x"
2608
- }
2609
- });
2610
- event.preventDefault();
2383
+ var setStyle = (type, enable) => ({ state: state2, dispatch }) => {
2384
+ const marker = inlineMarkerText(type);
2385
+ const changes = state2.changeByRange((range) => {
2386
+ if (!enable && range.empty) {
2387
+ const after = state2.doc.sliceString(range.head, range.head + 6);
2388
+ const found = after.indexOf(marker);
2389
+ if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2390
+ const before = state2.doc.sliceString(range.head - 6, range.head);
2391
+ if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2392
+ ...before.slice(before.length - found)
2393
+ ].reverse().join("") === after.slice(0, found)) {
2394
+ return {
2395
+ changes: [
2396
+ {
2397
+ from: range.head - marker.length - found,
2398
+ to: range.head - found
2399
+ },
2400
+ {
2401
+ from: range.head + found,
2402
+ to: range.head + found + marker.length
2403
+ }
2404
+ ],
2405
+ range: EditorSelection.cursor(range.from - marker.length)
2406
+ };
2611
2407
  }
2612
- };
2408
+ }
2613
2409
  }
2614
- return input;
2615
- }
2616
- ignoreEvent() {
2617
- return false;
2618
- }
2619
- };
2620
- var hide = Decoration5.replace({});
2621
- var fencedCodeLine = Decoration5.line({
2622
- class: mx2("cm-code cm-codeblock-line")
2623
- });
2624
- var fencedCodeLineFirst = Decoration5.line({
2625
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
2626
- });
2627
- var fencedCodeLineLast = Decoration5.line({
2628
- class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
2629
- });
2630
- var horizontalRule = Decoration5.replace({
2631
- widget: new HorizontalRuleWidget()
2632
- });
2633
- var checkedTask = Decoration5.replace({
2634
- widget: new CheckboxWidget(true)
2635
- });
2636
- var uncheckedTask = Decoration5.replace({
2637
- widget: new CheckboxWidget(false)
2638
- });
2639
- var editingRange = (state2, range, focus) => {
2640
- const { readOnly, selection: { main: { head } } } = state2;
2641
- return focus && !readOnly && head >= range.from && head <= range.to;
2642
- };
2643
- var MarksByParent = /* @__PURE__ */ new Set([
2644
- "CodeMark",
2645
- "EmphasisMark",
2646
- "StrikethroughMark",
2647
- "SubscriptMark",
2648
- "SuperscriptMark"
2649
- ]);
2650
- var buildDecorations = (view, options, focus) => {
2651
- const deco = new RangeSetBuilder2();
2652
- const atomicDeco = new RangeSetBuilder2();
2653
- const { state: state2 } = view;
2654
- for (const { from, to } of view.visibleRanges) {
2410
+ const changes2 = [];
2411
+ const changesAtEnd = [];
2412
+ let blockStart = -1;
2413
+ let blockEnd = -1;
2414
+ let startCovered = false;
2415
+ let endCovered = false;
2416
+ let { from, to } = range;
2655
2417
  syntaxTree(state2).iterate({
2656
2418
  from,
2657
2419
  to,
2658
2420
  enter: (node) => {
2659
- if (node.name === "FencedCode") {
2660
- const editing = editingRange(state2, node, focus);
2661
- for (const block of view.viewportLineBlocks) {
2662
- if (block.to < node.from) {
2663
- continue;
2664
- }
2665
- if (block.from > node.to) {
2666
- break;
2667
- }
2668
- const first = block.from <= node.from;
2669
- const last = block.to >= node.to && /^(\s>)*```$/.test(state2.doc.sliceString(block.from, block.to));
2670
- deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
2671
- if (!editing && (first || last)) {
2672
- atomicDeco.add(block.from, block.to, hide);
2673
- }
2421
+ const { name } = node;
2422
+ if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2423
+ blockStart = blockContentStart(node);
2424
+ blockEnd = blockContentEnd(node, state2.doc);
2425
+ startCovered = endCovered = false;
2426
+ } else if (name === "Link" || name === "Image" && enable) {
2427
+ if (from < node.from && to > node.from && to <= node.to) {
2428
+ to = node.to;
2429
+ } else if (to > node.to && from >= node.from && from < node.to) {
2430
+ from = node.from;
2674
2431
  }
2675
- return false;
2676
- } else if (node.name === "Link") {
2677
- const marks = node.node.getChildren("LinkMark");
2678
- const urlNode = node.node.getChild("URL");
2679
- const editing = editingRange(state2, node, focus);
2680
- if (urlNode && marks.length >= 2) {
2681
- const url = state2.sliceDoc(urlNode.from, urlNode.to);
2682
- if (!editing) {
2683
- atomicDeco.add(node.from, marks[0].to, hide);
2684
- }
2685
- deco.add(marks[0].to, marks[1].from, Decoration5.mark({
2686
- tagName: "a",
2687
- attributes: {
2688
- class: "cm-link",
2689
- href: url,
2690
- rel: "noreferrer",
2691
- target: "_blank"
2692
- }
2693
- }));
2694
- if (!editing) {
2695
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
2696
- widget: new LinkButton(url, options.renderLinkButton)
2697
- }) : hide);
2432
+ } else if (IgnoreInline.has(name) && enable) {
2433
+ if (node.from < from && node.to > from) {
2434
+ if (to === from) {
2435
+ to = node.to;
2698
2436
  }
2437
+ from = node.to;
2699
2438
  }
2700
- } else if (node.name === "HeaderMark") {
2701
- const parent = node.node.parent;
2702
- if (/^ATX/.test(parent.name) && !editingRange(state2, state2.doc.lineAt(node.from), focus)) {
2703
- const next = state2.doc.sliceString(node.to, node.to + 1);
2704
- atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2439
+ if (node.from < to && node.to > to) {
2440
+ to = node.from;
2705
2441
  }
2706
- } else if (node.name === "HorizontalRule") {
2707
- if (!editingRange(state2, node, focus)) {
2708
- deco.add(node.from, node.to, horizontalRule);
2442
+ } else if (Object.hasOwn(InlineMarker, name)) {
2443
+ const markType = InlineMarker[name];
2444
+ const size = inlineMarkerText(markType).length;
2445
+ const openEnd = node.from + size;
2446
+ const closeStart = node.to - size;
2447
+ if (markType === type) {
2448
+ if (openEnd <= from && closeStart >= from) {
2449
+ startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2450
+ from: node.from,
2451
+ to: openEnd
2452
+ } : true;
2453
+ }
2454
+ if (openEnd <= to && closeStart >= to) {
2455
+ endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2456
+ from: closeStart,
2457
+ to: node.to
2458
+ } : true;
2459
+ }
2709
2460
  }
2710
- } else if (node.name === "TaskMarker") {
2711
- if (!editingRange(state2, node, focus)) {
2712
- const checked = state2.doc.sliceString(node.from + 1, node.to - 1) === "x";
2713
- atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
2714
- class: "cm-task"
2715
- }));
2716
- atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2461
+ if (markType === type || type === 3 && enable) {
2462
+ if (node.from >= from && openEnd <= to) {
2463
+ changes2.push({
2464
+ from: node.from,
2465
+ to: openEnd
2466
+ });
2467
+ if (markType !== type && closeStart >= to) {
2468
+ changesAtEnd.push({
2469
+ from: skipSpaces(Math.min(to, blockEnd), state2.doc, 1, blockEnd),
2470
+ insert: inlineMarkerText(markType)
2471
+ });
2472
+ }
2473
+ }
2474
+ if (closeStart >= from && node.to <= to) {
2475
+ changes2.push({
2476
+ from: closeStart,
2477
+ to: node.to
2478
+ });
2479
+ if (markType !== type && openEnd <= from) {
2480
+ changes2.push({
2481
+ from: skipSpaces(Math.max(from, blockStart), state2.doc, -1, blockStart),
2482
+ insert: inlineMarkerText(markType)
2483
+ });
2484
+ }
2485
+ }
2717
2486
  }
2718
- } else if (MarksByParent.has(node.name)) {
2719
- if (!editingRange(state2, node.node.parent, focus)) {
2720
- atomicDeco.add(node.from, node.to, hide);
2487
+ }
2488
+ },
2489
+ leave: (node) => {
2490
+ if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
2491
+ const rangeStart = Math.max(from, blockStart);
2492
+ const rangeEnd = Math.min(to, blockEnd);
2493
+ if (enable) {
2494
+ if (!startCovered) {
2495
+ changes2.push({
2496
+ from: rangeStart,
2497
+ insert: marker
2498
+ });
2499
+ }
2500
+ if (!endCovered) {
2501
+ changes2.push({
2502
+ from: rangeEnd,
2503
+ insert: marker
2504
+ });
2505
+ }
2506
+ } else {
2507
+ if (typeof startCovered === "object") {
2508
+ changes2.push(startCovered);
2509
+ } else if (startCovered) {
2510
+ changes2.push({
2511
+ from: skipSpaces(rangeStart, state2.doc, -1, blockStart),
2512
+ insert: marker
2513
+ });
2514
+ }
2515
+ if (typeof endCovered === "object") {
2516
+ changes2.push(endCovered);
2517
+ } else if (endCovered) {
2518
+ changes2.push({
2519
+ from: skipSpaces(rangeEnd, state2.doc, 1, blockEnd),
2520
+ insert: marker
2521
+ });
2522
+ }
2721
2523
  }
2722
2524
  }
2723
2525
  }
2724
2526
  });
2527
+ if (blockStart < 0 && range.empty && enable && !/\S/.test(state2.doc.lineAt(range.from).text)) {
2528
+ return {
2529
+ changes: {
2530
+ from: range.head,
2531
+ insert: marker + marker
2532
+ },
2533
+ range: EditorSelection.cursor(range.head + marker.length)
2534
+ };
2535
+ }
2536
+ const changeSet = state2.changes(changes2.concat(changesAtEnd));
2537
+ return {
2538
+ changes: changeSet,
2539
+ range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
2540
+ };
2541
+ });
2542
+ dispatch(state2.update(changes, {
2543
+ userEvent: enable ? "format.style.add" : "format.style.remove",
2544
+ scrollIntoView: true
2545
+ }));
2546
+ return true;
2547
+ };
2548
+ var addStyle = (style) => setStyle(style, true);
2549
+ var removeStyle = (style) => setStyle(style, false);
2550
+ var toggleStyle = (style) => (arg) => {
2551
+ const form = getFormatting(arg.state);
2552
+ return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
2553
+ };
2554
+ var toggleStrong = toggleStyle(0);
2555
+ var toggleEmphasis = toggleStyle(1);
2556
+ var toggleStrikethrough = toggleStyle(2);
2557
+ var toggleInlineCode = toggleStyle(3);
2558
+ var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
2559
+ var blockContentStart = (node) => {
2560
+ const atx = /^ATXHeading(\d)/.exec(node.name);
2561
+ if (atx) {
2562
+ return Math.min(node.to, node.from + +atx[1] + 1);
2725
2563
  }
2726
- return {
2727
- deco: deco.finish(),
2728
- atomicDeco: atomicDeco.finish()
2729
- };
2564
+ return node.from;
2730
2565
  };
2731
- var forceUpdate = StateEffect4.define();
2732
- var decorateMarkdown = (options = {}) => {
2733
- return [
2734
- ViewPlugin4.fromClass(class {
2735
- constructor(view) {
2736
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
2737
- }
2738
- update(update2) {
2739
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
2740
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
2741
- this.clearUpdate();
2742
- } else if (update2.selectionSet) {
2743
- this.scheduleUpdate(update2.view);
2744
- }
2745
- }
2746
- // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
2747
- // the cursor will float in space or be in the wrong position after the decoration is applied.
2748
- scheduleUpdate(view) {
2749
- this.clearUpdate();
2750
- this.pendingUpdate = setTimeout(() => {
2751
- view.dispatch({
2752
- effects: forceUpdate.of(null)
2753
- });
2754
- }, options.selectionChangeDelay);
2755
- }
2756
- clearUpdate() {
2757
- if (this.pendingUpdate) {
2758
- clearTimeout(this.pendingUpdate);
2759
- this.pendingUpdate = void 0;
2760
- }
2761
- }
2762
- destroy() {
2763
- this.clearUpdate();
2764
- }
2765
- }, {
2766
- provide: (plugin) => [
2767
- EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2768
- EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
2769
- EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
2770
- ]
2771
- }),
2772
- formattingStyles
2773
- ];
2566
+ var blockContentEnd = (node, doc) => {
2567
+ const setext = /^SetextHeading(\d)/.exec(node.name);
2568
+ const lastLine = doc.lineAt(node.to);
2569
+ if (setext || /^[\s>]*$/.exec(lastLine.text)) {
2570
+ return lastLine.from - 1;
2571
+ }
2572
+ return node.to;
2774
2573
  };
2775
- var formattingStyles = EditorView12.baseTheme({
2776
- "& .cm-code": {
2777
- fontFamily: getToken("fontFamily.mono", []).join(",")
2778
- },
2779
- "& .cm-codeblock-line": {
2780
- paddingInline: "1rem !important"
2781
- },
2782
- "& .cm-codeblock-end": {
2783
- display: "inline-block",
2784
- width: "100%",
2785
- position: "relative",
2786
- "&::after": {
2787
- position: "absolute",
2788
- inset: 0,
2789
- content: '""'
2574
+ var skipSpaces = (pos, doc, dir, limit) => {
2575
+ const line = doc.lineAt(pos);
2576
+ while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
2577
+ pos += dir;
2578
+ }
2579
+ return pos;
2580
+ };
2581
+ var skipMarkers = (pos, tree, dir, limit) => {
2582
+ for (; ; ) {
2583
+ const next = tree.resolve(pos, dir);
2584
+ if (!IgnoreInline.has(next.name)) {
2585
+ return pos;
2790
2586
  }
2791
- },
2792
- "& .cm-codeblock-first": {
2793
- borderTopLeftRadius: ".5rem",
2794
- borderTopRightRadius: ".5rem"
2795
- },
2796
- "& .cm-codeblock-last": {
2797
- borderBottomLeftRadius: ".5rem",
2798
- borderBottomRightRadius: ".5rem"
2799
- },
2800
- "&light .cm-codeblock-line, &light .cm-activeLine.cm-codeblock-line": {
2801
- background: getToken("extend.semanticColors.input.light"),
2802
- mixBlendMode: "darken"
2803
- },
2804
- "&dark .cm-codeblock-line, &dark .cm-activeLine.cm-codeblock-line": {
2805
- background: getToken("extend.semanticColors.input.dark"),
2806
- mixBlendMode: "lighten"
2807
- },
2808
- "& .cm-hr": {
2809
- display: "inline-block",
2810
- width: "100%",
2811
- height: "0",
2812
- verticalAlign: "middle",
2813
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2814
- },
2815
- "& .cm-task": {
2816
- color: getToken("extend.colors.blue.500")
2817
- },
2818
- "& .cm-task-checkbox": {
2819
- marginLeft: "4px",
2820
- marginRight: "4px"
2587
+ const moveTo = dir < 0 ? next.from : next.to;
2588
+ if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
2589
+ return pos;
2590
+ }
2591
+ pos = moveTo;
2821
2592
  }
2822
- });
2823
-
2824
- // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2825
- import { snippet } from "@codemirror/autocomplete";
2826
- import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2827
- import { EditorSelection } from "@codemirror/state";
2828
- import { EditorView as EditorView13, keymap as keymap7 } from "@codemirror/view";
2829
- import { useMemo, useState } from "react";
2830
- 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;
2831
- var Inline;
2832
- (function(Inline2) {
2833
- Inline2[Inline2["Strong"] = 0] = "Strong";
2834
- Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
2835
- Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
2836
- Inline2[Inline2["Code"] = 3] = "Code";
2837
- })(Inline || (Inline = {}));
2838
- var List;
2839
- (function(List2) {
2840
- List2[List2["Ordered"] = 0] = "Ordered";
2841
- List2[List2["Bullet"] = 1] = "Bullet";
2842
- List2[List2["Task"] = 2] = "Task";
2843
- })(List || (List = {}));
2844
- var setHeading = (level) => ({ state: state2, dispatch }) => {
2845
- const { selection: { ranges }, doc } = state2;
2846
- const changes = [];
2847
- let prevBlock = -1;
2848
- for (const range of ranges) {
2849
- let sawBlock = false;
2850
- syntaxTree2(state2).iterate({
2851
- from: range.from,
2852
- to: range.to,
2853
- enter: (node) => {
2854
- if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
2855
- return;
2856
- }
2857
- sawBlock = true;
2858
- prevBlock = node.from;
2859
- const blockType = Textblocks[node.name];
2860
- const isHeading = /heading(\d)/.exec(blockType);
2861
- const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
2862
- if (curLevel < 0 || curLevel === level) {
2863
- return;
2864
- }
2865
- if (curLevel === 0) {
2866
- changes.push({
2867
- from: node.from,
2868
- insert: "#".repeat(level) + " "
2869
- });
2870
- } else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
2871
- const nextLine = doc.lineAt(node.to);
2872
- if (level) {
2873
- changes.push({
2874
- from: node.from,
2875
- insert: "#".repeat(level) + " "
2876
- });
2877
- }
2878
- changes.push({
2879
- from: nextLine.from - 1,
2880
- to: nextLine.to
2881
- });
2882
- } else {
2883
- if (level === 0) {
2884
- changes.push({
2885
- from: node.from,
2886
- to: Math.min(node.to, node.from + curLevel + 1)
2887
- });
2888
- } else if (level < curLevel) {
2593
+ };
2594
+ var snippets = {
2595
+ codeblock: snippet([
2596
+ //
2597
+ "```#{}",
2598
+ "",
2599
+ "```"
2600
+ ].join("\n")),
2601
+ table: snippet([
2602
+ //
2603
+ "| #{col1} | #{col2} |",
2604
+ "| ---- | ---- |",
2605
+ "| #{val1} | #{val2} |",
2606
+ "| #{val3} | #{val4} |",
2607
+ ""
2608
+ ].join("\n"))
2609
+ };
2610
+ var insertTable = (view) => {
2611
+ const { selection: { main }, doc } = view.state;
2612
+ const { number } = doc.lineAt(main.anchor);
2613
+ const { from } = doc.line(number);
2614
+ snippets.table(view, null, from, from);
2615
+ };
2616
+ var removeLinkInner = (from, to, changes, state2) => {
2617
+ syntaxTree(state2).iterate({
2618
+ from,
2619
+ to,
2620
+ enter: (node) => {
2621
+ if (node.name === "Link" && node.from < to && node.to > from) {
2622
+ node.node.cursor().iterate((node2) => {
2623
+ const { name } = node2;
2624
+ if (name === "LinkMark" || name === "LinkLabel") {
2889
2625
  changes.push({
2890
- from: node.from,
2891
- to: node.from + (curLevel - level)
2626
+ from: node2.from,
2627
+ to: node2.to
2892
2628
  });
2893
- } else {
2629
+ } else if (name === "LinkTitle" || name === "URL") {
2894
2630
  changes.push({
2895
- from: node.from,
2896
- insert: "#".repeat(level - curLevel)
2631
+ from: skipSpaces(node2.from, state2.doc, -1),
2632
+ to: skipSpaces(node2.to, state2.doc, 1)
2897
2633
  });
2898
2634
  }
2899
- }
2635
+ });
2636
+ return false;
2900
2637
  }
2901
- });
2902
- let line;
2903
- if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state2.doc.lineAt(range.from)).text)) {
2904
- changes.push({
2905
- from: line.from,
2906
- to: line.to,
2907
- insert: "#".repeat(level) + " "
2908
- });
2909
2638
  }
2639
+ });
2640
+ };
2641
+ var removeLink = ({ state: state2, dispatch }) => {
2642
+ const changes = [];
2643
+ for (const { from, to } of state2.selection.ranges) {
2644
+ removeLinkInner(from, to, changes, state2);
2910
2645
  }
2911
- if (!changes.length) {
2646
+ if (!changes) {
2912
2647
  return false;
2913
2648
  }
2914
- const changeSet = state2.changes(changes);
2915
2649
  dispatch(state2.update({
2916
- changes: changeSet,
2917
- selection: state2.selection.map(changeSet, 1),
2918
- userEvent: "format.setHeading",
2650
+ changes,
2651
+ userEvent: "format.link.remove",
2919
2652
  scrollIntoView: true
2920
2653
  }));
2921
2654
  return true;
2922
2655
  };
2923
- var setStyle = (type, enable) => ({ state: state2, dispatch }) => {
2924
- const marker = inlineMarkerText(type);
2656
+ var addLink = ({ url, image: image2 } = {}) => ({ state: state2, dispatch }) => {
2925
2657
  const changes = state2.changeByRange((range) => {
2926
- if (!enable && range.empty) {
2927
- const after = state2.doc.sliceString(range.head, range.head + 6);
2928
- const found = after.indexOf(marker);
2929
- if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
2930
- const before = state2.doc.sliceString(range.head - 6, range.head);
2931
- if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
2932
- ...before.slice(before.length - found)
2933
- ].reverse().join("") === after.slice(0, found)) {
2934
- return {
2935
- changes: [
2936
- {
2937
- from: range.head - marker.length - found,
2938
- to: range.head - found
2939
- },
2940
- {
2941
- from: range.head + found,
2942
- to: range.head + found + marker.length
2943
- }
2944
- ],
2945
- range: EditorSelection.cursor(range.from - marker.length)
2946
- };
2947
- }
2948
- }
2949
- }
2950
- const changes2 = [];
2951
- const changesAtEnd = [];
2952
- let blockStart = -1;
2953
- let blockEnd = -1;
2954
- let startCovered = false;
2955
- let endCovered = false;
2956
2658
  let { from, to } = range;
2957
- syntaxTree2(state2).iterate({
2659
+ const cutStyles = [];
2660
+ let okay = null;
2661
+ syntaxTree(state2).iterate({
2958
2662
  from,
2959
2663
  to,
2960
2664
  enter: (node) => {
2961
- const { name } = node;
2962
- if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
2963
- blockStart = blockContentStart(node);
2964
- blockEnd = blockContentEnd(node, state2.doc);
2965
- startCovered = endCovered = false;
2966
- } else if (name === "Link" || name === "Image" && enable) {
2967
- if (from < node.from && to > node.from && to <= node.to) {
2968
- to = node.to;
2969
- } else if (to > node.to && from >= node.from && from < node.to) {
2970
- from = node.from;
2971
- }
2972
- } else if (IgnoreInline.has(name) && enable) {
2973
- if (node.from < from && node.to > from) {
2974
- if (to === from) {
2975
- to = node.to;
2976
- }
2977
- from = node.to;
2978
- }
2979
- if (node.from < to && node.to > to) {
2980
- to = node.from;
2981
- }
2982
- } else if (Object.hasOwn(InlineMarker, name)) {
2983
- const markType = InlineMarker[name];
2984
- const size = inlineMarkerText(markType).length;
2985
- const openEnd = node.from + size;
2986
- const closeStart = node.to - size;
2987
- if (markType === type) {
2988
- if (openEnd <= from && closeStart >= from) {
2989
- startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2990
- from: node.from,
2991
- to: openEnd
2992
- } : true;
2665
+ if (Object.hasOwn(Textblocks, node.name)) {
2666
+ okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state2.doc);
2667
+ } else if (Object.hasOwn(InlineMarker, node.name)) {
2668
+ const sNode = node.node;
2669
+ if (node.from < from && node.to <= to) {
2670
+ if (sNode.firstChild.to === from) {
2671
+ from = node.from;
2672
+ } else {
2673
+ cutStyles.push(sNode);
2993
2674
  }
2994
- if (openEnd <= to && closeStart >= to) {
2995
- endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2996
- from: closeStart,
2997
- to: node.to
2998
- } : true;
2999
- }
3000
- }
3001
- if (markType === type || type === 3 && enable) {
3002
- if (node.from >= from && openEnd <= to) {
3003
- changes2.push({
3004
- from: node.from,
3005
- to: openEnd
3006
- });
3007
- if (markType !== type && closeStart >= to) {
3008
- changesAtEnd.push({
3009
- from: skipSpaces(Math.min(to, blockEnd), state2.doc, 1, blockEnd),
3010
- insert: inlineMarkerText(markType)
3011
- });
3012
- }
3013
- }
3014
- if (closeStart >= from && node.to <= to) {
3015
- changes2.push({
3016
- from: closeStart,
3017
- to: node.to
3018
- });
3019
- if (markType !== type && openEnd <= from) {
3020
- changes2.push({
3021
- from: skipSpaces(Math.max(from, blockStart), state2.doc, -1, blockStart),
3022
- insert: inlineMarkerText(markType)
3023
- });
3024
- }
3025
- }
3026
- }
3027
- }
3028
- },
3029
- leave: (node) => {
3030
- if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
3031
- const rangeStart = Math.max(from, blockStart);
3032
- const rangeEnd = Math.min(to, blockEnd);
3033
- if (enable) {
3034
- if (!startCovered) {
3035
- changes2.push({
3036
- from: rangeStart,
3037
- insert: marker
3038
- });
3039
- }
3040
- if (!endCovered) {
3041
- changes2.push({
3042
- from: rangeEnd,
3043
- insert: marker
3044
- });
3045
- }
3046
- } else {
3047
- if (typeof startCovered === "object") {
3048
- changes2.push(startCovered);
3049
- } else if (startCovered) {
3050
- changes2.push({
3051
- from: skipSpaces(rangeStart, state2.doc, -1, blockStart),
3052
- insert: marker
3053
- });
3054
- }
3055
- if (typeof endCovered === "object") {
3056
- changes2.push(endCovered);
3057
- } else if (endCovered) {
3058
- changes2.push({
3059
- from: skipSpaces(rangeEnd, state2.doc, 1, blockEnd),
3060
- insert: marker
3061
- });
3062
- }
3063
- }
3064
- }
3065
- }
3066
- });
3067
- if (blockStart < 0 && range.empty && enable && !/\S/.test(state2.doc.lineAt(range.from).text)) {
3068
- return {
3069
- changes: {
3070
- from: range.head,
3071
- insert: marker + marker
3072
- },
3073
- range: EditorSelection.cursor(range.head + marker.length)
3074
- };
3075
- }
3076
- const changeSet = state2.changes(changes2.concat(changesAtEnd));
3077
- return {
3078
- changes: changeSet,
3079
- range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
3080
- };
3081
- });
3082
- dispatch(state2.update(changes, {
3083
- userEvent: enable ? "format.style.add" : "format.style.remove",
3084
- scrollIntoView: true
3085
- }));
3086
- return true;
3087
- };
3088
- var addStyle = (style) => setStyle(style, true);
3089
- var removeStyle = (style) => setStyle(style, false);
3090
- var toggleStyle = (style) => (arg) => {
3091
- const form = getFormatting(arg.state);
3092
- return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
3093
- };
3094
- var toggleStrong = toggleStyle(0);
3095
- var toggleEmphasis = toggleStyle(1);
3096
- var toggleStrikethrough = toggleStyle(2);
3097
- var toggleInlineCode = toggleStyle(3);
3098
- var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
3099
- var blockContentStart = (node) => {
3100
- const atx = /^ATXHeading(\d)/.exec(node.name);
3101
- if (atx) {
3102
- return Math.min(node.to, node.from + +atx[1] + 1);
3103
- }
3104
- return node.from;
3105
- };
3106
- var blockContentEnd = (node, doc) => {
3107
- const setext = /^SetextHeading(\d)/.exec(node.name);
3108
- const lastLine = doc.lineAt(node.to);
3109
- if (setext || /^[\s>]*$/.exec(lastLine.text)) {
3110
- return lastLine.from - 1;
3111
- }
3112
- return node.to;
3113
- };
3114
- var skipSpaces = (pos, doc, dir, limit) => {
3115
- const line = doc.lineAt(pos);
3116
- while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
3117
- pos += dir;
3118
- }
3119
- return pos;
3120
- };
3121
- var skipMarkers = (pos, tree, dir, limit) => {
3122
- for (; ; ) {
3123
- const next = tree.resolve(pos, dir);
3124
- if (!IgnoreInline.has(next.name)) {
3125
- return pos;
3126
- }
3127
- const moveTo = dir < 0 ? next.from : next.to;
3128
- if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
3129
- return pos;
3130
- }
3131
- pos = moveTo;
3132
- }
3133
- };
3134
- var snippets = {
3135
- codeblock: snippet([
3136
- //
3137
- "```#{}",
3138
- "",
3139
- "```"
3140
- ].join("\n")),
3141
- table: snippet([
3142
- //
3143
- "| #{col1} | #{col2} |",
3144
- "| ---- | ---- |",
3145
- "| #{val1} | #{val2} |",
3146
- "| #{val3} | #{val4} |",
3147
- ""
3148
- ].join("\n"))
3149
- };
3150
- var insertTable = (view) => {
3151
- const { selection: { main }, doc } = view.state;
3152
- const { number } = doc.lineAt(main.anchor);
3153
- const { from } = doc.line(number);
3154
- snippets.table(view, null, from, from);
3155
- };
3156
- var removeLinkInner = (from, to, changes, state2) => {
3157
- syntaxTree2(state2).iterate({
3158
- from,
3159
- to,
3160
- enter: (node) => {
3161
- if (node.name === "Link" && node.from < to && node.to > from) {
3162
- node.node.cursor().iterate((node2) => {
3163
- const { name } = node2;
3164
- if (name === "LinkMark" || name === "LinkLabel") {
3165
- changes.push({
3166
- from: node2.from,
3167
- to: node2.to
3168
- });
3169
- } else if (name === "LinkTitle" || name === "URL") {
3170
- changes.push({
3171
- from: skipSpaces(node2.from, state2.doc, -1),
3172
- to: skipSpaces(node2.to, state2.doc, 1)
3173
- });
3174
- }
3175
- });
3176
- return false;
3177
- }
3178
- }
3179
- });
3180
- };
3181
- var removeLink = ({ state: state2, dispatch }) => {
3182
- const changes = [];
3183
- for (const { from, to } of state2.selection.ranges) {
3184
- removeLinkInner(from, to, changes, state2);
3185
- }
3186
- if (!changes) {
3187
- return false;
3188
- }
3189
- dispatch(state2.update({
3190
- changes,
3191
- userEvent: "format.link.remove",
3192
- scrollIntoView: true
3193
- }));
3194
- return true;
3195
- };
3196
- var addLink = ({ url, image: image2 } = {}) => ({ state: state2, dispatch }) => {
3197
- const changes = state2.changeByRange((range) => {
3198
- let { from, to } = range;
3199
- const cutStyles = [];
3200
- let okay = null;
3201
- syntaxTree2(state2).iterate({
3202
- from,
3203
- to,
3204
- enter: (node) => {
3205
- if (Object.hasOwn(Textblocks, node.name)) {
3206
- okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state2.doc);
3207
- } else if (Object.hasOwn(InlineMarker, node.name)) {
3208
- const sNode = node.node;
3209
- if (node.from < from && node.to <= to) {
3210
- if (sNode.firstChild.to === from) {
3211
- from = node.from;
3212
- } else {
3213
- cutStyles.push(sNode);
3214
- }
3215
- } else if (node.from >= from && node.to > to) {
3216
- if (sNode.lastChild.from === to) {
3217
- to = node.to;
3218
- } else {
3219
- cutStyles.push(sNode);
2675
+ } else if (node.from >= from && node.to > to) {
2676
+ if (sNode.lastChild.from === to) {
2677
+ to = node.to;
2678
+ } else {
2679
+ cutStyles.push(sNode);
3220
2680
  }
3221
2681
  }
3222
2682
  }
@@ -3291,7 +2751,7 @@ var addList = (type) => ({ state: state2, dispatch }) => {
3291
2751
  let parentColumn = null;
3292
2752
  const blocks = [];
3293
2753
  for (const { from, to } of state2.selection.ranges) {
3294
- syntaxTree2(state2).iterate({
2754
+ syntaxTree(state2).iterate({
3295
2755
  from,
3296
2756
  to,
3297
2757
  enter: (node) => {
@@ -3424,7 +2884,7 @@ var removeList = (type) => ({ state: state2, dispatch }) => {
3424
2884
  const stack = [];
3425
2885
  const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
3426
2886
  for (const { from, to } of state2.selection.ranges) {
3427
- syntaxTree2(state2).iterate({
2887
+ syntaxTree(state2).iterate({
3428
2888
  from,
3429
2889
  to,
3430
2890
  enter: (node) => {
@@ -3507,7 +2967,7 @@ var setBlockquote = (enable) => ({ state: state2, dispatch }) => {
3507
2967
  let lastBlock = -1;
3508
2968
  for (const { from, to } of state2.selection.ranges) {
3509
2969
  const sawBlock = false;
3510
- syntaxTree2(state2).iterate({
2970
+ syntaxTree(state2).iterate({
3511
2971
  from,
3512
2972
  to,
3513
2973
  enter: (node) => {
@@ -3596,7 +3056,7 @@ var addCodeblock = (target) => {
3596
3056
  for (const { from, to } of selection.ranges) {
3597
3057
  let blockFrom = from;
3598
3058
  let blockTo = to;
3599
- syntaxTree2(state2).iterate({
3059
+ syntaxTree(state2).iterate({
3600
3060
  from,
3601
3061
  to,
3602
3062
  enter: (node) => {
@@ -3620,304 +3080,855 @@ var addCodeblock = (target) => {
3620
3080
  });
3621
3081
  }
3622
3082
  }
3623
- if (!ranges.length) {
3624
- return false;
3083
+ if (!ranges.length) {
3084
+ return false;
3085
+ }
3086
+ const changes = ranges.map(({ from, to }) => {
3087
+ const column = from - state2.doc.lineAt(from).from;
3088
+ return [
3089
+ {
3090
+ from,
3091
+ insert: "```\n" + " ".repeat(column)
3092
+ },
3093
+ {
3094
+ from: to,
3095
+ insert: "\n" + " ".repeat(column) + "```"
3096
+ }
3097
+ ];
3098
+ });
3099
+ dispatch(state2.update({
3100
+ changes,
3101
+ userEvent: "format.codeblock.add",
3102
+ scrollIntoView: true
3103
+ }));
3104
+ return true;
3105
+ };
3106
+ var removeCodeblock = ({ state: state2, dispatch }) => {
3107
+ const changes = [];
3108
+ let lastBlock = -1;
3109
+ for (const { from, to } of state2.selection.ranges) {
3110
+ syntaxTree(state2).iterate({
3111
+ from,
3112
+ to,
3113
+ enter: (node) => {
3114
+ if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3115
+ lastBlock = node.from;
3116
+ const firstLine = state2.doc.lineAt(node.from);
3117
+ if (node.name === "FencedCode") {
3118
+ changes.push({
3119
+ from: node.from,
3120
+ to: firstLine.to + 1 + node.from - firstLine.from
3121
+ });
3122
+ const lastLine = state2.doc.lineAt(node.to);
3123
+ if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3124
+ changes.push({
3125
+ from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
3126
+ to: lastLine.to
3127
+ });
3128
+ }
3129
+ } else {
3130
+ const column = node.from - firstLine.from;
3131
+ for (let line = firstLine; ; line = state2.doc.line(line.number + 1)) {
3132
+ changes.push({
3133
+ from: line.from + column - 4,
3134
+ to: line.from + column
3135
+ });
3136
+ if (line.to >= node.to) {
3137
+ break;
3138
+ }
3139
+ }
3140
+ }
3141
+ }
3142
+ }
3143
+ });
3144
+ }
3145
+ if (!changes.length) {
3146
+ return false;
3147
+ }
3148
+ dispatch(state2.update({
3149
+ changes,
3150
+ userEvent: "format.codeblock.remove",
3151
+ scrollIntoView: true
3152
+ }));
3153
+ return true;
3154
+ };
3155
+ var toggleCodeblock = (target) => {
3156
+ return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3157
+ };
3158
+ var formattingKeymap = (options = {}) => {
3159
+ return [
3160
+ keymap6.of([
3161
+ {
3162
+ key: "meta-b",
3163
+ run: toggleStrong
3164
+ }
3165
+ ])
3166
+ ];
3167
+ };
3168
+ var InlineMarker = {
3169
+ Emphasis: 1,
3170
+ StrongEmphasis: 0,
3171
+ InlineCode: 3,
3172
+ Strikethrough: 2
3173
+ };
3174
+ var IgnoreInline = /* @__PURE__ */ new Set([
3175
+ "Autolink",
3176
+ "CodeMark",
3177
+ "CodeText",
3178
+ "Comment",
3179
+ "EmphasisMark",
3180
+ "Hardbreak",
3181
+ "HeaderMark",
3182
+ "HTMLTag",
3183
+ "LinkMark",
3184
+ "ListMark",
3185
+ "ProcessingInstruction",
3186
+ "QuoteMark",
3187
+ "StrikethroughMark",
3188
+ "SubscriptMark",
3189
+ "SuperscriptMark",
3190
+ "TaskMarker"
3191
+ ]);
3192
+ var Textblocks = {
3193
+ ATXHeading1: "heading1",
3194
+ ATXHeading2: "heading2",
3195
+ ATXHeading3: "heading3",
3196
+ ATXHeading4: "heading4",
3197
+ ATXHeading5: "heading5",
3198
+ ATXHeading6: "heading6",
3199
+ CodeBlock: "codeblock",
3200
+ FencedCode: "codeblock",
3201
+ Paragraph: "paragraph",
3202
+ SetextHeading1: "heading1",
3203
+ SetextHeading2: "heading2",
3204
+ TableCell: "tablecell",
3205
+ Task: "paragraph"
3206
+ };
3207
+ var getFormatting = (state2) => {
3208
+ let blockType = null;
3209
+ const inline = [
3210
+ null,
3211
+ null,
3212
+ null,
3213
+ null
3214
+ ];
3215
+ let link = false;
3216
+ let blockQuote = null;
3217
+ let listStyle = null;
3218
+ const stack = [];
3219
+ let currentBlock = null;
3220
+ const advanceInline = (upto) => {
3221
+ if (!currentBlock) {
3222
+ return;
3223
+ }
3224
+ upto = Math.min(upto, currentBlock.end);
3225
+ if (upto <= currentBlock.pos) {
3226
+ return;
3227
+ }
3228
+ for (let i = 0; i < currentBlock.active.length; i++) {
3229
+ if (inline[i] === false) {
3230
+ continue;
3231
+ } else if (currentBlock.active[i]) {
3232
+ inline[i] = true;
3233
+ } else if (/\S/.test(state2.doc.sliceString(currentBlock.pos, upto))) {
3234
+ inline[i] = false;
3235
+ }
3236
+ }
3237
+ currentBlock.pos = upto;
3238
+ };
3239
+ const skipInline = (upto) => {
3240
+ if (currentBlock && upto > currentBlock.pos) {
3241
+ currentBlock.pos = Math.min(upto, currentBlock.end);
3242
+ }
3243
+ };
3244
+ const { selection } = state2;
3245
+ for (const range of selection.ranges) {
3246
+ if (range.empty && inline.some((v) => v === null)) {
3247
+ const contextSize = Math.min(range.head, 6);
3248
+ const contextBefore = state2.doc.sliceString(range.head - contextSize, range.head);
3249
+ let contextAfter = state2.doc.sliceString(range.head, range.head + contextSize);
3250
+ for (let i = 0; i < contextSize; i++) {
3251
+ const ch = contextAfter[i];
3252
+ if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
3253
+ contextAfter = contextAfter.slice(0, i);
3254
+ break;
3255
+ }
3256
+ }
3257
+ for (let i = 0; i < inline.length; i++) {
3258
+ const mark2 = inlineMarkerText(i);
3259
+ const found = contextAfter.indexOf(mark2);
3260
+ if (found > -1) {
3261
+ contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3262
+ if (inline[i] === null) {
3263
+ inline[i] = true;
3264
+ }
3265
+ }
3266
+ }
3267
+ }
3268
+ syntaxTree(state2).iterate({
3269
+ from: range.from,
3270
+ to: range.to,
3271
+ enter: (node) => {
3272
+ advanceInline(node.from);
3273
+ const { name } = node;
3274
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3275
+ stack.push(name);
3276
+ } else if (name === "Link") {
3277
+ link = true;
3278
+ } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3279
+ if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3280
+ stack[stack.length - 1] = "TaskList";
3281
+ }
3282
+ const blockCode = Textblocks[name];
3283
+ if (blockType === null) {
3284
+ blockType = blockCode;
3285
+ } else if (blockType !== blockCode) {
3286
+ blockType = false;
3287
+ }
3288
+ if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
3289
+ currentBlock = {
3290
+ pos: Math.max(range.from, node.from),
3291
+ end: Math.min(range.to, node.to),
3292
+ active: [
3293
+ false,
3294
+ false,
3295
+ false,
3296
+ false
3297
+ ]
3298
+ };
3299
+ }
3300
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3301
+ const index = InlineMarker[name];
3302
+ if (range.empty && inline[index] === null) {
3303
+ inline[index] = true;
3304
+ }
3305
+ currentBlock.active[index] = true;
3306
+ } else if (IgnoreInline.has(name)) {
3307
+ skipInline(node.to);
3308
+ }
3309
+ },
3310
+ leave: (node) => {
3311
+ advanceInline(node.to);
3312
+ const { name } = node;
3313
+ if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3314
+ stack.pop();
3315
+ } else if (Object.hasOwn(Textblocks, name)) {
3316
+ let hasList = false;
3317
+ let hasQuote = false;
3318
+ for (let i = stack.length - 1; i >= 0; i--) {
3319
+ if (stack[i] === "Blockquote") {
3320
+ hasQuote = true;
3321
+ } else if (!hasList) {
3322
+ hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
3323
+ }
3324
+ }
3325
+ if (blockQuote === null) {
3326
+ blockQuote = hasQuote;
3327
+ } else if (!hasQuote && blockQuote) {
3328
+ blockQuote = false;
3329
+ }
3330
+ if (listStyle === null) {
3331
+ listStyle = hasList;
3332
+ } else if (listStyle !== hasList) {
3333
+ listStyle = false;
3334
+ }
3335
+ currentBlock = null;
3336
+ } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3337
+ currentBlock.active[InlineMarker[name]] = false;
3338
+ }
3339
+ }
3340
+ });
3341
+ }
3342
+ const { from, to } = state2.doc.lineAt(selection.main.anchor);
3343
+ const blankLine = from === to;
3344
+ return {
3345
+ blankLine,
3346
+ blockType: blockType || null,
3347
+ blockQuote: blockQuote ?? false,
3348
+ code: inline[3] ?? false,
3349
+ emphasis: inline[1] ?? false,
3350
+ strong: inline[0] ?? false,
3351
+ strikethrough: inline[2] ?? false,
3352
+ link,
3353
+ listStyle: listStyle || null
3354
+ };
3355
+ };
3356
+ var useFormattingState = () => {
3357
+ const [state2, setState] = useState();
3358
+ const observer = useMemo(() => EditorView11.updateListener.of((update2) => {
3359
+ if (update2.docChanged || update2.selectionSet) {
3360
+ setState((prevState) => {
3361
+ const newState = getFormatting(update2.state);
3362
+ if (!prevState || !formattingEquals(prevState, newState)) {
3363
+ return newState;
3364
+ }
3365
+ return prevState;
3366
+ });
3367
+ }
3368
+ }), [
3369
+ setState
3370
+ ]);
3371
+ return [
3372
+ state2,
3373
+ observer
3374
+ ];
3375
+ };
3376
+
3377
+ // packages/ui/react-ui-editor/src/extensions/markdown/action.ts
3378
+ var processAction = (view, action) => {
3379
+ let inlineType, listType;
3380
+ switch (action.type) {
3381
+ case "heading":
3382
+ setHeading(parseInt(action.data))(view);
3383
+ break;
3384
+ case "strong":
3385
+ case "emphasis":
3386
+ case "strikethrough":
3387
+ case "code":
3388
+ inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
3389
+ (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
3390
+ break;
3391
+ case "list-ordered":
3392
+ case "list-bullet":
3393
+ case "list-task":
3394
+ listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
3395
+ (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
3396
+ break;
3397
+ case "blockquote":
3398
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
3399
+ break;
3400
+ case "codeblock":
3401
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
3402
+ break;
3403
+ case "table":
3404
+ insertTable(view);
3405
+ break;
3406
+ case "link":
3407
+ (action.data === false ? removeLink : addLink())(view);
3408
+ break;
3409
+ case "image":
3410
+ addLink({
3411
+ url: action.data,
3412
+ image: true
3413
+ })(view);
3414
+ break;
3415
+ case "comment":
3416
+ createComment(view);
3417
+ break;
3418
+ }
3419
+ requestAnimationFrame(() => {
3420
+ if (!view.hasFocus) {
3421
+ view.focus();
3422
+ }
3423
+ });
3424
+ };
3425
+
3426
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3427
+ import { completionKeymap as completionKeymap2 } from "@codemirror/autocomplete";
3428
+ import { defaultKeymap as defaultKeymap2, indentWithTab as indentWithTab2 } from "@codemirror/commands";
3429
+ import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
3430
+ import { defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
3431
+ import { languages } from "@codemirror/language-data";
3432
+ import { lintKeymap } from "@codemirror/lint";
3433
+ import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
3434
+ import { keymap as keymap7 } from "@codemirror/view";
3435
+
3436
+ // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
3437
+ import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
3438
+ import { HighlightStyle } from "@codemirror/language";
3439
+ import { tags, styleTags, Tag } from "@lezer/highlight";
3440
+ import { Table } from "@lezer/markdown";
3441
+ var markdownTags = {
3442
+ Blockquote: Tag.define(),
3443
+ CodeMark: Tag.define(),
3444
+ CodeText: Tag.define(),
3445
+ EmphasisMark: Tag.define(),
3446
+ HeaderMark: Tag.define(),
3447
+ InlineCode: Tag.define(),
3448
+ LinkLabel: Tag.define(),
3449
+ LinkReference: Tag.define(),
3450
+ ListMark: Tag.define(),
3451
+ QuoteMark: Tag.define(),
3452
+ URL: Tag.define(),
3453
+ // Custom.
3454
+ TableCell: Tag.define()
3455
+ };
3456
+ Table.defineNodes?.forEach((node) => {
3457
+ switch (node?.name) {
3458
+ case "TableCell": {
3459
+ node.style = markdownTags.TableCell;
3460
+ break;
3461
+ }
3462
+ }
3463
+ });
3464
+ var markdownTagsExtensions = [
3465
+ Table,
3466
+ {
3467
+ props: [
3468
+ styleTags(markdownTags)
3469
+ ]
3470
+ }
3471
+ ];
3472
+ var markdownHighlightStyle = (readonly) => {
3473
+ return HighlightStyle.define([
3474
+ {
3475
+ tag: [
3476
+ tags.keyword,
3477
+ tags.name,
3478
+ tags.deleted,
3479
+ tags.character,
3480
+ tags.propertyName,
3481
+ tags.macroName,
3482
+ tags.color,
3483
+ tags.constant(tags.name),
3484
+ tags.standard(tags.name),
3485
+ tags.definition(tags.name),
3486
+ tags.separator,
3487
+ tags.typeName,
3488
+ tags.className,
3489
+ tags.number,
3490
+ tags.changed,
3491
+ tags.annotation,
3492
+ tags.modifier,
3493
+ tags.self,
3494
+ tags.namespace,
3495
+ tags.operator,
3496
+ tags.operatorKeyword,
3497
+ tags.escape,
3498
+ tags.regexp,
3499
+ tags.special(tags.string),
3500
+ tags.meta,
3501
+ tags.comment,
3502
+ tags.atom,
3503
+ tags.bool,
3504
+ tags.special(tags.variableName),
3505
+ tags.processingInstruction,
3506
+ tags.string,
3507
+ tags.inserted,
3508
+ tags.invalid
3509
+ ],
3510
+ color: "inherit !important"
3511
+ },
3512
+ // Markdown marks.
3513
+ {
3514
+ tag: [
3515
+ tags.meta,
3516
+ tags.processingInstruction,
3517
+ markdownTags.LinkLabel,
3518
+ markdownTags.LinkReference,
3519
+ markdownTags.ListMark
3520
+ ],
3521
+ class: mark
3522
+ },
3523
+ // Markdown marks.
3524
+ {
3525
+ tag: [
3526
+ markdownTags.CodeMark,
3527
+ markdownTags.HeaderMark,
3528
+ markdownTags.QuoteMark,
3529
+ markdownTags.EmphasisMark
3530
+ ],
3531
+ class: mark
3532
+ },
3533
+ // E.g., code block language (after ```).
3534
+ {
3535
+ tag: [
3536
+ tags.function(tags.variableName),
3537
+ tags.labelName
3538
+ ],
3539
+ class: codeMark
3540
+ },
3541
+ {
3542
+ tag: [
3543
+ tags.monospace
3544
+ ],
3545
+ class: "font-mono"
3546
+ },
3547
+ // Headings.
3548
+ {
3549
+ tag: tags.heading1,
3550
+ class: heading(1)
3551
+ },
3552
+ {
3553
+ tag: tags.heading2,
3554
+ class: heading(2)
3555
+ },
3556
+ {
3557
+ tag: tags.heading3,
3558
+ class: heading(3)
3559
+ },
3560
+ {
3561
+ tag: tags.heading4,
3562
+ class: heading(4)
3563
+ },
3564
+ {
3565
+ tag: tags.heading5,
3566
+ class: heading(5)
3567
+ },
3568
+ {
3569
+ tag: tags.heading6,
3570
+ class: heading(6)
3571
+ },
3572
+ // Emphasis.
3573
+ {
3574
+ tag: tags.emphasis,
3575
+ class: italic
3576
+ },
3577
+ {
3578
+ tag: tags.strong,
3579
+ class: bold
3580
+ },
3581
+ {
3582
+ tag: tags.strikethrough,
3583
+ class: strikethrough
3584
+ },
3585
+ // Naked URLs.
3586
+ {
3587
+ tag: [
3588
+ markdownTags.URL
3589
+ ],
3590
+ class: inlineUrl
3591
+ },
3592
+ // NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
3593
+ // However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
3594
+ // when a language is specified. In this case, the syntax highlighting extensions will colorize
3595
+ // the code, but all other CSS properties will be inherited.
3596
+ // IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
3597
+ {
3598
+ tag: [
3599
+ markdownTags.CodeText,
3600
+ markdownTags.InlineCode
3601
+ ],
3602
+ class: code
3603
+ },
3604
+ {
3605
+ tag: [
3606
+ markdownTags.QuoteMark
3607
+ ],
3608
+ class: blockquote
3609
+ },
3610
+ {
3611
+ tag: [
3612
+ markdownTags.TableCell
3613
+ ],
3614
+ class: "font-mono"
3615
+ }
3616
+ ], {
3617
+ scope: markdownLanguage2,
3618
+ all: {
3619
+ fontFamily: getToken("fontFamily.body", []).join(",")
3620
+ }
3621
+ });
3622
+ };
3623
+
3624
+ // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
3625
+ var createMarkdownExtensions = ({ themeMode } = {}) => {
3626
+ return [
3627
+ // Main extension.
3628
+ // https://github.com/codemirror/lang-markdown
3629
+ // https://codemirror.net/5/mode/markdown/index.html (demo).
3630
+ markdown({
3631
+ // GRM by default (vs strict CommonMark):
3632
+ // Table, TaskList, Strikethrough, and Autolink.
3633
+ // NOTE: This extends the parser; it doesn't affect rendering.
3634
+ // https://github.github.com/gfm
3635
+ // https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
3636
+ base: markdownLanguage3,
3637
+ // Languages for syntax highlighting fenced code blocks.
3638
+ codeLanguages: languages,
3639
+ // Parser extensions.
3640
+ extensions: [
3641
+ // GFM provided by default.
3642
+ markdownTagsExtensions
3643
+ ]
3644
+ }),
3645
+ // https://github.com/codemirror/theme-one-dark
3646
+ themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
3647
+ // Custom styles.
3648
+ syntaxHighlighting(markdownHighlightStyle()),
3649
+ keymap7.of([
3650
+ // https://codemirror.net/docs/ref/#commands.indentWithTab
3651
+ indentWithTab2,
3652
+ // https://codemirror.net/docs/ref/#commands.defaultKeymap
3653
+ ...defaultKeymap2,
3654
+ ...completionKeymap2,
3655
+ ...lintKeymap
3656
+ ])
3657
+ ];
3658
+ };
3659
+
3660
+ // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
3661
+ import { syntaxTree as syntaxTree2 } from "@codemirror/language";
3662
+ import { RangeSetBuilder as RangeSetBuilder2, StateEffect as StateEffect4 } from "@codemirror/state";
3663
+ import { EditorView as EditorView12, Decoration as Decoration5, WidgetType as WidgetType3, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
3664
+ import { mx as mx2 } from "@dxos/react-ui-theme";
3665
+ var HorizontalRuleWidget = class extends WidgetType3 {
3666
+ toDOM() {
3667
+ const el = document.createElement("span");
3668
+ el.className = "cm-hr";
3669
+ return el;
3670
+ }
3671
+ };
3672
+ var LinkButton = class extends WidgetType3 {
3673
+ constructor(url, render) {
3674
+ super();
3675
+ this.url = url;
3676
+ this.render = render;
3677
+ }
3678
+ eq(other) {
3679
+ return this.url === other.url;
3680
+ }
3681
+ toDOM(view) {
3682
+ const el = document.createElement("span");
3683
+ this.render(el, this.url);
3684
+ return el;
3625
3685
  }
3626
- const changes = ranges.map(({ from, to }) => {
3627
- const column = from - state2.doc.lineAt(from).from;
3628
- return [
3629
- {
3630
- from,
3631
- insert: "```\n" + " ".repeat(column)
3632
- },
3633
- {
3634
- from: to,
3635
- insert: "\n" + " ".repeat(column) + "```"
3636
- }
3637
- ];
3638
- });
3639
- dispatch(state2.update({
3640
- changes,
3641
- userEvent: "format.codeblock.add",
3642
- scrollIntoView: true
3643
- }));
3644
- return true;
3645
3686
  };
3646
- var removeCodeblock = ({ state: state2, dispatch }) => {
3647
- const changes = [];
3648
- let lastBlock = -1;
3649
- for (const { from, to } of state2.selection.ranges) {
3650
- syntaxTree2(state2).iterate({
3651
- from,
3652
- to,
3653
- enter: (node) => {
3654
- if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
3655
- lastBlock = node.from;
3656
- const firstLine = state2.doc.lineAt(node.from);
3657
- if (node.name === "FencedCode") {
3658
- changes.push({
3659
- from: node.from,
3660
- to: firstLine.to + 1 + node.from - firstLine.from
3661
- });
3662
- const lastLine = state2.doc.lineAt(node.to);
3663
- if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
3664
- changes.push({
3665
- from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
3666
- to: lastLine.to
3667
- });
3668
- }
3669
- } else {
3670
- const column = node.from - firstLine.from;
3671
- for (let line = firstLine; ; line = state2.doc.line(line.number + 1)) {
3672
- changes.push({
3673
- from: line.from + column - 4,
3674
- to: line.from + column
3675
- });
3676
- if (line.to >= node.to) {
3677
- break;
3678
- }
3687
+ var CheckboxWidget = class extends WidgetType3 {
3688
+ constructor(_checked) {
3689
+ super();
3690
+ this._checked = _checked;
3691
+ }
3692
+ eq(other) {
3693
+ return this._checked === other._checked;
3694
+ }
3695
+ toDOM(view) {
3696
+ const input = document.createElement("input");
3697
+ input.className = "cm-task-checkbox ch-checkbox ch-focus-ring -mbs-0.5";
3698
+ input.type = "checkbox";
3699
+ input.checked = this._checked;
3700
+ if (view.state.readOnly) {
3701
+ input.setAttribute("disabled", "true");
3702
+ } else {
3703
+ input.onmousedown = (event) => {
3704
+ const pos = view.posAtDOM(input);
3705
+ const text = view.state.sliceDoc(pos, pos + 3);
3706
+ if (text === (this._checked ? "[x]" : "[ ]")) {
3707
+ view.dispatch({
3708
+ changes: {
3709
+ from: pos + 1,
3710
+ to: pos + 2,
3711
+ insert: this._checked ? " " : "x"
3679
3712
  }
3680
- }
3713
+ });
3714
+ event.preventDefault();
3681
3715
  }
3682
- }
3683
- });
3716
+ };
3717
+ }
3718
+ return input;
3684
3719
  }
3685
- if (!changes.length) {
3720
+ ignoreEvent() {
3686
3721
  return false;
3687
3722
  }
3688
- dispatch(state2.update({
3689
- changes,
3690
- userEvent: "format.codeblock.remove",
3691
- scrollIntoView: true
3692
- }));
3693
- return true;
3694
- };
3695
- var toggleCodeblock = (target) => {
3696
- return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
3697
- };
3698
- var formattingKeymap = (options = {}) => {
3699
- return [
3700
- keymap7.of([
3701
- {
3702
- key: "meta-b",
3703
- run: toggleStrong
3704
- }
3705
- ])
3706
- ];
3707
3723
  };
3708
- var InlineMarker = {
3709
- Emphasis: 1,
3710
- StrongEmphasis: 0,
3711
- InlineCode: 3,
3712
- Strikethrough: 2
3724
+ var hide = Decoration5.replace({});
3725
+ var fencedCodeLine = Decoration5.line({
3726
+ class: mx2("cm-code cm-codeblock-line")
3727
+ });
3728
+ var fencedCodeLineFirst = Decoration5.line({
3729
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-first")
3730
+ });
3731
+ var fencedCodeLineLast = Decoration5.line({
3732
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-last")
3733
+ });
3734
+ var horizontalRule = Decoration5.replace({
3735
+ widget: new HorizontalRuleWidget()
3736
+ });
3737
+ var checkedTask = Decoration5.replace({
3738
+ widget: new CheckboxWidget(true)
3739
+ });
3740
+ var uncheckedTask = Decoration5.replace({
3741
+ widget: new CheckboxWidget(false)
3742
+ });
3743
+ var editingRange = (state2, range, focus) => {
3744
+ const { readOnly, selection: { main: { head } } } = state2;
3745
+ return focus && !readOnly && head >= range.from && head <= range.to;
3713
3746
  };
3714
- var IgnoreInline = /* @__PURE__ */ new Set([
3715
- "Autolink",
3747
+ var MarksByParent = /* @__PURE__ */ new Set([
3716
3748
  "CodeMark",
3717
- "CodeText",
3718
- "Comment",
3719
3749
  "EmphasisMark",
3720
- "Hardbreak",
3721
- "HeaderMark",
3722
- "HTMLTag",
3723
- "LinkMark",
3724
- "ListMark",
3725
- "ProcessingInstruction",
3726
- "QuoteMark",
3727
- "StrikethroughMark",
3728
- "SubscriptMark",
3729
- "SuperscriptMark",
3730
- "TaskMarker"
3731
- ]);
3732
- var Textblocks = {
3733
- ATXHeading1: "heading1",
3734
- ATXHeading2: "heading2",
3735
- ATXHeading3: "heading3",
3736
- ATXHeading4: "heading4",
3737
- ATXHeading5: "heading5",
3738
- ATXHeading6: "heading6",
3739
- CodeBlock: "codeblock",
3740
- FencedCode: "codeblock",
3741
- Paragraph: "paragraph",
3742
- SetextHeading1: "heading1",
3743
- SetextHeading2: "heading2",
3744
- TableCell: "tablecell",
3745
- Task: "paragraph"
3746
- };
3747
- var getFormatting = (state2) => {
3748
- let blockType = null;
3749
- const inline = [
3750
- null,
3751
- null,
3752
- null,
3753
- null
3754
- ];
3755
- let link = false;
3756
- let blockQuote = null;
3757
- let listStyle = null;
3758
- const stack = [];
3759
- let currentBlock = null;
3760
- const advanceInline = (upto) => {
3761
- if (!currentBlock) {
3762
- return;
3763
- }
3764
- upto = Math.min(upto, currentBlock.end);
3765
- if (upto <= currentBlock.pos) {
3766
- return;
3767
- }
3768
- for (let i = 0; i < currentBlock.active.length; i++) {
3769
- if (inline[i] === false) {
3770
- continue;
3771
- } else if (currentBlock.active[i]) {
3772
- inline[i] = true;
3773
- } else if (/\S/.test(state2.doc.sliceString(currentBlock.pos, upto))) {
3774
- inline[i] = false;
3775
- }
3776
- }
3777
- currentBlock.pos = upto;
3778
- };
3779
- const skipInline = (upto) => {
3780
- if (currentBlock && upto > currentBlock.pos) {
3781
- currentBlock.pos = Math.min(upto, currentBlock.end);
3782
- }
3783
- };
3784
- const { selection } = state2;
3785
- for (const range of selection.ranges) {
3786
- if (range.empty && inline.some((v) => v === null)) {
3787
- const contextSize = Math.min(range.head, 6);
3788
- const contextBefore = state2.doc.sliceString(range.head - contextSize, range.head);
3789
- let contextAfter = state2.doc.sliceString(range.head, range.head + contextSize);
3790
- for (let i = 0; i < contextSize; i++) {
3791
- const ch = contextAfter[i];
3792
- if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
3793
- contextAfter = contextAfter.slice(0, i);
3794
- break;
3795
- }
3796
- }
3797
- for (let i = 0; i < inline.length; i++) {
3798
- const mark2 = inlineMarkerText(i);
3799
- const found = contextAfter.indexOf(mark2);
3800
- if (found > -1) {
3801
- contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
3802
- if (inline[i] === null) {
3803
- inline[i] = true;
3804
- }
3805
- }
3806
- }
3807
- }
3808
- syntaxTree2(state2).iterate({
3809
- from: range.from,
3810
- to: range.to,
3811
- enter: (node) => {
3812
- advanceInline(node.from);
3813
- const { name } = node;
3814
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3815
- stack.push(name);
3816
- } else if (name === "Link") {
3817
- link = true;
3818
- } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3819
- if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3820
- stack[stack.length - 1] = "TaskList";
3821
- }
3822
- const blockCode = Textblocks[name];
3823
- if (blockType === null) {
3824
- blockType = blockCode;
3825
- } else if (blockType !== blockCode) {
3826
- blockType = false;
3750
+ "StrikethroughMark",
3751
+ "SubscriptMark",
3752
+ "SuperscriptMark"
3753
+ ]);
3754
+ var buildDecorations = (view, options, focus) => {
3755
+ const deco = new RangeSetBuilder2();
3756
+ const atomicDeco = new RangeSetBuilder2();
3757
+ const { state: state2 } = view;
3758
+ for (const { from, to } of view.visibleRanges) {
3759
+ syntaxTree2(state2).iterate({
3760
+ from,
3761
+ to,
3762
+ enter: (node) => {
3763
+ if (node.name === "FencedCode") {
3764
+ const editing = editingRange(state2, node, focus);
3765
+ for (const block of view.viewportLineBlocks) {
3766
+ if (block.to < node.from) {
3767
+ continue;
3768
+ }
3769
+ if (block.from > node.to) {
3770
+ break;
3771
+ }
3772
+ const first = block.from <= node.from;
3773
+ const last = block.to >= node.to && /^(\s>)*```$/.test(state2.doc.sliceString(block.from, block.to));
3774
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
3775
+ if (!editing && (first || last)) {
3776
+ atomicDeco.add(block.from, block.to, hide);
3777
+ }
3827
3778
  }
3828
- if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
3829
- currentBlock = {
3830
- pos: Math.max(range.from, node.from),
3831
- end: Math.min(range.to, node.to),
3832
- active: [
3833
- false,
3834
- false,
3835
- false,
3836
- false
3837
- ]
3838
- };
3779
+ return false;
3780
+ } else if (node.name === "Link") {
3781
+ const marks = node.node.getChildren("LinkMark");
3782
+ const urlNode = node.node.getChild("URL");
3783
+ const editing = editingRange(state2, node, focus);
3784
+ if (urlNode && marks.length >= 2) {
3785
+ const url = state2.sliceDoc(urlNode.from, urlNode.to);
3786
+ if (!editing) {
3787
+ atomicDeco.add(node.from, marks[0].to, hide);
3788
+ }
3789
+ deco.add(marks[0].to, marks[1].from, Decoration5.mark({
3790
+ tagName: "a",
3791
+ attributes: {
3792
+ class: "cm-link",
3793
+ href: url,
3794
+ rel: "noreferrer",
3795
+ target: "_blank"
3796
+ }
3797
+ }));
3798
+ if (!editing) {
3799
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration5.replace({
3800
+ widget: new LinkButton(url, options.renderLinkButton)
3801
+ }) : hide);
3802
+ }
3839
3803
  }
3840
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3841
- const index = InlineMarker[name];
3842
- if (range.empty && inline[index] === null) {
3843
- inline[index] = true;
3804
+ } else if (node.name === "HeaderMark") {
3805
+ const parent = node.node.parent;
3806
+ if (/^ATX/.test(parent.name) && !editingRange(state2, state2.doc.lineAt(node.from), focus)) {
3807
+ const next = state2.doc.sliceString(node.to, node.to + 1);
3808
+ atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
3844
3809
  }
3845
- currentBlock.active[index] = true;
3846
- } else if (IgnoreInline.has(name)) {
3847
- skipInline(node.to);
3848
- }
3849
- },
3850
- leave: (node) => {
3851
- advanceInline(node.to);
3852
- const { name } = node;
3853
- if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3854
- stack.pop();
3855
- } else if (Object.hasOwn(Textblocks, name)) {
3856
- let hasList = false;
3857
- let hasQuote = false;
3858
- for (let i = stack.length - 1; i >= 0; i--) {
3859
- if (stack[i] === "Blockquote") {
3860
- hasQuote = true;
3861
- } else if (!hasList) {
3862
- hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
3863
- }
3810
+ } else if (node.name === "HorizontalRule") {
3811
+ if (!editingRange(state2, node, focus)) {
3812
+ deco.add(node.from, node.to, horizontalRule);
3864
3813
  }
3865
- if (blockQuote === null) {
3866
- blockQuote = hasQuote;
3867
- } else if (!hasQuote && blockQuote) {
3868
- blockQuote = false;
3814
+ } else if (node.name === "TaskMarker") {
3815
+ if (!editingRange(state2, node, focus)) {
3816
+ const checked = state2.doc.sliceString(node.from + 1, node.to - 1) === "x";
3817
+ atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
3818
+ class: "cm-task"
3819
+ }));
3820
+ atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
3869
3821
  }
3870
- if (listStyle === null) {
3871
- listStyle = hasList;
3872
- } else if (listStyle !== hasList) {
3873
- listStyle = false;
3822
+ } else if (MarksByParent.has(node.name)) {
3823
+ if (!editingRange(state2, node.node.parent, focus)) {
3824
+ atomicDeco.add(node.from, node.to, hide);
3874
3825
  }
3875
- currentBlock = null;
3876
- } else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
3877
- currentBlock.active[InlineMarker[name]] = false;
3878
3826
  }
3879
3827
  }
3880
3828
  });
3881
3829
  }
3882
- const { from, to } = state2.doc.lineAt(selection.main.anchor);
3883
- const blankLine = from === to;
3884
3830
  return {
3885
- blankLine,
3886
- blockType: blockType || null,
3887
- blockQuote: blockQuote ?? false,
3888
- code: inline[3] ?? false,
3889
- emphasis: inline[1] ?? false,
3890
- strong: inline[0] ?? false,
3891
- strikethrough: inline[2] ?? false,
3892
- link,
3893
- listStyle: listStyle || null
3831
+ deco: deco.finish(),
3832
+ atomicDeco: atomicDeco.finish()
3894
3833
  };
3895
3834
  };
3896
- var useFormattingState = () => {
3897
- const [state2, setState] = useState();
3898
- const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
3899
- if (update2.docChanged || update2.selectionSet) {
3900
- setState((prevState) => {
3901
- const newState = getFormatting(update2.state);
3902
- if (!prevState || !formattingEquals(prevState, newState)) {
3903
- return newState;
3904
- }
3905
- return prevState;
3906
- });
3907
- }
3908
- }), [
3909
- setState
3910
- ]);
3835
+ var forceUpdate = StateEffect4.define();
3836
+ var decorateMarkdown = (options = {}) => {
3911
3837
  return [
3912
- state2,
3913
- observer
3838
+ ViewPlugin4.fromClass(class {
3839
+ constructor(view) {
3840
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(view, options, view.hasFocus));
3841
+ }
3842
+ update(update2) {
3843
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((e) => e.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
3844
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations(update2.view, options, update2.view.hasFocus));
3845
+ this.clearUpdate();
3846
+ } else if (update2.selectionSet) {
3847
+ this.scheduleUpdate(update2.view);
3848
+ }
3849
+ }
3850
+ // TODO(burdon): BUG: If the cursor is at the end of a link at the end of a line,
3851
+ // the cursor will float in space or be in the wrong position after the decoration is applied.
3852
+ scheduleUpdate(view) {
3853
+ this.clearUpdate();
3854
+ this.pendingUpdate = setTimeout(() => {
3855
+ view.dispatch({
3856
+ effects: forceUpdate.of(null)
3857
+ });
3858
+ }, options.selectionChangeDelay);
3859
+ }
3860
+ clearUpdate() {
3861
+ if (this.pendingUpdate) {
3862
+ clearTimeout(this.pendingUpdate);
3863
+ this.pendingUpdate = void 0;
3864
+ }
3865
+ }
3866
+ destroy() {
3867
+ this.clearUpdate();
3868
+ }
3869
+ }, {
3870
+ provide: (plugin) => [
3871
+ EditorView12.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
3872
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration5.none),
3873
+ EditorView12.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration5.none)
3874
+ ]
3875
+ }),
3876
+ formattingStyles
3914
3877
  ];
3915
3878
  };
3879
+ var formattingStyles = EditorView12.baseTheme({
3880
+ "& .cm-code": {
3881
+ fontFamily: getToken("fontFamily.mono", []).join(",")
3882
+ },
3883
+ "& .cm-codeblock-line": {
3884
+ paddingInline: "1rem !important"
3885
+ },
3886
+ "& .cm-codeblock-end": {
3887
+ display: "inline-block",
3888
+ width: "100%",
3889
+ position: "relative",
3890
+ "&::after": {
3891
+ position: "absolute",
3892
+ inset: 0,
3893
+ content: '""'
3894
+ }
3895
+ },
3896
+ "& .cm-codeblock-first": {
3897
+ borderTopLeftRadius: ".5rem",
3898
+ borderTopRightRadius: ".5rem"
3899
+ },
3900
+ "& .cm-codeblock-last": {
3901
+ borderBottomLeftRadius: ".5rem",
3902
+ borderBottomRightRadius: ".5rem"
3903
+ },
3904
+ "&light .cm-codeblock-line, &light .cm-activeLine.cm-codeblock-line": {
3905
+ background: getToken("extend.semanticColors.input.light"),
3906
+ mixBlendMode: "darken"
3907
+ },
3908
+ "&dark .cm-codeblock-line, &dark .cm-activeLine.cm-codeblock-line": {
3909
+ background: getToken("extend.semanticColors.input.dark"),
3910
+ mixBlendMode: "lighten"
3911
+ },
3912
+ "& .cm-hr": {
3913
+ display: "inline-block",
3914
+ width: "100%",
3915
+ height: "0",
3916
+ verticalAlign: "middle",
3917
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
3918
+ },
3919
+ "& .cm-task": {
3920
+ color: getToken("extend.colors.blue.500")
3921
+ },
3922
+ "& .cm-task-checkbox": {
3923
+ marginLeft: "4px",
3924
+ marginRight: "4px"
3925
+ }
3926
+ });
3916
3927
 
3917
3928
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3918
3929
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3919
3930
  import { StateField as StateField5 } from "@codemirror/state";
3920
- import { Decoration as Decoration6, EditorView as EditorView14, WidgetType as WidgetType4 } from "@codemirror/view";
3931
+ import { Decoration as Decoration6, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
3921
3932
  var image = (options = {}) => {
3922
3933
  return StateField5.define({
3923
3934
  create: (state2) => {
@@ -3944,7 +3955,7 @@ var image = (options = {}) => {
3944
3955
  add: buildDecorations2(from, to, tr.state)
3945
3956
  });
3946
3957
  },
3947
- provide: (field) => EditorView14.decorations.from(field)
3958
+ provide: (field) => EditorView13.decorations.from(field)
3948
3959
  });
3949
3960
  };
3950
3961
  var preloaded = /* @__PURE__ */ new Set();
@@ -4034,12 +4045,12 @@ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
4034
4045
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4035
4046
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4036
4047
  import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
4037
- import { Decoration as Decoration7, EditorView as EditorView15, WidgetType as WidgetType5 } from "@codemirror/view";
4048
+ import { Decoration as Decoration7, EditorView as EditorView14, WidgetType as WidgetType5 } from "@codemirror/view";
4038
4049
  var table = (options = {}) => {
4039
4050
  return StateField6.define({
4040
4051
  create: (state2) => update(state2, options),
4041
4052
  update: (_, tr) => update(tr.state, options),
4042
- provide: (field) => EditorView15.decorations.from(field)
4053
+ provide: (field) => EditorView14.decorations.from(field)
4043
4054
  });
4044
4055
  };
4045
4056
  var update = (state2, options) => {
@@ -4133,8 +4144,8 @@ var TableWidget = class extends WidgetType5 {
4133
4144
 
4134
4145
  // packages/ui/react-ui-editor/src/extensions/mention.ts
4135
4146
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
4136
- import { log as log8 } from "@dxos/log";
4137
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4147
+ import { log as log6 } from "@dxos/log";
4148
+ var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
4138
4149
  var mention = ({ onSearch }) => {
4139
4150
  return autocompletion2({
4140
4151
  // TODO(burdon): Not working.
@@ -4146,10 +4157,10 @@ var mention = ({ onSearch }) => {
4146
4157
  icons: false,
4147
4158
  override: [
4148
4159
  (context) => {
4149
- log8.info("completion context", {
4160
+ log6.info("completion context", {
4150
4161
  context
4151
4162
  }, {
4152
- F: __dxlog_file10,
4163
+ F: __dxlog_file8,
4153
4164
  L: 26,
4154
4165
  S: void 0,
4155
4166
  C: (f, a) => f(...a)
@@ -4209,18 +4220,18 @@ var EditorModes = {
4209
4220
  };
4210
4221
 
4211
4222
  // packages/ui/react-ui-editor/src/extensions/state.ts
4212
- import { Transaction as Transaction2 } from "@codemirror/state";
4213
- import { EditorView as EditorView16, keymap as keymap9 } from "@codemirror/view";
4223
+ import { Transaction } from "@codemirror/state";
4224
+ import { EditorView as EditorView15, keymap as keymap9 } from "@codemirror/view";
4214
4225
  import { debounce as debounce2 } from "@dxos/async";
4215
4226
  import { invariant as invariant4 } from "@dxos/invariant";
4216
4227
  import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
4217
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4228
+ var __dxlog_file9 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
4218
4229
  var scrollAnnotation = "dxos.org/cm/scrolling";
4219
4230
  var keyPrefix = "dxos.org/react-ui-editor/state";
4220
4231
  var localStorageStateStoreAdapter = {
4221
4232
  setState: (id, state2) => {
4222
4233
  invariant4(id, void 0, {
4223
- F: __dxlog_file11,
4234
+ F: __dxlog_file9,
4224
4235
  L: 35,
4225
4236
  S: void 0,
4226
4237
  A: [
@@ -4232,7 +4243,7 @@ var localStorageStateStoreAdapter = {
4232
4243
  },
4233
4244
  getState: (id) => {
4234
4245
  invariant4(id, void 0, {
4235
- F: __dxlog_file11,
4246
+ F: __dxlog_file9,
4236
4247
  L: 39,
4237
4248
  S: void 0,
4238
4249
  A: [
@@ -4248,7 +4259,7 @@ var state = ({ getState, setState } = {}) => {
4248
4259
  const setStateDebounced = debounce2(setState, 1e3);
4249
4260
  return [
4250
4261
  // TODO(burdon): Track scrolling (currently only updates when cursor moves).
4251
- EditorView16.updateListener.of(({ view, changes, transactions }) => {
4262
+ EditorView15.updateListener.of(({ view, changes, transactions }) => {
4252
4263
  const id = view.state.facet(documentId2);
4253
4264
  if (!id || transactions.some((tr) => tr.isUserEvent(scrollAnnotation))) {
4254
4265
  return;
@@ -4281,11 +4292,11 @@ var state = ({ getState, setState } = {}) => {
4281
4292
  const state2 = getState(view.state.facet(documentId2));
4282
4293
  if (state2) {
4283
4294
  view.dispatch({
4284
- effects: EditorView16.scrollIntoView(state2.scrollTo.from, {
4295
+ effects: EditorView15.scrollIntoView(state2.scrollTo.from, {
4285
4296
  yMargin: 0
4286
4297
  }),
4287
4298
  selection: state2.selection,
4288
- annotations: Transaction2.userEvent.of(scrollAnnotation)
4299
+ annotations: Transaction.userEvent.of(scrollAnnotation)
4289
4300
  });
4290
4301
  }
4291
4302
  return true;
@@ -4353,9 +4364,9 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
4353
4364
  };
4354
4365
 
4355
4366
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
4356
- var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4367
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
4357
4368
  var instanceCount = 0;
4358
- var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView17.scrollIntoView(0, {
4369
+ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView16.scrollIntoView(0, {
4359
4370
  yMargin: 0
4360
4371
  }), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
4361
4372
  const [instanceId] = useState2(() => `text-editor-${++instanceCount}`);
@@ -4376,11 +4387,11 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4376
4387
  autoFocus
4377
4388
  ]);
4378
4389
  useEffect2(() => {
4379
- log9("create", {
4390
+ log7("create", {
4380
4391
  id,
4381
4392
  instanceId
4382
4393
  }, {
4383
- F: __dxlog_file12,
4394
+ F: __dxlog_file10,
4384
4395
  L: 88,
4385
4396
  S: void 0,
4386
4397
  C: (f, a) => f(...a)
@@ -4391,16 +4402,16 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4391
4402
  extensions: [
4392
4403
  id && documentId2.of(id),
4393
4404
  // TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
4394
- EditorView17.exceptionSink.of((err) => {
4395
- log9.catch(err, void 0, {
4396
- F: __dxlog_file12,
4405
+ EditorView16.exceptionSink.of((err) => {
4406
+ log7.catch(err, void 0, {
4407
+ F: __dxlog_file10,
4397
4408
  L: 101,
4398
4409
  S: void 0,
4399
4410
  C: (f, a) => f(...a)
4400
4411
  });
4401
4412
  }),
4402
4413
  // Focus.
4403
- EditorView17.updateListener.of((update2) => {
4414
+ EditorView16.updateListener.of((update2) => {
4404
4415
  update2.transactions.forEach((transaction) => {
4405
4416
  if (transaction.isUserEvent(focusEvent)) {
4406
4417
  rootRef.current?.focus();
@@ -4410,7 +4421,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4410
4421
  extensions
4411
4422
  ].filter(isNotFalsy4)
4412
4423
  });
4413
- const view2 = new EditorView17({
4424
+ const view2 = new EditorView16({
4414
4425
  state: state2,
4415
4426
  parent: rootRef.current,
4416
4427
  scrollTo,
@@ -4436,11 +4447,11 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
4436
4447
  }
4437
4448
  setView(view2);
4438
4449
  return () => {
4439
- log9("destroy", {
4450
+ log7("destroy", {
4440
4451
  id,
4441
4452
  instanceId
4442
4453
  }, {
4443
- F: __dxlog_file12,
4454
+ F: __dxlog_file10,
4444
4455
  L: 150,
4445
4456
  S: void 0,
4446
4457
  C: (f, a) => f(...a)
@@ -4746,7 +4757,7 @@ var MarkdownCustom = ({ onUpload } = {}) => {
4746
4757
  });
4747
4758
  useEffect3(() => {
4748
4759
  if (onUpload && acceptedFiles.length) {
4749
- setTimeout(async () => {
4760
+ requestAnimationFrame(async () => {
4750
4761
  const f = acceptedFiles[0];
4751
4762
  const file = new File([
4752
4763
  f
@@ -4795,56 +4806,7 @@ var Toolbar = {
4795
4806
 
4796
4807
  // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
4797
4808
  var useActionHandler = (view) => {
4798
- return (action) => {
4799
- if (!view) {
4800
- return;
4801
- }
4802
- let inlineType, listType;
4803
- switch (action.type) {
4804
- case "heading":
4805
- setHeading(parseInt(action.data))(view);
4806
- break;
4807
- case "strong":
4808
- case "emphasis":
4809
- case "strikethrough":
4810
- case "code":
4811
- inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
4812
- (typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
4813
- break;
4814
- case "list-ordered":
4815
- case "list-bullet":
4816
- case "list-task":
4817
- listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
4818
- (action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
4819
- break;
4820
- case "blockquote":
4821
- (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
4822
- break;
4823
- case "codeblock":
4824
- (action.data === false ? removeCodeblock : addCodeblock)(view);
4825
- break;
4826
- case "table":
4827
- insertTable(view);
4828
- break;
4829
- case "link":
4830
- (action.data === false ? removeLink : addLink())(view);
4831
- break;
4832
- case "image":
4833
- addLink({
4834
- url: action.data,
4835
- image: true
4836
- })(view);
4837
- break;
4838
- case "comment":
4839
- createComment(view);
4840
- break;
4841
- }
4842
- setTimeout(() => {
4843
- if (!view.hasFocus) {
4844
- view.focus();
4845
- }
4846
- });
4847
- };
4809
+ return (action) => view && processAction(view, action);
4848
4810
  };
4849
4811
 
4850
4812
  // packages/ui/react-ui-editor/src/hooks/useDocAccessor.ts
@@ -4862,11 +4824,11 @@ var useDocAccessor = (text) => {
4862
4824
 
4863
4825
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4864
4826
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4865
- import { EditorView as EditorView18 } from "@codemirror/view";
4827
+ import { EditorView as EditorView17 } from "@codemirror/view";
4866
4828
  import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
4867
- import { log as log10 } from "@dxos/log";
4829
+ import { log as log8 } from "@dxos/log";
4868
4830
  import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
4869
- var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4831
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
4870
4832
  var useTextEditor = (cb = () => ({}), deps = []) => {
4871
4833
  let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
4872
4834
  const onUpdate = useRef3();
@@ -4875,10 +4837,10 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4875
4837
  useEffect4(() => {
4876
4838
  let view2;
4877
4839
  if (parentRef.current) {
4878
- log10("create", {
4840
+ log8("create", {
4879
4841
  id
4880
4842
  }, {
4881
- F: __dxlog_file13,
4843
+ F: __dxlog_file11,
4882
4844
  L: 36,
4883
4845
  S: void 0,
4884
4846
  C: (f, a) => f(...a)
@@ -4889,21 +4851,21 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4889
4851
  extensions: [
4890
4852
  id && documentId2.of(id),
4891
4853
  // TODO(burdon): Doesn't catch errors in keymap functions.
4892
- EditorView18.exceptionSink.of((err) => {
4893
- log10.catch(err, void 0, {
4894
- F: __dxlog_file13,
4854
+ EditorView17.exceptionSink.of((err) => {
4855
+ log8.catch(err, void 0, {
4856
+ F: __dxlog_file11,
4895
4857
  L: 46,
4896
4858
  S: void 0,
4897
4859
  C: (f, a) => f(...a)
4898
4860
  });
4899
4861
  }),
4900
4862
  extensions,
4901
- EditorView18.updateListener.of(() => {
4863
+ EditorView17.updateListener.of(() => {
4902
4864
  onUpdate.current?.();
4903
4865
  })
4904
4866
  ].filter(isNotFalsy5)
4905
4867
  });
4906
- view2 = new EditorView18({
4868
+ view2 = new EditorView17({
4907
4869
  parent: parentRef.current,
4908
4870
  scrollTo,
4909
4871
  state: state2,
@@ -4919,10 +4881,10 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
4919
4881
  setView(view2);
4920
4882
  }
4921
4883
  return () => {
4922
- log10("destroy", {
4884
+ log8("destroy", {
4923
4885
  id
4924
4886
  }, {
4925
- F: __dxlog_file13,
4887
+ F: __dxlog_file11,
4926
4888
  L: 74,
4927
4889
  S: void 0,
4928
4890
  C: (f, a) => f(...a)
@@ -4994,12 +4956,11 @@ export {
4994
4956
  createDataExtensions,
4995
4957
  createMarkdownExtensions,
4996
4958
  createThemeExtensions,
4997
- cursorLineMargin,
4998
4959
  decorateMarkdown,
4999
4960
  defaultOptions,
5000
4961
  defaultTheme,
5001
- dnd,
5002
4962
  documentId2 as documentId,
4963
+ dropFile,
5003
4964
  editorFillLayoutEditor,
5004
4965
  editorFillLayoutRoot,
5005
4966
  editorMode,
@@ -5022,6 +4983,7 @@ export {
5022
4983
  markdownTags,
5023
4984
  markdownTagsExtensions,
5024
4985
  mention,
4986
+ processAction,
5025
4987
  removeBlockquote,
5026
4988
  removeCodeblock,
5027
4989
  removeLink,