@dxos/react-ui-editor 0.4.4-next.e0df51e → 0.4.5-main.181e1ed

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 (30) hide show
  1. package/dist/lib/browser/index.mjs +201 -92
  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/MarkdownEditor.stories.d.ts.map +1 -1
  5. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
  7. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
  8. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  9. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  10. package/dist/types/src/extensions/cursor-line-margin.d.ts +8 -0
  11. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +1 -0
  12. package/dist/types/src/extensions/index.d.ts +1 -0
  13. package/dist/types/src/extensions/index.d.ts.map +1 -1
  14. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  15. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  16. package/dist/types/src/styles/layout.d.ts +2 -2
  17. package/dist/types/src/styles/layout.d.ts.map +1 -1
  18. package/package.json +31 -28
  19. package/src/components/TextEditor/MarkdownEditor.stories.tsx +6 -0
  20. package/src/components/TextEditor/TextEditor.tsx +18 -5
  21. package/src/components/TextEditor/automerge.stories.tsx +9 -6
  22. package/src/components/TextEditor/hooks.stories.tsx +47 -17
  23. package/src/components/Toolbar/Toolbar.stories.tsx +2 -1
  24. package/src/components/Toolbar/Toolbar.tsx +0 -1
  25. package/src/extensions/cursor-line-margin.ts +92 -0
  26. package/src/extensions/index.ts +1 -0
  27. package/src/extensions/markdown/formatting.test.ts +17 -0
  28. package/src/extensions/markdown/formatting.ts +66 -12
  29. package/src/hooks/useTextEditor.ts +6 -2
  30. package/src/styles/layout.ts +3 -2
@@ -8,10 +8,10 @@ import { tags as tags2 } from "@lezer/highlight";
8
8
 
9
9
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
10
10
  import { EditorState as EditorState2 } from "@codemirror/state";
11
- import { EditorView as EditorView15 } from "@codemirror/view";
11
+ import { EditorView as EditorView16 } from "@codemirror/view";
12
12
  import { useFocusableGroup } from "@fluentui/react-tabster";
13
13
  import defaultsDeep2 from "lodash.defaultsdeep";
14
- import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2 } from "react";
14
+ import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2, useMemo as useMemo2 } from "react";
15
15
  import { log as log3 } from "@dxos/log";
16
16
  import { useThemeContext } from "@dxos/react-ui";
17
17
  import { focusRing } from "@dxos/react-ui-theme";
@@ -102,8 +102,8 @@ var annotations = (options = {}) => {
102
102
  annotationsState
103
103
  ], (state) => {
104
104
  const annotations2 = state.field(annotationsState);
105
- const decorations = annotations2.map((annotation) => {
106
- const range = Cursor.getRangeFromCursor(state, annotation.cursor);
105
+ const decorations = annotations2.map((annotation2) => {
106
+ const range = Cursor.getRangeFromCursor(state, annotation2.cursor);
107
107
  return range && annotationMark.range(range.from, range.to);
108
108
  }).filter(isNotFalsy);
109
109
  return Decoration.set(decorations);
@@ -1116,8 +1116,8 @@ var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue)
1116
1116
 
1117
1117
  // packages/ui/react-ui-editor/src/styles/layout.ts
1118
1118
  var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
1119
- var editorFillLayoutRoot = "min-bs-full grid";
1120
- var editorFillLayoutEditor = "min-bs-full";
1119
+ var editorFillLayoutRoot = "bs-full overflow-hidden grid";
1120
+ var editorFillLayoutEditor = "bs-full overflow-hidden [&>.cm-scroller]:bs-full [&>.cm-scroller]:overflow-y-auto";
1121
1121
  var editorHalfViewportOverscrollContent = "after:block after:min-bs-[50dvh] after:pointer-events-none";
1122
1122
 
1123
1123
  // packages/ui/react-ui-editor/src/util.ts
@@ -1553,15 +1553,66 @@ var useComments = (view, id, comments2 = []) => {
1553
1553
  ]);
1554
1554
  };
1555
1555
 
1556
- // packages/ui/react-ui-editor/src/extensions/listener.ts
1556
+ // packages/ui/react-ui-editor/src/extensions/cursor-line-margin.ts
1557
+ import { Facet as Facet4, Transaction } from "@codemirror/state";
1557
1558
  import { EditorView as EditorView7 } from "@codemirror/view";
1559
+ var cursorLineMarginFacet = Facet4.define({
1560
+ combine: (input) => input[0] ?? 3
1561
+ });
1562
+ var annotation = "cursorLineMargin";
1563
+ var lineAtPos = (s, pos) => s.doc.lineAt(pos).number;
1564
+ var cursorLineMargin = EditorView7.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
1565
+ if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
1566
+ return;
1567
+ }
1568
+ const s = state;
1569
+ const { main } = s.selection;
1570
+ const cursorLineMargin2 = s.facet(cursorLineMarginFacet);
1571
+ const rect = view.dom.getBoundingClientRect();
1572
+ const viewportTop = rect.top - view.documentTop;
1573
+ const viewportBottom = rect.bottom - view.documentTop;
1574
+ const mainLine = lineAtPos(s, main.head);
1575
+ const _visTopLine = lineAtPos(s, view.lineBlockAtHeight(viewportTop).from);
1576
+ const botLineBlk = view.lineBlockAtHeight(viewportBottom);
1577
+ const visBotLineDoc = lineAtPos(s, Math.min(botLineBlk.to, s.doc.length));
1578
+ const visBotLine = botLineBlk.bottom >= viewportBottom ? visBotLineDoc : visBotLineDoc + Math.floor((viewportBottom - botLineBlk.bottom) / view.defaultLineHeight);
1579
+ const needsScrollTop = false;
1580
+ const needsScrollBot = mainLine >= visBotLine - cursorLineMargin2;
1581
+ if (needsScrollTop && needsScrollBot) {
1582
+ console.warn("is cursorScrollMargin too large for the editor size?");
1583
+ return;
1584
+ }
1585
+ if (!needsScrollTop && !needsScrollBot) {
1586
+ return;
1587
+ }
1588
+ if (selectionSet) {
1589
+ const { head } = startState.selection.main;
1590
+ const oldMainLine = lineAtPos(startState, head);
1591
+ if (needsScrollTop && oldMainLine < mainLine) {
1592
+ return;
1593
+ }
1594
+ if (needsScrollBot && oldMainLine > mainLine) {
1595
+ return;
1596
+ }
1597
+ }
1598
+ view.dispatch({
1599
+ annotations: Transaction.userEvent.of(annotation),
1600
+ effects: EditorView7.scrollIntoView(s.selection.main.head, {
1601
+ y: needsScrollTop ? "start" : "end",
1602
+ yMargin: view.defaultLineHeight * cursorLineMargin2
1603
+ })
1604
+ });
1605
+ });
1606
+
1607
+ // packages/ui/react-ui-editor/src/extensions/listener.ts
1608
+ import { EditorView as EditorView8 } from "@codemirror/view";
1558
1609
  var listener = ({ onFocus, onChange }) => {
1559
1610
  const extensions = [];
1560
- onFocus && extensions.push(EditorView7.focusChangeEffect.of((_, focused) => {
1611
+ onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
1561
1612
  onFocus(focused);
1562
1613
  return null;
1563
1614
  }));
1564
- onChange && extensions.push(EditorView7.updateListener.of((update2) => {
1615
+ onChange && extensions.push(EditorView8.updateListener.of((update2) => {
1565
1616
  onChange(update2.state.doc.toString());
1566
1617
  }));
1567
1618
  return extensions;
@@ -1576,7 +1627,7 @@ import { languages } from "@codemirror/language-data";
1576
1627
  import { searchKeymap } from "@codemirror/search";
1577
1628
  import { EditorState } from "@codemirror/state";
1578
1629
  import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
1579
- import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView8, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1630
+ import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
1580
1631
  import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
1581
1632
 
1582
1633
  // packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
@@ -1770,7 +1821,7 @@ var markdownHighlightStyle = (readonly) => {
1770
1821
  // packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
1771
1822
  var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
1772
1823
  return [
1773
- lineWrapping && EditorView8.lineWrapping,
1824
+ lineWrapping && EditorView9.lineWrapping,
1774
1825
  EditorState.allowMultipleSelections.of(true),
1775
1826
  EditorState.tabSize.of(2),
1776
1827
  // https://github.com/codemirror/basic-setup
@@ -1823,9 +1874,9 @@ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrap
1823
1874
  // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
1824
1875
  import { syntaxTree } from "@codemirror/language";
1825
1876
  import { RangeSetBuilder } from "@codemirror/state";
1826
- import { ViewPlugin as ViewPlugin3, EditorView as EditorView9, Decoration as Decoration4, WidgetType as WidgetType2 } from "@codemirror/view";
1877
+ import { ViewPlugin as ViewPlugin3, EditorView as EditorView10, Decoration as Decoration4, WidgetType as WidgetType2 } from "@codemirror/view";
1827
1878
  import { mx as mx2 } from "@dxos/react-ui-theme";
1828
- var styles4 = EditorView9.baseTheme({
1879
+ var styles4 = EditorView10.baseTheme({
1829
1880
  "& .cm-code": {
1830
1881
  fontFamily: getToken("fontFamily.mono", []).join(",")
1831
1882
  },
@@ -1948,7 +1999,7 @@ var code2 = () => {
1948
1999
  import { snippet } from "@codemirror/autocomplete";
1949
2000
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1950
2001
  import { RangeSetBuilder as RangeSetBuilder2, EditorSelection } from "@codemirror/state";
1951
- import { Decoration as Decoration5, EditorView as EditorView10, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2002
+ import { Decoration as Decoration5, EditorView as EditorView11, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1952
2003
  import { useState, useMemo } from "react";
1953
2004
  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;
1954
2005
  var Inline;
@@ -1969,6 +2020,7 @@ var setHeading = (level) => ({ state, dispatch }) => {
1969
2020
  const changes = [];
1970
2021
  let prevBlock = -1;
1971
2022
  for (const range of ranges) {
2023
+ let sawBlock = false;
1972
2024
  syntaxTree2(state).iterate({
1973
2025
  from: range.from,
1974
2026
  to: range.to,
@@ -1976,6 +2028,7 @@ var setHeading = (level) => ({ state, dispatch }) => {
1976
2028
  if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
1977
2029
  return;
1978
2030
  }
2031
+ sawBlock = true;
1979
2032
  prevBlock = node.from;
1980
2033
  const blockType = Textblocks[node.name];
1981
2034
  const isHeading = /heading(\d)/.exec(blockType);
@@ -2020,12 +2073,22 @@ var setHeading = (level) => ({ state, dispatch }) => {
2020
2073
  }
2021
2074
  }
2022
2075
  });
2076
+ let line;
2077
+ if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state.doc.lineAt(range.from)).text)) {
2078
+ changes.push({
2079
+ from: line.from,
2080
+ to: line.to,
2081
+ insert: "#".repeat(level) + " "
2082
+ });
2083
+ }
2023
2084
  }
2024
2085
  if (!changes.length) {
2025
2086
  return false;
2026
2087
  }
2088
+ const changeSet = state.changes(changes);
2027
2089
  dispatch(state.update({
2028
- changes,
2090
+ changes: changeSet,
2091
+ selection: state.selection.map(changeSet, 1),
2029
2092
  userEvent: "format.setHeading",
2030
2093
  scrollIntoView: true
2031
2094
  }));
@@ -2097,10 +2160,16 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2097
2160
  const closeStart = node.to - size;
2098
2161
  if (markType === type) {
2099
2162
  if (openEnd <= from && closeStart >= from) {
2100
- startCovered = openEnd === from ? "adjacent" : true;
2163
+ startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2164
+ from: node.from,
2165
+ to: openEnd
2166
+ } : true;
2101
2167
  }
2102
2168
  if (openEnd <= to && closeStart >= to) {
2103
- endCovered = closeStart === to ? "adjacent" : true;
2169
+ endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2170
+ from: closeStart,
2171
+ to: node.to
2172
+ } : true;
2104
2173
  }
2105
2174
  }
2106
2175
  if (markType === type || type === 3 && enable) {
@@ -2149,22 +2218,16 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2149
2218
  });
2150
2219
  }
2151
2220
  } else {
2152
- if (startCovered === "adjacent") {
2153
- changes2.push({
2154
- from: from - marker.length,
2155
- to: from
2156
- });
2221
+ if (typeof startCovered === "object") {
2222
+ changes2.push(startCovered);
2157
2223
  } else if (startCovered) {
2158
2224
  changes2.push({
2159
2225
  from: skipSpaces(rangeStart, state.doc, -1, blockStart),
2160
2226
  insert: marker
2161
2227
  });
2162
2228
  }
2163
- if (endCovered === "adjacent") {
2164
- changes2.push({
2165
- from: to,
2166
- to: to + marker.length
2167
- });
2229
+ if (typeof endCovered === "object") {
2230
+ changes2.push(endCovered);
2168
2231
  } else if (endCovered) {
2169
2232
  changes2.push({
2170
2233
  from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
@@ -2175,6 +2238,15 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2175
2238
  }
2176
2239
  }
2177
2240
  });
2241
+ if (blockStart < 0 && range.empty && enable && !/\S/.test(state.doc.lineAt(range.from).text)) {
2242
+ return {
2243
+ changes: {
2244
+ from: range.head,
2245
+ insert: marker + marker
2246
+ },
2247
+ range: EditorSelection.cursor(range.head + marker.length)
2248
+ };
2249
+ }
2178
2250
  const changeSet = state.changes(changes2.concat(changesAtEnd));
2179
2251
  return {
2180
2252
  changes: changeSet,
@@ -2220,6 +2292,19 @@ var skipSpaces = (pos, doc, dir, limit) => {
2220
2292
  }
2221
2293
  return pos;
2222
2294
  };
2295
+ var skipMarkers = (pos, tree, dir, limit) => {
2296
+ for (; ; ) {
2297
+ const next = tree.resolve(pos, dir);
2298
+ if (!IgnoreInline.has(next.name)) {
2299
+ return pos;
2300
+ }
2301
+ const moveTo = dir < 0 ? next.from : next.to;
2302
+ if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
2303
+ return pos;
2304
+ }
2305
+ pos = moveTo;
2306
+ }
2307
+ };
2223
2308
  var snippets = {
2224
2309
  codeblock: snippet([
2225
2310
  //
@@ -2498,8 +2583,10 @@ var addList = (type) => ({ state, dispatch }) => {
2498
2583
  renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
2499
2584
  }
2500
2585
  }
2586
+ const changeSet = state.changes(changes);
2501
2587
  dispatch(state.update({
2502
- changes,
2588
+ changes: changeSet,
2589
+ selection: state.selection.map(changeSet, 1),
2503
2590
  userEvent: "format.list.add",
2504
2591
  scrollIntoView: true
2505
2592
  }));
@@ -2593,6 +2680,7 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2593
2680
  const lines = [];
2594
2681
  let lastBlock = -1;
2595
2682
  for (const { from, to } of state.selection.ranges) {
2683
+ const sawBlock = false;
2596
2684
  syntaxTree2(state).iterate({
2597
2685
  from,
2598
2686
  to,
@@ -2602,9 +2690,9 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2602
2690
  return false;
2603
2691
  }
2604
2692
  lastBlock = node.from;
2605
- let line = state.doc.lineAt(node.from);
2606
- if (line.number > 1) {
2607
- const prevLine = state.doc.line(line.number - 1);
2693
+ let line2 = state.doc.lineAt(node.from);
2694
+ if (line2.number > 1) {
2695
+ const prevLine = state.doc.line(line2.number - 1);
2608
2696
  if (/^[>\s]*$/.test(prevLine.text)) {
2609
2697
  if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
2610
2698
  lines.push(prevLine);
@@ -2612,14 +2700,14 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2612
2700
  }
2613
2701
  }
2614
2702
  for (; ; ) {
2615
- lines.push(line);
2616
- if (line.to >= node.to) {
2703
+ lines.push(line2);
2704
+ if (line2.to >= node.to) {
2617
2705
  break;
2618
2706
  }
2619
- line = state.doc.line(line.number + 1);
2707
+ line2 = state.doc.line(line2.number + 1);
2620
2708
  }
2621
- if (!enable && line.number < state.doc.lines) {
2622
- const nextLine = state.doc.line(line.number + 1);
2709
+ if (!enable && line2.number < state.doc.lines) {
2710
+ const nextLine = state.doc.line(line2.number + 1);
2623
2711
  if (/^[>\s]*$/.test(nextLine.text)) {
2624
2712
  lines.push(nextLine);
2625
2713
  }
@@ -2628,6 +2716,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2628
2716
  }
2629
2717
  }
2630
2718
  });
2719
+ let line;
2720
+ if (!sawBlock && enable && from === to && !/\S/.test((line = state.doc.lineAt(from)).text)) {
2721
+ lines.push(line);
2722
+ }
2631
2723
  }
2632
2724
  const changes = [];
2633
2725
  for (const line of lines) {
@@ -2649,8 +2741,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2649
2741
  if (!changes.length) {
2650
2742
  return false;
2651
2743
  }
2744
+ const changeSet = state.changes(changes);
2652
2745
  dispatch(state.update({
2653
- changes,
2746
+ changes: changeSet,
2747
+ selection: state.selection.map(changeSet, 1),
2654
2748
  userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
2655
2749
  scrollIntoView: true
2656
2750
  }));
@@ -3024,7 +3118,7 @@ var getFormatting = (state) => {
3024
3118
  };
3025
3119
  var useFormattingState = () => {
3026
3120
  const [state, setState] = useState(null);
3027
- const observer = useMemo(() => EditorView10.updateListener.of((update2) => {
3121
+ const observer = useMemo(() => EditorView11.updateListener.of((update2) => {
3028
3122
  if (update2.docChanged || update2.selectionSet) {
3029
3123
  const newState = getFormatting(update2.state);
3030
3124
  if (!state || !compareFormatting(state, newState)) {
@@ -3089,8 +3183,8 @@ var heading2 = () => {
3089
3183
  // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
3090
3184
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3091
3185
  import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
3092
- import { Decoration as Decoration7, EditorView as EditorView11, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
3093
- var styles5 = EditorView11.baseTheme({
3186
+ import { Decoration as Decoration7, EditorView as EditorView12, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
3187
+ var styles5 = EditorView12.baseTheme({
3094
3188
  "& .cm-hr": {
3095
3189
  // Note that block-level decorations should not have vertical margins,
3096
3190
  borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
@@ -3144,7 +3238,7 @@ var hr = () => {
3144
3238
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3145
3239
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3146
3240
  import { StateField as StateField4 } from "@codemirror/state";
3147
- import { Decoration as Decoration8, EditorView as EditorView12, WidgetType as WidgetType4 } from "@codemirror/view";
3241
+ import { Decoration as Decoration8, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
3148
3242
  var image = (options = {}) => {
3149
3243
  return StateField4.define({
3150
3244
  create: (state) => {
@@ -3171,7 +3265,7 @@ var image = (options = {}) => {
3171
3265
  add: buildDecorations4(from, to, tr.state)
3172
3266
  });
3173
3267
  },
3174
- provide: (field) => EditorView12.decorations.from(field)
3268
+ provide: (field) => EditorView13.decorations.from(field)
3175
3269
  });
3176
3270
  };
3177
3271
  var buildDecorations4 = (from, to, state) => {
@@ -3353,12 +3447,12 @@ var buildDecorations5 = (view, options) => {
3353
3447
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
3354
3448
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
3355
3449
  import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField5 } from "@codemirror/state";
3356
- import { Decoration as Decoration10, EditorView as EditorView13, WidgetType as WidgetType6 } from "@codemirror/view";
3450
+ import { Decoration as Decoration10, EditorView as EditorView14, WidgetType as WidgetType6 } from "@codemirror/view";
3357
3451
  var table = (options = {}) => {
3358
3452
  return StateField5.define({
3359
3453
  create: (state) => update(state, options),
3360
3454
  update: (_, tr) => update(tr.state, options),
3361
- provide: (field) => EditorView13.decorations.from(field)
3455
+ provide: (field) => EditorView14.decorations.from(field)
3362
3456
  });
3363
3457
  };
3364
3458
  var update = (state, options) => {
@@ -3451,8 +3545,8 @@ var TableWidget = class extends WidgetType6 {
3451
3545
  // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
3452
3546
  import { syntaxTree as syntaxTree8 } from "@codemirror/language";
3453
3547
  import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
3454
- import { EditorView as EditorView14, Decoration as Decoration11, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
3455
- var styles6 = EditorView14.baseTheme({
3548
+ import { EditorView as EditorView15, Decoration as Decoration11, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
3549
+ var styles6 = EditorView15.baseTheme({
3456
3550
  "& .cm-task": {
3457
3551
  color: getToken("extend.colors.blue.500")
3458
3552
  },
@@ -3575,10 +3669,10 @@ var mention = ({ onSearch }) => {
3575
3669
  };
3576
3670
 
3577
3671
  // packages/ui/react-ui-editor/src/extensions/modes.ts
3578
- import { Facet as Facet4 } from "@codemirror/state";
3672
+ import { Facet as Facet5 } from "@codemirror/state";
3579
3673
  import { keymap as keymap6 } from "@codemirror/view";
3580
3674
  import { vim } from "@replit/codemirror-vim";
3581
- var editorMode = Facet4.define({
3675
+ var editorMode = Facet5.define({
3582
3676
  combine: (modes) => modes[0] ?? {}
3583
3677
  });
3584
3678
  var EditorModes = {
@@ -3942,27 +4036,27 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3942
4036
  selection,
3943
4037
  extensions: [
3944
4038
  // TODO(burdon): Doesn't catch errors in keymap functions.
3945
- EditorView15.exceptionSink.of((err) => {
4039
+ EditorView16.exceptionSink.of((err) => {
3946
4040
  log3.catch(err, void 0, {
3947
4041
  F: __dxlog_file4,
3948
- L: 112,
4042
+ L: 113,
3949
4043
  S: void 0,
3950
4044
  C: (f, a) => f(...a)
3951
4045
  });
3952
4046
  }),
3953
4047
  // Theme.
3954
4048
  // TODO(burdon): Make configurable.
3955
- EditorView15.baseTheme(defaultTheme),
3956
- EditorView15.theme(theme ?? {}),
3957
- EditorView15.darkTheme.of(themeMode === "dark"),
3958
- EditorView15.editorAttributes.of({
4049
+ EditorView16.baseTheme(defaultTheme),
4050
+ EditorView16.theme(theme ?? {}),
4051
+ EditorView16.darkTheme.of(themeMode === "dark"),
4052
+ EditorView16.editorAttributes.of({
3959
4053
  class: slots.editor?.className ?? ""
3960
4054
  }),
3961
- EditorView15.contentAttributes.of({
4055
+ EditorView16.contentAttributes.of({
3962
4056
  class: slots.content?.className ?? ""
3963
4057
  }),
3964
4058
  // Focus.
3965
- EditorView15.updateListener.of((update2) => {
4059
+ EditorView16.updateListener.of((update2) => {
3966
4060
  update2.transactions.forEach((transaction) => {
3967
4061
  if (transaction.isUserEvent("focus.container")) {
3968
4062
  rootRef.current?.focus();
@@ -3970,7 +4064,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3970
4064
  });
3971
4065
  }),
3972
4066
  // State.
3973
- EditorView15.editable.of(!readonly),
4067
+ EditorView16.editable.of(!readonly),
3974
4068
  EditorState2.readOnly.of(!!readonly),
3975
4069
  // Storage and replication.
3976
4070
  // NOTE: This must come before user extensions.
@@ -3979,7 +4073,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3979
4073
  ...extensions
3980
4074
  ].filter(isNotFalsy4)
3981
4075
  });
3982
- const newView = new EditorView15({
4076
+ const newView = new EditorView16({
3983
4077
  state,
3984
4078
  parent: rootRef.current,
3985
4079
  scrollTo,
@@ -4003,6 +4097,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
4003
4097
  };
4004
4098
  }, [
4005
4099
  rootRef,
4100
+ extensions,
4006
4101
  model,
4007
4102
  readonly,
4008
4103
  themeMode
@@ -4028,38 +4123,49 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
4028
4123
  ...tabsterDOMAttribute
4029
4124
  });
4030
4125
  });
4031
- var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
4126
+ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4032
4127
  const { themeMode } = useThemeContext();
4033
4128
  const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
4129
+ const extensions = useMemo2(() => [
4130
+ createBasicBundle({
4131
+ themeMode,
4132
+ placeholder: placeholder3,
4133
+ lineWrapping
4134
+ }),
4135
+ ..._extensions ?? []
4136
+ ], [
4137
+ themeMode,
4138
+ placeholder3,
4139
+ lineWrapping,
4140
+ _extensions
4141
+ ]);
4034
4142
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
4035
4143
  ref: forwardedRef,
4036
4144
  readonly,
4037
- extensions: [
4038
- createBasicBundle({
4039
- themeMode,
4040
- placeholder: placeholder3,
4041
- lineWrapping
4042
- }),
4043
- ...extensions
4044
- ],
4145
+ extensions,
4045
4146
  theme,
4046
4147
  slots: updatedSlots,
4047
4148
  ...props
4048
4149
  });
4049
4150
  });
4050
- var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
4151
+ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
4051
4152
  const { themeMode } = useThemeContext();
4052
4153
  const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
4154
+ const extensions = useMemo2(() => [
4155
+ createMarkdownExtensions({
4156
+ themeMode,
4157
+ placeholder: placeholder3
4158
+ }),
4159
+ ..._extensions ?? []
4160
+ ], [
4161
+ themeMode,
4162
+ placeholder3,
4163
+ _extensions
4164
+ ]);
4053
4165
  return /* @__PURE__ */ React.createElement(BaseTextEditor, {
4054
4166
  ref: forwardedRef,
4055
4167
  readonly,
4056
- extensions: [
4057
- createMarkdownExtensions({
4058
- themeMode,
4059
- placeholder: placeholder3
4060
- }),
4061
- ...extensions
4062
- ],
4168
+ extensions,
4063
4169
  theme,
4064
4170
  slots: updatedSlots,
4065
4171
  ...props
@@ -4289,8 +4395,7 @@ var markdownBlocks = [
4289
4395
  {
4290
4396
  type: "codeblock",
4291
4397
  Icon: CodeBlock,
4292
- getState: (state) => state.blockType === "codeblock",
4293
- disabled: (state) => !state.blankLine
4398
+ getState: (state) => state.blockType === "codeblock"
4294
4399
  },
4295
4400
  {
4296
4401
  type: "table",
@@ -4523,7 +4628,7 @@ var useEditorView = (id) => {
4523
4628
 
4524
4629
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4525
4630
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4526
- import { EditorView as EditorView16 } from "@codemirror/view";
4631
+ import { EditorView as EditorView17 } from "@codemirror/view";
4527
4632
  import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
4528
4633
  import { log as log5 } from "@dxos/log";
4529
4634
  import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
@@ -4533,27 +4638,28 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
4533
4638
  const [view, setView] = useState4();
4534
4639
  const parentRef = useRef3(null);
4535
4640
  useEffect3(() => {
4641
+ let view2;
4536
4642
  if (parentRef.current) {
4537
4643
  const state = EditorState3.create({
4538
4644
  doc,
4539
4645
  selection,
4540
4646
  extensions: [
4541
4647
  // TODO(burdon): Doesn't catch errors in keymap functions.
4542
- EditorView16.exceptionSink.of((err) => {
4648
+ EditorView17.exceptionSink.of((err) => {
4543
4649
  log5.catch(err, void 0, {
4544
4650
  F: __dxlog_file6,
4545
- L: 57,
4651
+ L: 60,
4546
4652
  S: void 0,
4547
4653
  C: (f, a) => f(...a)
4548
4654
  });
4549
4655
  }),
4550
4656
  extensions,
4551
- EditorView16.updateListener.of(() => {
4657
+ EditorView17.updateListener.of(() => {
4552
4658
  onUpdate.current?.();
4553
4659
  })
4554
4660
  ].filter(isNotFalsy5)
4555
4661
  });
4556
- const view2 = new EditorView16({
4662
+ view2 = new EditorView17({
4557
4663
  parent: parentRef.current,
4558
4664
  scrollTo,
4559
4665
  state,
@@ -4569,10 +4675,11 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
4569
4675
  setView(view2);
4570
4676
  }
4571
4677
  return () => {
4572
- view?.destroy();
4678
+ view2?.destroy();
4573
4679
  };
4574
4680
  }, [
4575
- parentRef
4681
+ parentRef,
4682
+ extensions
4576
4683
  ]);
4577
4684
  useEffect3(() => {
4578
4685
  if (view) {
@@ -4610,19 +4717,19 @@ var createDataExtensions = ({ readonly = false } = {}) => {
4610
4717
  return [
4611
4718
  //
4612
4719
  EditorState3.readOnly.of(readonly),
4613
- EditorView16.editable.of(!readonly)
4720
+ EditorView17.editable.of(!readonly)
4614
4721
  ];
4615
4722
  };
4616
4723
  var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4617
4724
  return [
4618
4725
  //
4619
- EditorView16.baseTheme(defaultTheme),
4620
- theme && EditorView16.theme(theme),
4621
- EditorView16.darkTheme.of(themeMode === "dark"),
4622
- EditorView16.editorAttributes.of({
4726
+ EditorView17.baseTheme(defaultTheme),
4727
+ theme && EditorView17.theme(theme),
4728
+ EditorView17.darkTheme.of(themeMode === "dark"),
4729
+ EditorView17.editorAttributes.of({
4623
4730
  class: slots?.editor?.className ?? ""
4624
4731
  }),
4625
- EditorView16.contentAttributes.of({
4732
+ EditorView17.contentAttributes.of({
4626
4733
  class: slots?.content?.className ?? ""
4627
4734
  })
4628
4735
  ].filter(isNotFalsy5);
@@ -4630,13 +4737,13 @@ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4630
4737
 
4631
4738
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
4632
4739
  import get4 from "lodash.get";
4633
- import { useState as useState5, useMemo as useMemo2 } from "react";
4740
+ import { useState as useState5, useMemo as useMemo3 } from "react";
4634
4741
  import { generateName } from "@dxos/display-name";
4635
4742
  import { getRawDoc } from "@dxos/echo-schema";
4636
4743
  import { invariant as invariant3 } from "@dxos/invariant";
4637
4744
  import { isAutomergeObject } from "@dxos/react-client/echo";
4638
4745
  var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
4639
- var useTextModel = (props) => useMemo2(() => createModel(props), Object.values(props));
4746
+ var useTextModel = (props) => useMemo3(() => createModel(props), Object.values(props));
4640
4747
  var useInMemoryTextModel = ({ id, defaultContent }) => {
4641
4748
  const [content, setContent] = useState5(defaultContent ?? "");
4642
4749
  return {
@@ -4731,6 +4838,8 @@ export {
4731
4838
  createDataExtensions,
4732
4839
  createMarkdownExtensions,
4733
4840
  createThemeExtensions,
4841
+ cursorLineMargin,
4842
+ cursorLineMarginFacet,
4734
4843
  defaultMarkdownSlots,
4735
4844
  defaultOptions,
4736
4845
  defaultSlots,