@dxos/react-ui-editor 0.4.4 → 0.4.5-main.17763ce

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 (46) hide show
  1. package/dist/lib/browser/index.mjs +421 -540
  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 +5 -5
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
  7. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  8. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  9. package/dist/types/src/extensions/cursor-line-margin.d.ts +8 -0
  10. package/dist/types/src/extensions/cursor-line-margin.d.ts.map +1 -0
  11. package/dist/types/src/extensions/index.d.ts +1 -0
  12. package/dist/types/src/extensions/index.d.ts.map +1 -1
  13. package/dist/types/src/extensions/markdown/decorate.d.ts +5 -0
  14. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -0
  15. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  16. package/dist/types/src/extensions/markdown/index.d.ts +1 -4
  17. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  18. package/dist/types/src/extensions/markdown/link.d.ts +1 -11
  19. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  20. package/dist/types/src/styles/layout.d.ts +2 -2
  21. package/dist/types/src/styles/layout.d.ts.map +1 -1
  22. package/package.json +31 -28
  23. package/src/components/TextEditor/MarkdownEditor.stories.tsx +29 -37
  24. package/src/components/TextEditor/hooks.stories.tsx +5 -17
  25. package/src/components/Toolbar/Toolbar.stories.tsx +4 -7
  26. package/src/components/Toolbar/Toolbar.tsx +0 -1
  27. package/src/extensions/cursor-line-margin.ts +92 -0
  28. package/src/extensions/index.ts +1 -0
  29. package/src/extensions/markdown/decorate.ts +301 -0
  30. package/src/extensions/markdown/formatting.test.ts +17 -0
  31. package/src/extensions/markdown/formatting.ts +67 -76
  32. package/src/extensions/markdown/index.ts +1 -4
  33. package/src/extensions/markdown/link.ts +23 -185
  34. package/src/styles/layout.ts +3 -2
  35. package/dist/types/src/extensions/markdown/code.d.ts +0 -2
  36. package/dist/types/src/extensions/markdown/code.d.ts.map +0 -1
  37. package/dist/types/src/extensions/markdown/heading.d.ts +0 -6
  38. package/dist/types/src/extensions/markdown/heading.d.ts.map +0 -1
  39. package/dist/types/src/extensions/markdown/hr.d.ts +0 -2
  40. package/dist/types/src/extensions/markdown/hr.d.ts.map +0 -1
  41. package/dist/types/src/extensions/markdown/tasklist.d.ts +0 -3
  42. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +0 -1
  43. package/src/extensions/markdown/code.ts +0 -162
  44. package/src/extensions/markdown/heading.ts +0 -59
  45. package/src/extensions/markdown/hr.ts +0 -71
  46. package/src/extensions/markdown/tasklist.ts +0 -110
@@ -8,7 +8,7 @@ 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 EditorView14 } from "@codemirror/view";
12
12
  import { useFocusableGroup } from "@fluentui/react-tabster";
13
13
  import defaultsDeep2 from "lodash.defaultsdeep";
14
14
  import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2, useMemo as useMemo2 } from "react";
@@ -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);
@@ -765,10 +765,10 @@ var blast = (options = defaultOptions) => {
765
765
  const getPoint = (view, pos) => {
766
766
  const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
767
767
  const element = document.elementFromPoint(x, y);
768
- const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
768
+ const { offsetLeft: left = 0, offsetTop: top = 0 } = view.scrollDOM.parentElement ?? {};
769
769
  const point = {
770
770
  x: x - left,
771
- y: y - top2
771
+ y: y - top
772
772
  };
773
773
  return {
774
774
  element,
@@ -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
@@ -1820,12 +1871,207 @@ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrap
1820
1871
  ].filter(isNotFalsy3);
1821
1872
  };
1822
1873
 
1823
- // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
1874
+ // packages/ui/react-ui-editor/src/extensions/markdown/decorate.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 { EditorView as EditorView10, Decoration as Decoration4, WidgetType as WidgetType2, ViewPlugin as ViewPlugin3 } from "@codemirror/view";
1827
1878
  import { mx as mx2 } from "@dxos/react-ui-theme";
1828
- var styles4 = EditorView9.baseTheme({
1879
+ var FencedCodeBoundary = class extends WidgetType2 {
1880
+ constructor(_className) {
1881
+ super();
1882
+ this._className = _className;
1883
+ }
1884
+ toDOM() {
1885
+ const el = document.createElement("span");
1886
+ el.innerHTML = "&nbsp;";
1887
+ el.className = mx2("cm-code cm-codeblock", this._className);
1888
+ return el;
1889
+ }
1890
+ };
1891
+ var HorizontalRuleWidget = class extends WidgetType2 {
1892
+ toDOM() {
1893
+ const el = document.createElement("span");
1894
+ el.className = "cm-hr";
1895
+ return el;
1896
+ }
1897
+ };
1898
+ var LinkButton = class extends WidgetType2 {
1899
+ constructor(url, render) {
1900
+ super();
1901
+ this.url = url;
1902
+ this.render = render;
1903
+ }
1904
+ eq(other) {
1905
+ return this.url === other.url;
1906
+ }
1907
+ toDOM(view) {
1908
+ const el = document.createElement("span");
1909
+ this.render(el, this.url);
1910
+ return el;
1911
+ }
1912
+ };
1913
+ var CheckboxWidget = class extends WidgetType2 {
1914
+ constructor(_checked) {
1915
+ super();
1916
+ this._checked = _checked;
1917
+ }
1918
+ eq(other) {
1919
+ return this._checked === other._checked;
1920
+ }
1921
+ toDOM(view) {
1922
+ const input = document.createElement("input");
1923
+ input.className = "cm-task-checkbox";
1924
+ input.type = "checkbox";
1925
+ input.checked = this._checked;
1926
+ if (view.state.readOnly) {
1927
+ input.setAttribute("disabled", "true");
1928
+ } else {
1929
+ input.onmousedown = (event) => {
1930
+ const pos = view.posAtDOM(input);
1931
+ const text = view.state.sliceDoc(pos, pos + 3);
1932
+ if (text === (this._checked ? "[x]" : "[ ]")) {
1933
+ view.dispatch({
1934
+ changes: {
1935
+ from: pos + 1,
1936
+ to: pos + 2,
1937
+ insert: this._checked ? " " : "x"
1938
+ }
1939
+ });
1940
+ event.preventDefault();
1941
+ }
1942
+ };
1943
+ }
1944
+ return input;
1945
+ }
1946
+ ignoreEvent() {
1947
+ return false;
1948
+ }
1949
+ };
1950
+ var hide = Decoration4.replace({});
1951
+ var fencedCodeTop = Decoration4.replace({
1952
+ widget: new FencedCodeBoundary("cm-codeblock-end cm-codeblock-first")
1953
+ });
1954
+ var fencedCodeBottom = Decoration4.replace({
1955
+ widget: new FencedCodeBoundary("cm-codeblock-end cm-codeblock-last")
1956
+ });
1957
+ var horizontalRule = Decoration4.replace({
1958
+ widget: new HorizontalRuleWidget()
1959
+ });
1960
+ var checkedTask = Decoration4.replace({
1961
+ widget: new CheckboxWidget(true)
1962
+ });
1963
+ var uncheckedTask = Decoration4.replace({
1964
+ widget: new CheckboxWidget(false)
1965
+ });
1966
+ var editingRange = (state, range) => {
1967
+ const { readOnly, selection: { main: { head } } } = state;
1968
+ return !readOnly && head >= range.from && head <= range.to;
1969
+ };
1970
+ var MarksByParent = /* @__PURE__ */ new Set([
1971
+ "CodeMark",
1972
+ "EmphasisMark",
1973
+ "StrikethroughMark",
1974
+ "SubscriptMark",
1975
+ "SuperscriptMark"
1976
+ ]);
1977
+ var buildDecorations = (view, options) => {
1978
+ const builder = new RangeSetBuilder();
1979
+ const { state } = view;
1980
+ for (const { from, to } of view.visibleRanges) {
1981
+ syntaxTree(state).iterate({
1982
+ from,
1983
+ to,
1984
+ enter: (node) => {
1985
+ if (node.name === "FencedCode") {
1986
+ const editing = editingRange(state, node);
1987
+ for (const block of view.viewportLineBlocks) {
1988
+ if (block.to < node.from) {
1989
+ continue;
1990
+ }
1991
+ if (block.from > node.to) {
1992
+ break;
1993
+ }
1994
+ const first = block.from <= node.from;
1995
+ const last = block.to >= node.to;
1996
+ if (!editing && (first || last)) {
1997
+ builder.add(block.from, block.to, first ? fencedCodeTop : fencedCodeBottom);
1998
+ } else {
1999
+ builder.add(block.from, block.from, Decoration4.line({
2000
+ class: mx2("cm-code cm-codeblock-line", first && "cm-codeblock-first", last && "cm-codeblock-last")
2001
+ }));
2002
+ }
2003
+ }
2004
+ return false;
2005
+ } else if (node.name === "Link") {
2006
+ const marks = node.node.getChildren("LinkMark");
2007
+ const urlNode = node.node.getChild("URL");
2008
+ const editing = editingRange(state, node);
2009
+ if (urlNode && marks.length >= 2) {
2010
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
2011
+ if (!editing) {
2012
+ builder.add(node.from, marks[0].to, hide);
2013
+ }
2014
+ builder.add(marks[0].to, marks[1].from, Decoration4.mark({
2015
+ tagName: "a",
2016
+ attributes: {
2017
+ class: "cm-link",
2018
+ href: url,
2019
+ rel: "noreferrer",
2020
+ target: "_blank"
2021
+ }
2022
+ }));
2023
+ if (!editing) {
2024
+ builder.add(marks[1].from, node.to, options.renderLinkButton ? Decoration4.replace({
2025
+ widget: new LinkButton(url, options.renderLinkButton)
2026
+ }) : hide);
2027
+ }
2028
+ }
2029
+ } else if (node.name === "HeaderMark") {
2030
+ const parent = node.node.parent;
2031
+ if (/^ATX/.test(parent.name) && !editingRange(state, state.doc.lineAt(node.from))) {
2032
+ const next = state.doc.sliceString(node.to, node.to + 1);
2033
+ builder.add(node.from, node.to + (next === " " ? 1 : 0), hide);
2034
+ }
2035
+ } else if (node.name === "HorizontalRule") {
2036
+ if (!editingRange(state, node)) {
2037
+ builder.add(node.from, node.to, horizontalRule);
2038
+ }
2039
+ } else if (node.name === "TaskMarker") {
2040
+ if (!editingRange(state, node)) {
2041
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
2042
+ builder.add(node.from - 2, node.from - 1, Decoration4.mark({
2043
+ class: "cm-task"
2044
+ }));
2045
+ builder.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
2046
+ }
2047
+ } else if (MarksByParent.has(node.name)) {
2048
+ if (!editingRange(state, node.node.parent)) {
2049
+ builder.add(node.from, node.to, hide);
2050
+ }
2051
+ }
2052
+ }
2053
+ });
2054
+ }
2055
+ return builder.finish();
2056
+ };
2057
+ var decorateMarkdown = (options = {}) => {
2058
+ return [
2059
+ ViewPlugin3.fromClass(class {
2060
+ constructor(view) {
2061
+ this.decorations = buildDecorations(view, options);
2062
+ }
2063
+ update(update2) {
2064
+ if (update2.docChanged || update2.selectionSet || update2.viewportChanged) {
2065
+ this.decorations = buildDecorations(update2.view, options);
2066
+ }
2067
+ }
2068
+ }, {
2069
+ decorations: (value) => value.decorations
2070
+ }),
2071
+ formattingStyles
2072
+ ];
2073
+ };
2074
+ var formattingStyles = EditorView10.baseTheme({
1829
2075
  "& .cm-code": {
1830
2076
  fontFamily: getToken("fontFamily.mono", []).join(",")
1831
2077
  },
@@ -1873,82 +2119,28 @@ var styles4 = EditorView9.baseTheme({
1873
2119
  "&::after": {
1874
2120
  background: getToken("extend.semanticColors.input.dark")
1875
2121
  }
2122
+ },
2123
+ "& .cm-hr": {
2124
+ display: "inline-block",
2125
+ width: "100%",
2126
+ height: "0",
2127
+ verticalAlign: "middle",
2128
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
2129
+ },
2130
+ "& .cm-task": {
2131
+ color: getToken("extend.colors.blue.500")
2132
+ },
2133
+ "& .cm-task-checkbox": {
2134
+ marginLeft: "4px",
2135
+ marginRight: "4px"
1876
2136
  }
1877
2137
  });
1878
- var getLineRange = (lines, from, to) => {
1879
- const start = lines.findIndex((line) => line.from >= from);
1880
- const end = lines.findIndex((line) => line.to >= to);
1881
- return [
1882
- start,
1883
- end
1884
- ];
1885
- };
1886
- var LineWidget = class extends WidgetType2 {
1887
- constructor(_className) {
1888
- super();
1889
- this._className = _className;
1890
- }
1891
- toDOM() {
1892
- const el = document.createElement("span");
1893
- el.innerHTML = "&nbsp;";
1894
- el.className = mx2("cm-code cm-codeblock", this._className);
1895
- return el;
1896
- }
1897
- };
1898
- var top = new LineWidget("cm-codeblock-end cm-codeblock-first");
1899
- var bottom = new LineWidget("cm-codeblock-end cm-codeblock-last");
1900
- var buildDecorations = (view) => {
1901
- const builder = new RangeSetBuilder();
1902
- const { state } = view;
1903
- const blocks = view.viewportLineBlocks;
1904
- const cursor = state.selection.main.head;
1905
- for (const { from, to } of view.visibleRanges) {
1906
- syntaxTree(state).iterate({
1907
- enter: (node) => {
1908
- if (node.name === "FencedCode") {
1909
- const editing = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
1910
- const range = getLineRange(blocks, node.from, node.to);
1911
- for (let i = range[0]; i <= range[1]; i++) {
1912
- const block = blocks[i];
1913
- if (!editing && (i === range[0] || i === range[1])) {
1914
- builder.add(block.from, block.to, Decoration4.replace({
1915
- widget: i === range[0] ? top : bottom
1916
- }));
1917
- } else {
1918
- builder.add(block.from, block.from, Decoration4.line({
1919
- class: mx2("cm-code cm-codeblock-line", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1920
- }));
1921
- }
1922
- }
1923
- }
1924
- },
1925
- from,
1926
- to
1927
- });
1928
- }
1929
- return builder.finish();
1930
- };
1931
- var code2 = () => {
1932
- return [
1933
- ViewPlugin3.fromClass(class {
1934
- constructor(view) {
1935
- this.decorations = buildDecorations(view);
1936
- }
1937
- update(update2) {
1938
- this.decorations = buildDecorations(update2.view);
1939
- }
1940
- }, {
1941
- decorations: (value) => value.decorations
1942
- }),
1943
- styles4
1944
- ];
1945
- };
1946
2138
 
1947
2139
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1948
2140
  import { snippet } from "@codemirror/autocomplete";
1949
2141
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1950
- 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";
2142
+ import { EditorSelection } from "@codemirror/state";
2143
+ import { EditorView as EditorView11, keymap as keymap5 } from "@codemirror/view";
1952
2144
  import { useState, useMemo } from "react";
1953
2145
  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
2146
  var Inline;
@@ -1969,6 +2161,7 @@ var setHeading = (level) => ({ state, dispatch }) => {
1969
2161
  const changes = [];
1970
2162
  let prevBlock = -1;
1971
2163
  for (const range of ranges) {
2164
+ let sawBlock = false;
1972
2165
  syntaxTree2(state).iterate({
1973
2166
  from: range.from,
1974
2167
  to: range.to,
@@ -1976,6 +2169,7 @@ var setHeading = (level) => ({ state, dispatch }) => {
1976
2169
  if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
1977
2170
  return;
1978
2171
  }
2172
+ sawBlock = true;
1979
2173
  prevBlock = node.from;
1980
2174
  const blockType = Textblocks[node.name];
1981
2175
  const isHeading = /heading(\d)/.exec(blockType);
@@ -2020,12 +2214,22 @@ var setHeading = (level) => ({ state, dispatch }) => {
2020
2214
  }
2021
2215
  }
2022
2216
  });
2217
+ let line;
2218
+ if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state.doc.lineAt(range.from)).text)) {
2219
+ changes.push({
2220
+ from: line.from,
2221
+ to: line.to,
2222
+ insert: "#".repeat(level) + " "
2223
+ });
2224
+ }
2023
2225
  }
2024
2226
  if (!changes.length) {
2025
2227
  return false;
2026
2228
  }
2229
+ const changeSet = state.changes(changes);
2027
2230
  dispatch(state.update({
2028
- changes,
2231
+ changes: changeSet,
2232
+ selection: state.selection.map(changeSet, 1),
2029
2233
  userEvent: "format.setHeading",
2030
2234
  scrollIntoView: true
2031
2235
  }));
@@ -2097,10 +2301,16 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2097
2301
  const closeStart = node.to - size;
2098
2302
  if (markType === type) {
2099
2303
  if (openEnd <= from && closeStart >= from) {
2100
- startCovered = openEnd === from ? "adjacent" : true;
2304
+ startCovered = !enable && openEnd === skipMarkers(from, node.node, -1, openEnd) ? {
2305
+ from: node.from,
2306
+ to: openEnd
2307
+ } : true;
2101
2308
  }
2102
2309
  if (openEnd <= to && closeStart >= to) {
2103
- endCovered = closeStart === to ? "adjacent" : true;
2310
+ endCovered = !enable && closeStart === skipMarkers(to, node.node, 1, closeStart) ? {
2311
+ from: closeStart,
2312
+ to: node.to
2313
+ } : true;
2104
2314
  }
2105
2315
  }
2106
2316
  if (markType === type || type === 3 && enable) {
@@ -2149,22 +2359,16 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2149
2359
  });
2150
2360
  }
2151
2361
  } else {
2152
- if (startCovered === "adjacent") {
2153
- changes2.push({
2154
- from: from - marker.length,
2155
- to: from
2156
- });
2362
+ if (typeof startCovered === "object") {
2363
+ changes2.push(startCovered);
2157
2364
  } else if (startCovered) {
2158
2365
  changes2.push({
2159
2366
  from: skipSpaces(rangeStart, state.doc, -1, blockStart),
2160
2367
  insert: marker
2161
2368
  });
2162
2369
  }
2163
- if (endCovered === "adjacent") {
2164
- changes2.push({
2165
- from: to,
2166
- to: to + marker.length
2167
- });
2370
+ if (typeof endCovered === "object") {
2371
+ changes2.push(endCovered);
2168
2372
  } else if (endCovered) {
2169
2373
  changes2.push({
2170
2374
  from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
@@ -2175,6 +2379,15 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
2175
2379
  }
2176
2380
  }
2177
2381
  });
2382
+ if (blockStart < 0 && range.empty && enable && !/\S/.test(state.doc.lineAt(range.from).text)) {
2383
+ return {
2384
+ changes: {
2385
+ from: range.head,
2386
+ insert: marker + marker
2387
+ },
2388
+ range: EditorSelection.cursor(range.head + marker.length)
2389
+ };
2390
+ }
2178
2391
  const changeSet = state.changes(changes2.concat(changesAtEnd));
2179
2392
  return {
2180
2393
  changes: changeSet,
@@ -2220,6 +2433,19 @@ var skipSpaces = (pos, doc, dir, limit) => {
2220
2433
  }
2221
2434
  return pos;
2222
2435
  };
2436
+ var skipMarkers = (pos, tree, dir, limit) => {
2437
+ for (; ; ) {
2438
+ const next = tree.resolve(pos, dir);
2439
+ if (!IgnoreInline.has(next.name)) {
2440
+ return pos;
2441
+ }
2442
+ const moveTo = dir < 0 ? next.from : next.to;
2443
+ if (limit != null && (dir < 0 ? moveTo < limit : moveTo > limit)) {
2444
+ return pos;
2445
+ }
2446
+ pos = moveTo;
2447
+ }
2448
+ };
2223
2449
  var snippets = {
2224
2450
  codeblock: snippet([
2225
2451
  //
@@ -2498,8 +2724,10 @@ var addList = (type) => ({ state, dispatch }) => {
2498
2724
  renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
2499
2725
  }
2500
2726
  }
2727
+ const changeSet = state.changes(changes);
2501
2728
  dispatch(state.update({
2502
- changes,
2729
+ changes: changeSet,
2730
+ selection: state.selection.map(changeSet, 1),
2503
2731
  userEvent: "format.list.add",
2504
2732
  scrollIntoView: true
2505
2733
  }));
@@ -2593,6 +2821,7 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2593
2821
  const lines = [];
2594
2822
  let lastBlock = -1;
2595
2823
  for (const { from, to } of state.selection.ranges) {
2824
+ const sawBlock = false;
2596
2825
  syntaxTree2(state).iterate({
2597
2826
  from,
2598
2827
  to,
@@ -2602,9 +2831,9 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2602
2831
  return false;
2603
2832
  }
2604
2833
  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);
2834
+ let line2 = state.doc.lineAt(node.from);
2835
+ if (line2.number > 1) {
2836
+ const prevLine = state.doc.line(line2.number - 1);
2608
2837
  if (/^[>\s]*$/.test(prevLine.text)) {
2609
2838
  if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
2610
2839
  lines.push(prevLine);
@@ -2612,14 +2841,14 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2612
2841
  }
2613
2842
  }
2614
2843
  for (; ; ) {
2615
- lines.push(line);
2616
- if (line.to >= node.to) {
2844
+ lines.push(line2);
2845
+ if (line2.to >= node.to) {
2617
2846
  break;
2618
2847
  }
2619
- line = state.doc.line(line.number + 1);
2848
+ line2 = state.doc.line(line2.number + 1);
2620
2849
  }
2621
- if (!enable && line.number < state.doc.lines) {
2622
- const nextLine = state.doc.line(line.number + 1);
2850
+ if (!enable && line2.number < state.doc.lines) {
2851
+ const nextLine = state.doc.line(line2.number + 1);
2623
2852
  if (/^[>\s]*$/.test(nextLine.text)) {
2624
2853
  lines.push(nextLine);
2625
2854
  }
@@ -2628,6 +2857,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2628
2857
  }
2629
2858
  }
2630
2859
  });
2860
+ let line;
2861
+ if (!sawBlock && enable && from === to && !/\S/.test((line = state.doc.lineAt(from)).text)) {
2862
+ lines.push(line);
2863
+ }
2631
2864
  }
2632
2865
  const changes = [];
2633
2866
  for (const line of lines) {
@@ -2649,8 +2882,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
2649
2882
  if (!changes.length) {
2650
2883
  return false;
2651
2884
  }
2885
+ const changeSet = state.changes(changes);
2652
2886
  dispatch(state.update({
2653
- changes,
2887
+ changes: changeSet,
2888
+ selection: state.selection.map(changeSet, 1),
2654
2889
  userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
2655
2890
  scrollIntoView: true
2656
2891
  }));
@@ -2782,56 +3017,7 @@ var formatting = (options = {}) => {
2782
3017
  key: "meta-b",
2783
3018
  run: toggleStrong
2784
3019
  }
2785
- ]),
2786
- styling()
2787
- ];
2788
- };
2789
- var styling = () => {
2790
- const buildDecorations7 = (view) => {
2791
- const builder = new RangeSetBuilder2();
2792
- const { state } = view;
2793
- const cursor = state.selection.main.head;
2794
- const replace = (node, marks) => {
2795
- if (cursor <= node.from || cursor >= node.to) {
2796
- for (const mark2 of marks) {
2797
- builder.add(mark2.from, mark2.to, Decoration5.replace({}));
2798
- }
2799
- }
2800
- };
2801
- for (const { from, to } of view.visibleRanges) {
2802
- syntaxTree2(state).iterate({
2803
- enter: (node) => {
2804
- switch (node.name) {
2805
- case "Emphasis":
2806
- case "StrongEmphasis": {
2807
- const marks = node.node.getChildren("EmphasisMark");
2808
- replace(node, marks);
2809
- break;
2810
- }
2811
- case "Strikethrough": {
2812
- const marks = node.node.getChildren("StrikethroughMark");
2813
- replace(node, marks);
2814
- break;
2815
- }
2816
- }
2817
- },
2818
- from,
2819
- to
2820
- });
2821
- }
2822
- return builder.finish();
2823
- };
2824
- return [
2825
- ViewPlugin4.fromClass(class {
2826
- constructor(view) {
2827
- this.decorations = buildDecorations7(view);
2828
- }
2829
- update(update2) {
2830
- this.decorations = buildDecorations7(update2.view);
2831
- }
2832
- }, {
2833
- decorations: (value) => value.decorations
2834
- })
3020
+ ])
2835
3021
  ];
2836
3022
  };
2837
3023
  var InlineMarker = {
@@ -2881,7 +3067,7 @@ var getFormatting = (state) => {
2881
3067
  null,
2882
3068
  null
2883
3069
  ];
2884
- let link2 = false;
3070
+ let link = false;
2885
3071
  let blockQuote = null;
2886
3072
  let listStyle = null;
2887
3073
  const stack = [];
@@ -2943,7 +3129,7 @@ var getFormatting = (state) => {
2943
3129
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
2944
3130
  stack.push(name);
2945
3131
  } else if (name === "Link") {
2946
- link2 = true;
3132
+ link = true;
2947
3133
  } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
2948
3134
  if (name === "Task" && stack[stack.length - 1] === "BulletList") {
2949
3135
  stack[stack.length - 1] = "TaskList";
@@ -3018,13 +3204,13 @@ var getFormatting = (state) => {
3018
3204
  emphasis: inline[1] ?? false,
3019
3205
  strong: inline[0] ?? false,
3020
3206
  strikethrough: inline[2] ?? false,
3021
- link: link2,
3207
+ link,
3022
3208
  listStyle: listStyle || null
3023
3209
  };
3024
3210
  };
3025
3211
  var useFormattingState = () => {
3026
3212
  const [state, setState] = useState(null);
3027
- const observer = useMemo(() => EditorView10.updateListener.of((update2) => {
3213
+ const observer = useMemo(() => EditorView11.updateListener.of((update2) => {
3028
3214
  if (update2.docChanged || update2.selectionSet) {
3029
3215
  const newState = getFormatting(update2.state);
3030
3216
  if (!state || !compareFormatting(state, newState)) {
@@ -3038,117 +3224,14 @@ var useFormattingState = () => {
3038
3224
  ];
3039
3225
  };
3040
3226
 
3041
- // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
3042
- import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3043
- import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
3044
- import { Decoration as Decoration6, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
3045
- var buildDecorations2 = (view) => {
3046
- const builder = new RangeSetBuilder3();
3047
- const { state } = view;
3048
- const cursor = state.selection.main.head;
3049
- for (const { from, to } of view.visibleRanges) {
3050
- syntaxTree3(state).iterate({
3051
- enter: (node) => {
3052
- switch (node.name) {
3053
- case "ATXHeading1":
3054
- case "ATXHeading2":
3055
- case "ATXHeading3":
3056
- case "ATXHeading4":
3057
- case "ATXHeading5":
3058
- case "ATXHeading6": {
3059
- const mark2 = node.node.getChild("HeaderMark");
3060
- if (mark2) {
3061
- if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
3062
- builder.add(mark2.from, mark2.to + 1, Decoration6.replace({}));
3063
- }
3064
- }
3065
- }
3066
- }
3067
- },
3068
- from,
3069
- to
3070
- });
3071
- }
3072
- return builder.finish();
3073
- };
3074
- var heading2 = () => {
3075
- return [
3076
- ViewPlugin5.fromClass(class {
3077
- constructor(view) {
3078
- this.decorations = buildDecorations2(view);
3079
- }
3080
- update(update2) {
3081
- this.decorations = buildDecorations2(update2.view);
3082
- }
3083
- }, {
3084
- decorations: (value) => value.decorations
3085
- })
3086
- ];
3087
- };
3088
-
3089
- // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
3090
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3091
- 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({
3094
- "& .cm-hr": {
3095
- // Note that block-level decorations should not have vertical margins,
3096
- borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
3097
- }
3098
- });
3099
- var HorizontalRuleWidget = class extends WidgetType3 {
3100
- toDOM() {
3101
- const el = document.createElement("div");
3102
- el.className = "cm-hr";
3103
- return el;
3104
- }
3105
- };
3106
- var decoration = Decoration7.replace({
3107
- widget: new HorizontalRuleWidget()
3108
- });
3109
- var buildDecorations3 = (view) => {
3110
- const builder = new RangeSetBuilder4();
3111
- const { state } = view;
3112
- const cursor = state.selection.main.head;
3113
- for (const { from, to } of view.visibleRanges) {
3114
- syntaxTree4(state).iterate({
3115
- enter: (node) => {
3116
- if (node.name === "HorizontalRule") {
3117
- if (cursor <= node.from || cursor >= node.to) {
3118
- builder.add(node.from, node.to, decoration);
3119
- }
3120
- }
3121
- },
3122
- from,
3123
- to
3124
- });
3125
- }
3126
- return builder.finish();
3127
- };
3128
- var hr = () => {
3129
- return [
3130
- ViewPlugin6.fromClass(class {
3131
- constructor(view) {
3132
- this.decorations = buildDecorations3(view);
3133
- }
3134
- update(update2) {
3135
- this.decorations = buildDecorations3(update2.view);
3136
- }
3137
- }, {
3138
- decorations: (value) => value.decorations
3139
- }),
3140
- styles5
3141
- ];
3142
- };
3143
-
3144
3227
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3145
- import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3228
+ import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3146
3229
  import { StateField as StateField4 } from "@codemirror/state";
3147
- import { Decoration as Decoration8, EditorView as EditorView12, WidgetType as WidgetType4 } from "@codemirror/view";
3230
+ import { Decoration as Decoration5, EditorView as EditorView12, WidgetType as WidgetType3 } from "@codemirror/view";
3148
3231
  var image = (options = {}) => {
3149
3232
  return StateField4.define({
3150
3233
  create: (state) => {
3151
- return Decoration8.set(buildDecorations4(0, state.doc.length, state));
3234
+ return Decoration5.set(buildDecorations2(0, state.doc.length, state));
3152
3235
  },
3153
3236
  update: (value, tr) => {
3154
3237
  if (!tr.docChanged && !tr.selection) {
@@ -3168,26 +3251,26 @@ var image = (options = {}) => {
3168
3251
  filterFrom: from,
3169
3252
  filterTo: to,
3170
3253
  filter: () => false,
3171
- add: buildDecorations4(from, to, tr.state)
3254
+ add: buildDecorations2(from, to, tr.state)
3172
3255
  });
3173
3256
  },
3174
3257
  provide: (field) => EditorView12.decorations.from(field)
3175
3258
  });
3176
3259
  };
3177
- var buildDecorations4 = (from, to, state) => {
3260
+ var buildDecorations2 = (from, to, state) => {
3178
3261
  const decorations = [];
3179
3262
  const cursor = state.selection.main.head;
3180
- syntaxTree5(state).iterate({
3263
+ syntaxTree3(state).iterate({
3181
3264
  enter: (node) => {
3182
3265
  if (node.name === "Image") {
3183
3266
  const urlNode = node.node.getChild("URL");
3184
3267
  if (urlNode) {
3185
- const hide = state.readOnly || cursor < node.from || cursor > node.to;
3268
+ const hide2 = state.readOnly || cursor < node.from || cursor > node.to;
3186
3269
  const url = state.sliceDoc(urlNode.from, urlNode.to);
3187
- decorations.push(Decoration8.replace({
3270
+ decorations.push(Decoration5.replace({
3188
3271
  block: true,
3189
3272
  widget: new ImageWidget(url)
3190
- }).range(hide ? node.from : node.to, node.to));
3273
+ }).range(hide2 ? node.from : node.to, node.to));
3191
3274
  }
3192
3275
  }
3193
3276
  },
@@ -3196,7 +3279,7 @@ var buildDecorations4 = (from, to, state) => {
3196
3279
  });
3197
3280
  return decorations;
3198
3281
  };
3199
- var ImageWidget = class extends WidgetType4 {
3282
+ var ImageWidget = class extends WidgetType3 {
3200
3283
  constructor(_url) {
3201
3284
  super();
3202
3285
  this._url = _url;
@@ -3214,146 +3297,43 @@ var ImageWidget = class extends WidgetType4 {
3214
3297
  };
3215
3298
 
3216
3299
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
3217
- import { syntaxTree as syntaxTree6 } from "@codemirror/language";
3218
- import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
3219
- import { hoverTooltip as hoverTooltip2, Decoration as Decoration9, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
3300
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3301
+ import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
3220
3302
  import { tooltipContent } from "@dxos/react-ui-theme";
3221
- var link = (options = {}) => {
3222
- const extensions = [
3223
- ViewPlugin7.fromClass(class {
3224
- constructor(view) {
3225
- this.decorations = buildDecorations5(view, options);
3226
- }
3227
- update(update2) {
3228
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
3229
- this.decorations = buildDecorations5(update2.view, options);
3230
- }
3231
- }
3232
- }, {
3233
- decorations: (value) => value.decorations
3234
- })
3235
- ];
3236
- if (options.onHover) {
3237
- extensions.push(
3238
- // https://codemirror.net/examples/tooltip
3239
- // https://codemirror.net/docs/ref/#view.hoverTooltip
3240
- // https://github.com/codemirror/view/blob/main/src/tooltip.ts
3241
- hoverTooltip2((view, pos, side) => {
3242
- const syntax = syntaxTree6(view.state).resolveInner(pos, side);
3243
- let link2 = null;
3244
- for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
3245
- link2 = node.name === "Link" ? node : null;
3246
- }
3247
- const url = link2 && link2.getChild("URL");
3248
- if (!url || !link2) {
3249
- return null;
3250
- }
3251
- const urlText = view.state.sliceDoc(url.from, url.to);
3252
- return {
3253
- pos: link2.from,
3254
- end: link2.to,
3255
- above: true,
3256
- create: () => {
3257
- const el = document.createElement("div");
3258
- el.className = tooltipContent({}, "pli-2 plb-1");
3259
- options.onHover(el, urlText);
3260
- return {
3261
- dom: el,
3262
- offset: {
3263
- x: 0,
3264
- y: 4
3265
- }
3266
- };
3267
- }
3268
- };
3269
- })
3270
- );
3271
- }
3272
- return extensions;
3273
- };
3274
- var LinkButton = class extends WidgetType5 {
3275
- constructor(_url, _onAttach) {
3276
- super();
3277
- this._url = _url;
3278
- this._onAttach = _onAttach;
3279
- }
3280
- eq(other) {
3281
- return this._url === other._url;
3282
- }
3283
- toDOM(view) {
3284
- const el = document.createElement("span");
3285
- this._onAttach(el, this._url);
3286
- return el;
3287
- }
3288
- };
3289
- var LinkText = class extends WidgetType5 {
3290
- constructor(_text, _url) {
3291
- super();
3292
- this._text = _text;
3293
- this._url = _url;
3294
- }
3295
- eq(other) {
3296
- return this._url === other._url;
3303
+ var linkTooltip = (render) => hoverTooltip2((view, pos, side) => {
3304
+ const syntax = syntaxTree4(view.state).resolveInner(pos, side);
3305
+ let link = null;
3306
+ for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
3307
+ link = node.name === "Link" ? node : null;
3308
+ }
3309
+ const url = link && link.getChild("URL");
3310
+ if (!url || !link) {
3311
+ return null;
3297
3312
  }
3298
- toDOM(view) {
3299
- const link2 = document.createElement("a");
3300
- link2.setAttribute("class", "cm-link");
3301
- link2.textContent = this._text;
3302
- if (this._url) {
3303
- link2.setAttribute("rel", "noreferrer");
3304
- link2.setAttribute("target", "_blank");
3305
- link2.href = this._url;
3306
- } else {
3307
- link2.onclick = () => {
3308
- view.dispatch({
3309
- selection: {
3310
- anchor: view.posAtDOM(link2)
3311
- }
3312
- });
3313
+ const urlText = view.state.sliceDoc(url.from, url.to);
3314
+ return {
3315
+ pos: link.from,
3316
+ end: link.to,
3317
+ above: true,
3318
+ create: () => {
3319
+ const el = document.createElement("div");
3320
+ el.className = tooltipContent({}, "pli-2 plb-1");
3321
+ render(el, urlText);
3322
+ return {
3323
+ dom: el,
3324
+ offset: {
3325
+ x: 0,
3326
+ y: 4
3327
+ }
3313
3328
  };
3314
3329
  }
3315
- return link2;
3316
- }
3317
- };
3318
- var buildDecorations5 = (view, options) => {
3319
- const builder = new RangeSetBuilder5();
3320
- const { state } = view;
3321
- const cursor = state.selection.main.head;
3322
- for (const { from, to } of view.visibleRanges) {
3323
- syntaxTree6(state).iterate({
3324
- enter: (node) => {
3325
- if (node.name === "Link") {
3326
- const marks = node.node.getChildren("LinkMark");
3327
- const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
3328
- const urlNode = node.node.getChild("URL");
3329
- const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
3330
- if (!url) {
3331
- return false;
3332
- }
3333
- if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
3334
- builder.add(node.from, node.to, Decoration9.replace({
3335
- widget: new LinkText(text, options.link ? url : void 0)
3336
- }));
3337
- }
3338
- if (options.onRender) {
3339
- builder.add(node.to, node.to, Decoration9.replace({
3340
- widget: new LinkButton(url, options.onRender)
3341
- }));
3342
- }
3343
- return false;
3344
- }
3345
- },
3346
- from,
3347
- to
3348
- });
3349
- }
3350
- return builder.finish();
3351
- };
3330
+ };
3331
+ });
3352
3332
 
3353
3333
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
3354
- import { syntaxTree as syntaxTree7 } from "@codemirror/language";
3355
- import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField5 } from "@codemirror/state";
3356
- import { Decoration as Decoration10, EditorView as EditorView13, WidgetType as WidgetType6 } from "@codemirror/view";
3334
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3335
+ import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField5 } from "@codemirror/state";
3336
+ import { Decoration as Decoration6, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
3357
3337
  var table = (options = {}) => {
3358
3338
  return StateField5.define({
3359
3339
  create: (state) => update(state, options),
@@ -3362,7 +3342,7 @@ var table = (options = {}) => {
3362
3342
  });
3363
3343
  };
3364
3344
  var update = (state, options) => {
3365
- const builder = new RangeSetBuilder6();
3345
+ const builder = new RangeSetBuilder2();
3366
3346
  const cursor = state.selection.main.head;
3367
3347
  const tables = [];
3368
3348
  const getTable = () => tables[tables.length - 1];
@@ -3370,7 +3350,7 @@ var update = (state, options) => {
3370
3350
  const table2 = getTable();
3371
3351
  return table2.rows?.[table2.rows.length - 1];
3372
3352
  };
3373
- syntaxTree7(state).iterate({
3353
+ syntaxTree5(state).iterate({
3374
3354
  enter: (node) => {
3375
3355
  switch (node.name) {
3376
3356
  case "Table": {
@@ -3401,17 +3381,17 @@ var update = (state, options) => {
3401
3381
  }
3402
3382
  });
3403
3383
  tables.forEach((table2) => {
3404
- const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
3405
- hide && builder.add(table2.from, table2.to, Decoration10.replace({
3384
+ const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
3385
+ hide2 && builder.add(table2.from, table2.to, Decoration6.replace({
3406
3386
  widget: new TableWidget(table2)
3407
3387
  }));
3408
- builder.add(table2.from, table2.to, Decoration10.mark({
3388
+ builder.add(table2.from, table2.to, Decoration6.mark({
3409
3389
  class: "cm-table"
3410
3390
  }));
3411
3391
  });
3412
3392
  return builder.finish();
3413
3393
  };
3414
- var TableWidget = class extends WidgetType6 {
3394
+ var TableWidget = class extends WidgetType4 {
3415
3395
  constructor(_table) {
3416
3396
  super();
3417
3397
  this._table = _table;
@@ -3448,103 +3428,6 @@ var TableWidget = class extends WidgetType6 {
3448
3428
  }
3449
3429
  };
3450
3430
 
3451
- // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
3452
- import { syntaxTree as syntaxTree8 } from "@codemirror/language";
3453
- 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({
3456
- "& .cm-task": {
3457
- color: getToken("extend.colors.blue.500")
3458
- },
3459
- "& .cm-task-checkbox": {
3460
- marginLeft: "4px",
3461
- marginRight: "4px"
3462
- }
3463
- });
3464
- var CheckboxWidget = class extends WidgetType7 {
3465
- constructor(_checked) {
3466
- super();
3467
- this._checked = _checked;
3468
- }
3469
- eq(other) {
3470
- return this._checked === other._checked;
3471
- }
3472
- toDOM(view) {
3473
- const input = document.createElement("input");
3474
- input.className = "cm-task-checkbox";
3475
- input.type = "checkbox";
3476
- input.checked = this._checked;
3477
- if (view.state.readOnly) {
3478
- input.setAttribute("disabled", "true");
3479
- } else {
3480
- input.onmousedown = (event) => {
3481
- const pos = view.posAtDOM(input);
3482
- const text = view.state.sliceDoc(pos, pos + 3);
3483
- if (text === (this._checked ? "[x]" : "[ ]")) {
3484
- view.dispatch({
3485
- changes: {
3486
- from: pos + 1,
3487
- to: pos + 2,
3488
- insert: this._checked ? " " : "x"
3489
- }
3490
- });
3491
- event.preventDefault();
3492
- }
3493
- };
3494
- }
3495
- return input;
3496
- }
3497
- ignoreEvent() {
3498
- return false;
3499
- }
3500
- };
3501
- var checkedDecoration = Decoration11.replace({
3502
- widget: new CheckboxWidget(true)
3503
- });
3504
- var uncheckedDecoration = Decoration11.replace({
3505
- widget: new CheckboxWidget(false)
3506
- });
3507
- var buildDecorations6 = (view) => {
3508
- const builder = new RangeSetBuilder7();
3509
- const { state } = view;
3510
- const cursor = state.selection.main.head;
3511
- for (const { from, to } of view.visibleRanges) {
3512
- syntaxTree8(state).iterate({
3513
- enter: (node) => {
3514
- if (node.name === "TaskMarker") {
3515
- if (cursor < node.from || cursor > node.to) {
3516
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
3517
- builder.add(node.from - 2, node.from - 1, Decoration11.mark({
3518
- class: "cm-task"
3519
- }));
3520
- builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
3521
- }
3522
- }
3523
- },
3524
- from,
3525
- to
3526
- });
3527
- }
3528
- return builder.finish();
3529
- };
3530
- var tasklist = (options = {}) => {
3531
- return [
3532
- ViewPlugin8.fromClass(class {
3533
- constructor(view) {
3534
- this.decorations = buildDecorations6(view);
3535
- }
3536
- update(update2) {
3537
- if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
3538
- this.decorations = buildDecorations6(update2.view);
3539
- }
3540
- }
3541
- }, {
3542
- decorations: (value) => value.decorations
3543
- }),
3544
- styles6
3545
- ];
3546
- };
3547
-
3548
3431
  // packages/ui/react-ui-editor/src/extensions/mention.ts
3549
3432
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
3550
3433
  var mention = ({ onSearch }) => {
@@ -3575,10 +3458,10 @@ var mention = ({ onSearch }) => {
3575
3458
  };
3576
3459
 
3577
3460
  // packages/ui/react-ui-editor/src/extensions/modes.ts
3578
- import { Facet as Facet4 } from "@codemirror/state";
3461
+ import { Facet as Facet5 } from "@codemirror/state";
3579
3462
  import { keymap as keymap6 } from "@codemirror/view";
3580
3463
  import { vim } from "@replit/codemirror-vim";
3581
- var editorMode = Facet4.define({
3464
+ var editorMode = Facet5.define({
3582
3465
  combine: (modes) => modes[0] ?? {}
3583
3466
  });
3584
3467
  var EditorModes = {
@@ -3942,7 +3825,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3942
3825
  selection,
3943
3826
  extensions: [
3944
3827
  // TODO(burdon): Doesn't catch errors in keymap functions.
3945
- EditorView15.exceptionSink.of((err) => {
3828
+ EditorView14.exceptionSink.of((err) => {
3946
3829
  log3.catch(err, void 0, {
3947
3830
  F: __dxlog_file4,
3948
3831
  L: 113,
@@ -3952,17 +3835,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3952
3835
  }),
3953
3836
  // Theme.
3954
3837
  // TODO(burdon): Make configurable.
3955
- EditorView15.baseTheme(defaultTheme),
3956
- EditorView15.theme(theme ?? {}),
3957
- EditorView15.darkTheme.of(themeMode === "dark"),
3958
- EditorView15.editorAttributes.of({
3838
+ EditorView14.baseTheme(defaultTheme),
3839
+ EditorView14.theme(theme ?? {}),
3840
+ EditorView14.darkTheme.of(themeMode === "dark"),
3841
+ EditorView14.editorAttributes.of({
3959
3842
  class: slots.editor?.className ?? ""
3960
3843
  }),
3961
- EditorView15.contentAttributes.of({
3844
+ EditorView14.contentAttributes.of({
3962
3845
  class: slots.content?.className ?? ""
3963
3846
  }),
3964
3847
  // Focus.
3965
- EditorView15.updateListener.of((update2) => {
3848
+ EditorView14.updateListener.of((update2) => {
3966
3849
  update2.transactions.forEach((transaction) => {
3967
3850
  if (transaction.isUserEvent("focus.container")) {
3968
3851
  rootRef.current?.focus();
@@ -3970,7 +3853,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3970
3853
  });
3971
3854
  }),
3972
3855
  // State.
3973
- EditorView15.editable.of(!readonly),
3856
+ EditorView14.editable.of(!readonly),
3974
3857
  EditorState2.readOnly.of(!!readonly),
3975
3858
  // Storage and replication.
3976
3859
  // NOTE: This must come before user extensions.
@@ -3979,7 +3862,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3979
3862
  ...extensions
3980
3863
  ].filter(isNotFalsy4)
3981
3864
  });
3982
- const newView = new EditorView15({
3865
+ const newView = new EditorView14({
3983
3866
  state,
3984
3867
  parent: rootRef.current,
3985
3868
  scrollTo,
@@ -4301,8 +4184,7 @@ var markdownBlocks = [
4301
4184
  {
4302
4185
  type: "codeblock",
4303
4186
  Icon: CodeBlock,
4304
- getState: (state) => state.blockType === "codeblock",
4305
- disabled: (state) => !state.blankLine
4187
+ getState: (state) => state.blockType === "codeblock"
4306
4188
  },
4307
4189
  {
4308
4190
  type: "table",
@@ -4535,7 +4417,7 @@ var useEditorView = (id) => {
4535
4417
 
4536
4418
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4537
4419
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4538
- import { EditorView as EditorView16 } from "@codemirror/view";
4420
+ import { EditorView as EditorView15 } from "@codemirror/view";
4539
4421
  import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
4540
4422
  import { log as log5 } from "@dxos/log";
4541
4423
  import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
@@ -4552,7 +4434,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
4552
4434
  selection,
4553
4435
  extensions: [
4554
4436
  // TODO(burdon): Doesn't catch errors in keymap functions.
4555
- EditorView16.exceptionSink.of((err) => {
4437
+ EditorView15.exceptionSink.of((err) => {
4556
4438
  log5.catch(err, void 0, {
4557
4439
  F: __dxlog_file6,
4558
4440
  L: 60,
@@ -4561,12 +4443,12 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
4561
4443
  });
4562
4444
  }),
4563
4445
  extensions,
4564
- EditorView16.updateListener.of(() => {
4446
+ EditorView15.updateListener.of(() => {
4565
4447
  onUpdate.current?.();
4566
4448
  })
4567
4449
  ].filter(isNotFalsy5)
4568
4450
  });
4569
- view2 = new EditorView16({
4451
+ view2 = new EditorView15({
4570
4452
  parent: parentRef.current,
4571
4453
  scrollTo,
4572
4454
  state,
@@ -4624,19 +4506,19 @@ var createDataExtensions = ({ readonly = false } = {}) => {
4624
4506
  return [
4625
4507
  //
4626
4508
  EditorState3.readOnly.of(readonly),
4627
- EditorView16.editable.of(!readonly)
4509
+ EditorView15.editable.of(!readonly)
4628
4510
  ];
4629
4511
  };
4630
4512
  var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4631
4513
  return [
4632
4514
  //
4633
- EditorView16.baseTheme(defaultTheme),
4634
- theme && EditorView16.theme(theme),
4635
- EditorView16.darkTheme.of(themeMode === "dark"),
4636
- EditorView16.editorAttributes.of({
4515
+ EditorView15.baseTheme(defaultTheme),
4516
+ theme && EditorView15.theme(theme),
4517
+ EditorView15.darkTheme.of(themeMode === "dark"),
4518
+ EditorView15.editorAttributes.of({
4637
4519
  class: slots?.editor?.className ?? ""
4638
4520
  }),
4639
- EditorView16.contentAttributes.of({
4521
+ EditorView15.contentAttributes.of({
4640
4522
  class: slots?.content?.className ?? ""
4641
4523
  })
4642
4524
  ].filter(isNotFalsy5);
@@ -4736,7 +4618,6 @@ export {
4736
4618
  awarenessProvider,
4737
4619
  blast,
4738
4620
  callbackWrapper,
4739
- code2 as code,
4740
4621
  codeTheme,
4741
4622
  comments,
4742
4623
  compareFormatting,
@@ -4745,6 +4626,9 @@ export {
4745
4626
  createDataExtensions,
4746
4627
  createMarkdownExtensions,
4747
4628
  createThemeExtensions,
4629
+ cursorLineMargin,
4630
+ cursorLineMarginFacet,
4631
+ decorateMarkdown,
4748
4632
  defaultMarkdownSlots,
4749
4633
  defaultOptions,
4750
4634
  defaultSlots,
@@ -4759,12 +4643,10 @@ export {
4759
4643
  formatting,
4760
4644
  getFormatting,
4761
4645
  getToken,
4762
- heading2 as heading,
4763
- hr,
4764
4646
  image,
4765
4647
  insertTable,
4766
4648
  keymap8 as keymap,
4767
- link,
4649
+ linkTooltip,
4768
4650
  listener,
4769
4651
  logChanges,
4770
4652
  markdownHighlightStyle,
@@ -4786,7 +4668,6 @@ export {
4786
4668
  setStyle,
4787
4669
  table,
4788
4670
  tags2 as tags,
4789
- tasklist,
4790
4671
  textTheme,
4791
4672
  toggleBlockquote,
4792
4673
  toggleCodeblock,