@dxos/react-ui-editor 0.4.8-main.457648 → 0.4.8-main.483207a

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