@dxos/react-ui-editor 0.4.5-main.519e45d → 0.4.5-main.5fcdf8d

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 (34) hide show
  1. package/dist/lib/browser/index.mjs +505 -291
  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.stories.d.ts.map +1 -1
  8. package/dist/types/src/extensions/markdown/code.d.ts +2 -0
  9. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  10. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  11. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  12. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  13. package/dist/types/src/extensions/markdown/hr.d.ts +2 -0
  14. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  15. package/dist/types/src/extensions/markdown/index.d.ts +4 -1
  16. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  17. package/dist/types/src/extensions/markdown/link.d.ts +11 -1
  18. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  19. package/dist/types/src/extensions/markdown/tasklist.d.ts +3 -0
  20. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  21. package/package.json +24 -24
  22. package/src/components/TextEditor/MarkdownEditor.stories.tsx +37 -29
  23. package/src/components/TextEditor/hooks.stories.tsx +10 -2
  24. package/src/components/Toolbar/Toolbar.stories.tsx +6 -2
  25. package/src/extensions/markdown/code.ts +162 -0
  26. package/src/extensions/markdown/formatting.ts +64 -1
  27. package/src/extensions/markdown/heading.ts +59 -0
  28. package/src/extensions/markdown/hr.ts +71 -0
  29. package/src/extensions/markdown/index.ts +4 -1
  30. package/src/extensions/markdown/link.ts +185 -23
  31. package/src/extensions/markdown/tasklist.ts +110 -0
  32. package/dist/types/src/extensions/markdown/decorate.d.ts +0 -5
  33. package/dist/types/src/extensions/markdown/decorate.d.ts.map +0 -1
  34. package/src/extensions/markdown/decorate.ts +0 -301
@@ -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 EditorView14 } 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
14
  import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2, useMemo as useMemo2 } from "react";
@@ -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: top = 0 } = view.scrollDOM.parentElement ?? {};
768
+ const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
769
769
  const point = {
770
770
  x: x - left,
771
- y: y - top
771
+ y: y - top2
772
772
  };
773
773
  return {
774
774
  element,
@@ -1871,207 +1871,12 @@ var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrap
1871
1871
  ].filter(isNotFalsy3);
1872
1872
  };
1873
1873
 
1874
- // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
1874
+ // packages/ui/react-ui-editor/src/extensions/markdown/code.ts
1875
1875
  import { syntaxTree } from "@codemirror/language";
1876
1876
  import { RangeSetBuilder } from "@codemirror/state";
1877
- import { EditorView as EditorView10, Decoration as Decoration4, WidgetType as WidgetType2, ViewPlugin as ViewPlugin3 } from "@codemirror/view";
1877
+ import { ViewPlugin as ViewPlugin3, EditorView as EditorView10, Decoration as Decoration4, WidgetType as WidgetType2 } from "@codemirror/view";
1878
1878
  import { mx as mx2 } from "@dxos/react-ui-theme";
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 = " ";
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({
1879
+ var styles4 = EditorView10.baseTheme({
2075
1880
  "& .cm-code": {
2076
1881
  fontFamily: getToken("fontFamily.mono", []).join(",")
2077
1882
  },
@@ -2119,28 +1924,82 @@ var formattingStyles = EditorView10.baseTheme({
2119
1924
  "&::after": {
2120
1925
  background: getToken("extend.semanticColors.input.dark")
2121
1926
  }
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"
2136
1927
  }
2137
1928
  });
1929
+ var getLineRange = (lines, from, to) => {
1930
+ const start = lines.findIndex((line) => line.from >= from);
1931
+ const end = lines.findIndex((line) => line.to >= to);
1932
+ return [
1933
+ start,
1934
+ end
1935
+ ];
1936
+ };
1937
+ var LineWidget = class extends WidgetType2 {
1938
+ constructor(_className) {
1939
+ super();
1940
+ this._className = _className;
1941
+ }
1942
+ toDOM() {
1943
+ const el = document.createElement("span");
1944
+ el.innerHTML = "&nbsp;";
1945
+ el.className = mx2("cm-code cm-codeblock", this._className);
1946
+ return el;
1947
+ }
1948
+ };
1949
+ var top = new LineWidget("cm-codeblock-end cm-codeblock-first");
1950
+ var bottom = new LineWidget("cm-codeblock-end cm-codeblock-last");
1951
+ var buildDecorations = (view) => {
1952
+ const builder = new RangeSetBuilder();
1953
+ const { state } = view;
1954
+ const blocks = view.viewportLineBlocks;
1955
+ const cursor = state.selection.main.head;
1956
+ for (const { from, to } of view.visibleRanges) {
1957
+ syntaxTree(state).iterate({
1958
+ enter: (node) => {
1959
+ if (node.name === "FencedCode") {
1960
+ const editing = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
1961
+ const range = getLineRange(blocks, node.from, node.to);
1962
+ for (let i = range[0]; i <= range[1]; i++) {
1963
+ const block = blocks[i];
1964
+ if (!editing && (i === range[0] || i === range[1])) {
1965
+ builder.add(block.from, block.to, Decoration4.replace({
1966
+ widget: i === range[0] ? top : bottom
1967
+ }));
1968
+ } else {
1969
+ builder.add(block.from, block.from, Decoration4.line({
1970
+ class: mx2("cm-code cm-codeblock-line", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
1971
+ }));
1972
+ }
1973
+ }
1974
+ }
1975
+ },
1976
+ from,
1977
+ to
1978
+ });
1979
+ }
1980
+ return builder.finish();
1981
+ };
1982
+ var code2 = () => {
1983
+ return [
1984
+ ViewPlugin3.fromClass(class {
1985
+ constructor(view) {
1986
+ this.decorations = buildDecorations(view);
1987
+ }
1988
+ update(update2) {
1989
+ this.decorations = buildDecorations(update2.view);
1990
+ }
1991
+ }, {
1992
+ decorations: (value) => value.decorations
1993
+ }),
1994
+ styles4
1995
+ ];
1996
+ };
2138
1997
 
2139
1998
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
2140
1999
  import { snippet } from "@codemirror/autocomplete";
2141
2000
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2142
- import { EditorSelection } from "@codemirror/state";
2143
- import { EditorView as EditorView11, keymap as keymap5 } from "@codemirror/view";
2001
+ import { RangeSetBuilder as RangeSetBuilder2, EditorSelection } from "@codemirror/state";
2002
+ import { Decoration as Decoration5, EditorView as EditorView11, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
2144
2003
  import { useState, useMemo } from "react";
2145
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;
2146
2005
  var Inline;
@@ -3017,7 +2876,56 @@ var formatting = (options = {}) => {
3017
2876
  key: "meta-b",
3018
2877
  run: toggleStrong
3019
2878
  }
3020
- ])
2879
+ ]),
2880
+ styling()
2881
+ ];
2882
+ };
2883
+ var styling = () => {
2884
+ const buildDecorations7 = (view) => {
2885
+ const builder = new RangeSetBuilder2();
2886
+ const { state } = view;
2887
+ const cursor = state.selection.main.head;
2888
+ const replace = (node, marks) => {
2889
+ if (cursor <= node.from || cursor >= node.to) {
2890
+ for (const mark2 of marks) {
2891
+ builder.add(mark2.from, mark2.to, Decoration5.replace({}));
2892
+ }
2893
+ }
2894
+ };
2895
+ for (const { from, to } of view.visibleRanges) {
2896
+ syntaxTree2(state).iterate({
2897
+ enter: (node) => {
2898
+ switch (node.name) {
2899
+ case "Emphasis":
2900
+ case "StrongEmphasis": {
2901
+ const marks = node.node.getChildren("EmphasisMark");
2902
+ replace(node, marks);
2903
+ break;
2904
+ }
2905
+ case "Strikethrough": {
2906
+ const marks = node.node.getChildren("StrikethroughMark");
2907
+ replace(node, marks);
2908
+ break;
2909
+ }
2910
+ }
2911
+ },
2912
+ from,
2913
+ to
2914
+ });
2915
+ }
2916
+ return builder.finish();
2917
+ };
2918
+ return [
2919
+ ViewPlugin4.fromClass(class {
2920
+ constructor(view) {
2921
+ this.decorations = buildDecorations7(view);
2922
+ }
2923
+ update(update2) {
2924
+ this.decorations = buildDecorations7(update2.view);
2925
+ }
2926
+ }, {
2927
+ decorations: (value) => value.decorations
2928
+ })
3021
2929
  ];
3022
2930
  };
3023
2931
  var InlineMarker = {
@@ -3067,7 +2975,7 @@ var getFormatting = (state) => {
3067
2975
  null,
3068
2976
  null
3069
2977
  ];
3070
- let link = false;
2978
+ let link2 = false;
3071
2979
  let blockQuote = null;
3072
2980
  let listStyle = null;
3073
2981
  const stack = [];
@@ -3129,7 +3037,7 @@ var getFormatting = (state) => {
3129
3037
  if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
3130
3038
  stack.push(name);
3131
3039
  } else if (name === "Link") {
3132
- link = true;
3040
+ link2 = true;
3133
3041
  } else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
3134
3042
  if (name === "Task" && stack[stack.length - 1] === "BulletList") {
3135
3043
  stack[stack.length - 1] = "TaskList";
@@ -3204,7 +3112,7 @@ var getFormatting = (state) => {
3204
3112
  emphasis: inline[1] ?? false,
3205
3113
  strong: inline[0] ?? false,
3206
3114
  strikethrough: inline[2] ?? false,
3207
- link,
3115
+ link: link2,
3208
3116
  listStyle: listStyle || null
3209
3117
  };
3210
3118
  };
@@ -3224,14 +3132,117 @@ var useFormattingState = () => {
3224
3132
  ];
3225
3133
  };
3226
3134
 
3227
- // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3135
+ // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
3228
3136
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
3137
+ import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
3138
+ import { Decoration as Decoration6, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
3139
+ var buildDecorations2 = (view) => {
3140
+ const builder = new RangeSetBuilder3();
3141
+ const { state } = view;
3142
+ const cursor = state.selection.main.head;
3143
+ for (const { from, to } of view.visibleRanges) {
3144
+ syntaxTree3(state).iterate({
3145
+ enter: (node) => {
3146
+ switch (node.name) {
3147
+ case "ATXHeading1":
3148
+ case "ATXHeading2":
3149
+ case "ATXHeading3":
3150
+ case "ATXHeading4":
3151
+ case "ATXHeading5":
3152
+ case "ATXHeading6": {
3153
+ const mark2 = node.node.getChild("HeaderMark");
3154
+ if (mark2) {
3155
+ if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
3156
+ builder.add(mark2.from, mark2.to + 1, Decoration6.replace({}));
3157
+ }
3158
+ }
3159
+ }
3160
+ }
3161
+ },
3162
+ from,
3163
+ to
3164
+ });
3165
+ }
3166
+ return builder.finish();
3167
+ };
3168
+ var heading2 = () => {
3169
+ return [
3170
+ ViewPlugin5.fromClass(class {
3171
+ constructor(view) {
3172
+ this.decorations = buildDecorations2(view);
3173
+ }
3174
+ update(update2) {
3175
+ this.decorations = buildDecorations2(update2.view);
3176
+ }
3177
+ }, {
3178
+ decorations: (value) => value.decorations
3179
+ })
3180
+ ];
3181
+ };
3182
+
3183
+ // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
3184
+ import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3185
+ import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
3186
+ import { Decoration as Decoration7, EditorView as EditorView12, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
3187
+ var styles5 = EditorView12.baseTheme({
3188
+ "& .cm-hr": {
3189
+ // Note that block-level decorations should not have vertical margins,
3190
+ borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
3191
+ }
3192
+ });
3193
+ var HorizontalRuleWidget = class extends WidgetType3 {
3194
+ toDOM() {
3195
+ const el = document.createElement("div");
3196
+ el.className = "cm-hr";
3197
+ return el;
3198
+ }
3199
+ };
3200
+ var decoration = Decoration7.replace({
3201
+ widget: new HorizontalRuleWidget()
3202
+ });
3203
+ var buildDecorations3 = (view) => {
3204
+ const builder = new RangeSetBuilder4();
3205
+ const { state } = view;
3206
+ const cursor = state.selection.main.head;
3207
+ for (const { from, to } of view.visibleRanges) {
3208
+ syntaxTree4(state).iterate({
3209
+ enter: (node) => {
3210
+ if (node.name === "HorizontalRule") {
3211
+ if (cursor <= node.from || cursor >= node.to) {
3212
+ builder.add(node.from, node.to, decoration);
3213
+ }
3214
+ }
3215
+ },
3216
+ from,
3217
+ to
3218
+ });
3219
+ }
3220
+ return builder.finish();
3221
+ };
3222
+ var hr = () => {
3223
+ return [
3224
+ ViewPlugin6.fromClass(class {
3225
+ constructor(view) {
3226
+ this.decorations = buildDecorations3(view);
3227
+ }
3228
+ update(update2) {
3229
+ this.decorations = buildDecorations3(update2.view);
3230
+ }
3231
+ }, {
3232
+ decorations: (value) => value.decorations
3233
+ }),
3234
+ styles5
3235
+ ];
3236
+ };
3237
+
3238
+ // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
3239
+ import { syntaxTree as syntaxTree5 } from "@codemirror/language";
3229
3240
  import { StateField as StateField4 } from "@codemirror/state";
3230
- import { Decoration as Decoration5, EditorView as EditorView12, WidgetType as WidgetType3 } from "@codemirror/view";
3241
+ import { Decoration as Decoration8, EditorView as EditorView13, WidgetType as WidgetType4 } from "@codemirror/view";
3231
3242
  var image = (options = {}) => {
3232
3243
  return StateField4.define({
3233
3244
  create: (state) => {
3234
- return Decoration5.set(buildDecorations2(0, state.doc.length, state));
3245
+ return Decoration8.set(buildDecorations4(0, state.doc.length, state));
3235
3246
  },
3236
3247
  update: (value, tr) => {
3237
3248
  if (!tr.docChanged && !tr.selection) {
@@ -3251,26 +3262,26 @@ var image = (options = {}) => {
3251
3262
  filterFrom: from,
3252
3263
  filterTo: to,
3253
3264
  filter: () => false,
3254
- add: buildDecorations2(from, to, tr.state)
3265
+ add: buildDecorations4(from, to, tr.state)
3255
3266
  });
3256
3267
  },
3257
- provide: (field) => EditorView12.decorations.from(field)
3268
+ provide: (field) => EditorView13.decorations.from(field)
3258
3269
  });
3259
3270
  };
3260
- var buildDecorations2 = (from, to, state) => {
3271
+ var buildDecorations4 = (from, to, state) => {
3261
3272
  const decorations = [];
3262
3273
  const cursor = state.selection.main.head;
3263
- syntaxTree3(state).iterate({
3274
+ syntaxTree5(state).iterate({
3264
3275
  enter: (node) => {
3265
3276
  if (node.name === "Image") {
3266
3277
  const urlNode = node.node.getChild("URL");
3267
3278
  if (urlNode) {
3268
- const hide2 = state.readOnly || cursor < node.from || cursor > node.to;
3279
+ const hide = state.readOnly || cursor < node.from || cursor > node.to;
3269
3280
  const url = state.sliceDoc(urlNode.from, urlNode.to);
3270
- decorations.push(Decoration5.replace({
3281
+ decorations.push(Decoration8.replace({
3271
3282
  block: true,
3272
3283
  widget: new ImageWidget(url)
3273
- }).range(hide2 ? node.from : node.to, node.to));
3284
+ }).range(hide ? node.from : node.to, node.to));
3274
3285
  }
3275
3286
  }
3276
3287
  },
@@ -3279,7 +3290,7 @@ var buildDecorations2 = (from, to, state) => {
3279
3290
  });
3280
3291
  return decorations;
3281
3292
  };
3282
- var ImageWidget = class extends WidgetType3 {
3293
+ var ImageWidget = class extends WidgetType4 {
3283
3294
  constructor(_url) {
3284
3295
  super();
3285
3296
  this._url = _url;
@@ -3297,52 +3308,155 @@ var ImageWidget = class extends WidgetType3 {
3297
3308
  };
3298
3309
 
3299
3310
  // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
3300
- import { syntaxTree as syntaxTree4 } from "@codemirror/language";
3301
- import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
3311
+ import { syntaxTree as syntaxTree6 } from "@codemirror/language";
3312
+ import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
3313
+ import { hoverTooltip as hoverTooltip2, Decoration as Decoration9, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
3302
3314
  import { tooltipContent } from "@dxos/react-ui-theme";
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;
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
3315
+ var link = (options = {}) => {
3316
+ const extensions = [
3317
+ ViewPlugin7.fromClass(class {
3318
+ constructor(view) {
3319
+ this.decorations = buildDecorations5(view, options);
3320
+ }
3321
+ update(update2) {
3322
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
3323
+ this.decorations = buildDecorations5(update2.view, options);
3327
3324
  }
3325
+ }
3326
+ }, {
3327
+ decorations: (value) => value.decorations
3328
+ })
3329
+ ];
3330
+ if (options.onHover) {
3331
+ extensions.push(
3332
+ // https://codemirror.net/examples/tooltip
3333
+ // https://codemirror.net/docs/ref/#view.hoverTooltip
3334
+ // https://github.com/codemirror/view/blob/main/src/tooltip.ts
3335
+ hoverTooltip2((view, pos, side) => {
3336
+ const syntax = syntaxTree6(view.state).resolveInner(pos, side);
3337
+ let link2 = null;
3338
+ for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
3339
+ link2 = node.name === "Link" ? node : null;
3340
+ }
3341
+ const url = link2 && link2.getChild("URL");
3342
+ if (!url || !link2) {
3343
+ return null;
3344
+ }
3345
+ const urlText = view.state.sliceDoc(url.from, url.to);
3346
+ return {
3347
+ pos: link2.from,
3348
+ end: link2.to,
3349
+ above: true,
3350
+ create: () => {
3351
+ const el = document.createElement("div");
3352
+ el.className = tooltipContent({}, "pli-2 plb-1");
3353
+ options.onHover(el, urlText);
3354
+ return {
3355
+ dom: el,
3356
+ offset: {
3357
+ x: 0,
3358
+ y: 4
3359
+ }
3360
+ };
3361
+ }
3362
+ };
3363
+ })
3364
+ );
3365
+ }
3366
+ return extensions;
3367
+ };
3368
+ var LinkButton = class extends WidgetType5 {
3369
+ constructor(_url, _onAttach) {
3370
+ super();
3371
+ this._url = _url;
3372
+ this._onAttach = _onAttach;
3373
+ }
3374
+ eq(other) {
3375
+ return this._url === other._url;
3376
+ }
3377
+ toDOM(view) {
3378
+ const el = document.createElement("span");
3379
+ this._onAttach(el, this._url);
3380
+ return el;
3381
+ }
3382
+ };
3383
+ var LinkText = class extends WidgetType5 {
3384
+ constructor(_text, _url) {
3385
+ super();
3386
+ this._text = _text;
3387
+ this._url = _url;
3388
+ }
3389
+ eq(other) {
3390
+ return this._url === other._url;
3391
+ }
3392
+ toDOM(view) {
3393
+ const link2 = document.createElement("a");
3394
+ link2.setAttribute("class", "cm-link");
3395
+ link2.textContent = this._text;
3396
+ if (this._url) {
3397
+ link2.setAttribute("rel", "noreferrer");
3398
+ link2.setAttribute("target", "_blank");
3399
+ link2.href = this._url;
3400
+ } else {
3401
+ link2.onclick = () => {
3402
+ view.dispatch({
3403
+ selection: {
3404
+ anchor: view.posAtDOM(link2)
3405
+ }
3406
+ });
3328
3407
  };
3329
3408
  }
3330
- };
3331
- });
3409
+ return link2;
3410
+ }
3411
+ };
3412
+ var buildDecorations5 = (view, options) => {
3413
+ const builder = new RangeSetBuilder5();
3414
+ const { state } = view;
3415
+ const cursor = state.selection.main.head;
3416
+ for (const { from, to } of view.visibleRanges) {
3417
+ syntaxTree6(state).iterate({
3418
+ enter: (node) => {
3419
+ if (node.name === "Link") {
3420
+ const marks = node.node.getChildren("LinkMark");
3421
+ const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
3422
+ const urlNode = node.node.getChild("URL");
3423
+ const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
3424
+ if (!url) {
3425
+ return false;
3426
+ }
3427
+ if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
3428
+ builder.add(node.from, node.to, Decoration9.replace({
3429
+ widget: new LinkText(text, options.link ? url : void 0)
3430
+ }));
3431
+ }
3432
+ if (options.onRender) {
3433
+ builder.add(node.to, node.to, Decoration9.replace({
3434
+ widget: new LinkButton(url, options.onRender)
3435
+ }));
3436
+ }
3437
+ return false;
3438
+ }
3439
+ },
3440
+ from,
3441
+ to
3442
+ });
3443
+ }
3444
+ return builder.finish();
3445
+ };
3332
3446
 
3333
3447
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
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";
3448
+ import { syntaxTree as syntaxTree7 } from "@codemirror/language";
3449
+ import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField5 } from "@codemirror/state";
3450
+ import { Decoration as Decoration10, EditorView as EditorView14, WidgetType as WidgetType6 } from "@codemirror/view";
3337
3451
  var table = (options = {}) => {
3338
3452
  return StateField5.define({
3339
3453
  create: (state) => update(state, options),
3340
3454
  update: (_, tr) => update(tr.state, options),
3341
- provide: (field) => EditorView13.decorations.from(field)
3455
+ provide: (field) => EditorView14.decorations.from(field)
3342
3456
  });
3343
3457
  };
3344
3458
  var update = (state, options) => {
3345
- const builder = new RangeSetBuilder2();
3459
+ const builder = new RangeSetBuilder6();
3346
3460
  const cursor = state.selection.main.head;
3347
3461
  const tables = [];
3348
3462
  const getTable = () => tables[tables.length - 1];
@@ -3350,7 +3464,7 @@ var update = (state, options) => {
3350
3464
  const table2 = getTable();
3351
3465
  return table2.rows?.[table2.rows.length - 1];
3352
3466
  };
3353
- syntaxTree5(state).iterate({
3467
+ syntaxTree7(state).iterate({
3354
3468
  enter: (node) => {
3355
3469
  switch (node.name) {
3356
3470
  case "Table": {
@@ -3381,17 +3495,17 @@ var update = (state, options) => {
3381
3495
  }
3382
3496
  });
3383
3497
  tables.forEach((table2) => {
3384
- const hide2 = state.readOnly || cursor < table2.from || cursor > table2.to;
3385
- hide2 && builder.add(table2.from, table2.to, Decoration6.replace({
3498
+ const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
3499
+ hide && builder.add(table2.from, table2.to, Decoration10.replace({
3386
3500
  widget: new TableWidget(table2)
3387
3501
  }));
3388
- builder.add(table2.from, table2.to, Decoration6.mark({
3502
+ builder.add(table2.from, table2.to, Decoration10.mark({
3389
3503
  class: "cm-table"
3390
3504
  }));
3391
3505
  });
3392
3506
  return builder.finish();
3393
3507
  };
3394
- var TableWidget = class extends WidgetType4 {
3508
+ var TableWidget = class extends WidgetType6 {
3395
3509
  constructor(_table) {
3396
3510
  super();
3397
3511
  this._table = _table;
@@ -3428,6 +3542,103 @@ var TableWidget = class extends WidgetType4 {
3428
3542
  }
3429
3543
  };
3430
3544
 
3545
+ // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
3546
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
3547
+ import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
3548
+ import { EditorView as EditorView15, Decoration as Decoration11, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
3549
+ var styles6 = EditorView15.baseTheme({
3550
+ "& .cm-task": {
3551
+ color: getToken("extend.colors.blue.500")
3552
+ },
3553
+ "& .cm-task-checkbox": {
3554
+ marginLeft: "4px",
3555
+ marginRight: "4px"
3556
+ }
3557
+ });
3558
+ var CheckboxWidget = class extends WidgetType7 {
3559
+ constructor(_checked) {
3560
+ super();
3561
+ this._checked = _checked;
3562
+ }
3563
+ eq(other) {
3564
+ return this._checked === other._checked;
3565
+ }
3566
+ toDOM(view) {
3567
+ const input = document.createElement("input");
3568
+ input.className = "cm-task-checkbox";
3569
+ input.type = "checkbox";
3570
+ input.checked = this._checked;
3571
+ if (view.state.readOnly) {
3572
+ input.setAttribute("disabled", "true");
3573
+ } else {
3574
+ input.onmousedown = (event) => {
3575
+ const pos = view.posAtDOM(input);
3576
+ const text = view.state.sliceDoc(pos, pos + 3);
3577
+ if (text === (this._checked ? "[x]" : "[ ]")) {
3578
+ view.dispatch({
3579
+ changes: {
3580
+ from: pos + 1,
3581
+ to: pos + 2,
3582
+ insert: this._checked ? " " : "x"
3583
+ }
3584
+ });
3585
+ event.preventDefault();
3586
+ }
3587
+ };
3588
+ }
3589
+ return input;
3590
+ }
3591
+ ignoreEvent() {
3592
+ return false;
3593
+ }
3594
+ };
3595
+ var checkedDecoration = Decoration11.replace({
3596
+ widget: new CheckboxWidget(true)
3597
+ });
3598
+ var uncheckedDecoration = Decoration11.replace({
3599
+ widget: new CheckboxWidget(false)
3600
+ });
3601
+ var buildDecorations6 = (view) => {
3602
+ const builder = new RangeSetBuilder7();
3603
+ const { state } = view;
3604
+ const cursor = state.selection.main.head;
3605
+ for (const { from, to } of view.visibleRanges) {
3606
+ syntaxTree8(state).iterate({
3607
+ enter: (node) => {
3608
+ if (node.name === "TaskMarker") {
3609
+ if (cursor < node.from || cursor > node.to) {
3610
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
3611
+ builder.add(node.from - 2, node.from - 1, Decoration11.mark({
3612
+ class: "cm-task"
3613
+ }));
3614
+ builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
3615
+ }
3616
+ }
3617
+ },
3618
+ from,
3619
+ to
3620
+ });
3621
+ }
3622
+ return builder.finish();
3623
+ };
3624
+ var tasklist = (options = {}) => {
3625
+ return [
3626
+ ViewPlugin8.fromClass(class {
3627
+ constructor(view) {
3628
+ this.decorations = buildDecorations6(view);
3629
+ }
3630
+ update(update2) {
3631
+ if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
3632
+ this.decorations = buildDecorations6(update2.view);
3633
+ }
3634
+ }
3635
+ }, {
3636
+ decorations: (value) => value.decorations
3637
+ }),
3638
+ styles6
3639
+ ];
3640
+ };
3641
+
3431
3642
  // packages/ui/react-ui-editor/src/extensions/mention.ts
3432
3643
  import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
3433
3644
  var mention = ({ onSearch }) => {
@@ -3825,7 +4036,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3825
4036
  selection,
3826
4037
  extensions: [
3827
4038
  // TODO(burdon): Doesn't catch errors in keymap functions.
3828
- EditorView14.exceptionSink.of((err) => {
4039
+ EditorView16.exceptionSink.of((err) => {
3829
4040
  log3.catch(err, void 0, {
3830
4041
  F: __dxlog_file4,
3831
4042
  L: 113,
@@ -3835,17 +4046,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3835
4046
  }),
3836
4047
  // Theme.
3837
4048
  // TODO(burdon): Make configurable.
3838
- EditorView14.baseTheme(defaultTheme),
3839
- EditorView14.theme(theme ?? {}),
3840
- EditorView14.darkTheme.of(themeMode === "dark"),
3841
- EditorView14.editorAttributes.of({
4049
+ EditorView16.baseTheme(defaultTheme),
4050
+ EditorView16.theme(theme ?? {}),
4051
+ EditorView16.darkTheme.of(themeMode === "dark"),
4052
+ EditorView16.editorAttributes.of({
3842
4053
  class: slots.editor?.className ?? ""
3843
4054
  }),
3844
- EditorView14.contentAttributes.of({
4055
+ EditorView16.contentAttributes.of({
3845
4056
  class: slots.content?.className ?? ""
3846
4057
  }),
3847
4058
  // Focus.
3848
- EditorView14.updateListener.of((update2) => {
4059
+ EditorView16.updateListener.of((update2) => {
3849
4060
  update2.transactions.forEach((transaction) => {
3850
4061
  if (transaction.isUserEvent("focus.container")) {
3851
4062
  rootRef.current?.focus();
@@ -3853,7 +4064,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3853
4064
  });
3854
4065
  }),
3855
4066
  // State.
3856
- EditorView14.editable.of(!readonly),
4067
+ EditorView16.editable.of(!readonly),
3857
4068
  EditorState2.readOnly.of(!!readonly),
3858
4069
  // Storage and replication.
3859
4070
  // NOTE: This must come before user extensions.
@@ -3862,7 +4073,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
3862
4073
  ...extensions
3863
4074
  ].filter(isNotFalsy4)
3864
4075
  });
3865
- const newView = new EditorView14({
4076
+ const newView = new EditorView16({
3866
4077
  state,
3867
4078
  parent: rootRef.current,
3868
4079
  scrollTo,
@@ -4417,7 +4628,7 @@ var useEditorView = (id) => {
4417
4628
 
4418
4629
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
4419
4630
  import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
4420
- import { EditorView as EditorView15 } from "@codemirror/view";
4631
+ import { EditorView as EditorView17 } from "@codemirror/view";
4421
4632
  import { useEffect as useEffect3, useRef as useRef3, useState as useState4 } from "react";
4422
4633
  import { log as log5 } from "@dxos/log";
4423
4634
  import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
@@ -4434,7 +4645,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
4434
4645
  selection,
4435
4646
  extensions: [
4436
4647
  // TODO(burdon): Doesn't catch errors in keymap functions.
4437
- EditorView15.exceptionSink.of((err) => {
4648
+ EditorView17.exceptionSink.of((err) => {
4438
4649
  log5.catch(err, void 0, {
4439
4650
  F: __dxlog_file6,
4440
4651
  L: 60,
@@ -4443,12 +4654,12 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
4443
4654
  });
4444
4655
  }),
4445
4656
  extensions,
4446
- EditorView15.updateListener.of(() => {
4657
+ EditorView17.updateListener.of(() => {
4447
4658
  onUpdate.current?.();
4448
4659
  })
4449
4660
  ].filter(isNotFalsy5)
4450
4661
  });
4451
- view2 = new EditorView15({
4662
+ view2 = new EditorView17({
4452
4663
  parent: parentRef.current,
4453
4664
  scrollTo,
4454
4665
  state,
@@ -4506,19 +4717,19 @@ var createDataExtensions = ({ readonly = false } = {}) => {
4506
4717
  return [
4507
4718
  //
4508
4719
  EditorState3.readOnly.of(readonly),
4509
- EditorView15.editable.of(!readonly)
4720
+ EditorView17.editable.of(!readonly)
4510
4721
  ];
4511
4722
  };
4512
4723
  var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
4513
4724
  return [
4514
4725
  //
4515
- EditorView15.baseTheme(defaultTheme),
4516
- theme && EditorView15.theme(theme),
4517
- EditorView15.darkTheme.of(themeMode === "dark"),
4518
- EditorView15.editorAttributes.of({
4726
+ EditorView17.baseTheme(defaultTheme),
4727
+ theme && EditorView17.theme(theme),
4728
+ EditorView17.darkTheme.of(themeMode === "dark"),
4729
+ EditorView17.editorAttributes.of({
4519
4730
  class: slots?.editor?.className ?? ""
4520
4731
  }),
4521
- EditorView15.contentAttributes.of({
4732
+ EditorView17.contentAttributes.of({
4522
4733
  class: slots?.content?.className ?? ""
4523
4734
  })
4524
4735
  ].filter(isNotFalsy5);
@@ -4618,6 +4829,7 @@ export {
4618
4829
  awarenessProvider,
4619
4830
  blast,
4620
4831
  callbackWrapper,
4832
+ code2 as code,
4621
4833
  codeTheme,
4622
4834
  comments,
4623
4835
  compareFormatting,
@@ -4628,7 +4840,6 @@ export {
4628
4840
  createThemeExtensions,
4629
4841
  cursorLineMargin,
4630
4842
  cursorLineMarginFacet,
4631
- decorateMarkdown,
4632
4843
  defaultMarkdownSlots,
4633
4844
  defaultOptions,
4634
4845
  defaultSlots,
@@ -4643,10 +4854,12 @@ export {
4643
4854
  formatting,
4644
4855
  getFormatting,
4645
4856
  getToken,
4857
+ heading2 as heading,
4858
+ hr,
4646
4859
  image,
4647
4860
  insertTable,
4648
4861
  keymap8 as keymap,
4649
- linkTooltip,
4862
+ link,
4650
4863
  listener,
4651
4864
  logChanges,
4652
4865
  markdownHighlightStyle,
@@ -4668,6 +4881,7 @@ export {
4668
4881
  setStyle,
4669
4882
  table,
4670
4883
  tags2 as tags,
4884
+ tasklist,
4671
4885
  textTheme,
4672
4886
  toggleBlockquote,
4673
4887
  toggleCodeblock,