@dxos/react-ui-editor 0.3.11-main.5c52a13 → 0.3.11-main.5e05862
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.
- package/dist/lib/browser/index.mjs +1164 -151
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +8 -4
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +5 -3
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +5 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +47 -9
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.test.d.ts +3 -0
- package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -0
- package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
- package/package.json +24 -24
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +2 -2
- package/src/components/TextEditor/hooks.stories.tsx +6 -1
- package/src/components/Toolbar/Toolbar.stories.tsx +15 -3
- package/src/components/Toolbar/Toolbar.tsx +100 -23
- package/src/extensions/comments.ts +14 -4
- package/src/extensions/markdown/formatting.test.ts +481 -0
- package/src/extensions/markdown/formatting.ts +1036 -67
- package/src/hooks/useActionHandler.ts +48 -15
- package/src/themes/default.ts +2 -2
|
@@ -8,10 +8,10 @@ import { tags as tags2 } from "@lezer/highlight";
|
|
|
8
8
|
|
|
9
9
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
10
10
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
11
|
-
import { EditorView as
|
|
11
|
+
import { EditorView as EditorView14 } from "@codemirror/view";
|
|
12
12
|
import { vim } from "@replit/codemirror-vim";
|
|
13
13
|
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
14
|
-
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
|
|
14
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState as useState2 } from "react";
|
|
15
15
|
import { log as log2 } from "@dxos/log";
|
|
16
16
|
import { useThemeContext } from "@dxos/react-ui";
|
|
17
17
|
import { focusRing, mx as mx3 } from "@dxos/react-ui-theme";
|
|
@@ -1074,11 +1074,25 @@ var logChanges = (trs) => {
|
|
|
1074
1074
|
|
|
1075
1075
|
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1076
1076
|
var styles2 = EditorView5.baseTheme({
|
|
1077
|
-
"& .cm-comment": {
|
|
1077
|
+
"&light .cm-comment, &light .cm-comment-current": {
|
|
1078
|
+
mixBlendMode: "darken"
|
|
1079
|
+
},
|
|
1080
|
+
"&dark .cm-comment, &dark .cm-comment-current": {
|
|
1081
|
+
mixBlendMode: "plus-lighter"
|
|
1082
|
+
},
|
|
1083
|
+
"&light .cm-comment": {
|
|
1078
1084
|
backgroundColor: getToken("extend.colors.yellow.50")
|
|
1079
1085
|
},
|
|
1080
|
-
"& .cm-comment-current": {
|
|
1086
|
+
"&light .cm-comment-current": {
|
|
1081
1087
|
backgroundColor: getToken("extend.colors.yellow.100")
|
|
1088
|
+
},
|
|
1089
|
+
"&dark .cm-comment": {
|
|
1090
|
+
color: getToken("extend.colors.yellow.50"),
|
|
1091
|
+
backgroundColor: getToken("extend.colors.yellow.900")
|
|
1092
|
+
},
|
|
1093
|
+
"&dark .cm-comment-current": {
|
|
1094
|
+
color: getToken("extend.colors.yellow.100"),
|
|
1095
|
+
backgroundColor: getToken("extend.colors.yellow.950")
|
|
1082
1096
|
}
|
|
1083
1097
|
});
|
|
1084
1098
|
var commentMark = Decoration2.mark({
|
|
@@ -1287,7 +1301,11 @@ var createComment = (view) => {
|
|
|
1287
1301
|
to
|
|
1288
1302
|
});
|
|
1289
1303
|
if (cursor) {
|
|
1290
|
-
const id = options.onCreate?.(
|
|
1304
|
+
const id = options.onCreate?.({
|
|
1305
|
+
cursor,
|
|
1306
|
+
from,
|
|
1307
|
+
location: view.coordsAtPos(from)
|
|
1308
|
+
});
|
|
1291
1309
|
if (id) {
|
|
1292
1310
|
view.dispatch({
|
|
1293
1311
|
effects: setSelection.of({
|
|
@@ -1815,57 +1833,279 @@ var code2 = () => {
|
|
|
1815
1833
|
// packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
|
|
1816
1834
|
import { snippet } from "@codemirror/autocomplete";
|
|
1817
1835
|
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1818
|
-
import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
|
|
1819
|
-
import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
|
|
1820
|
-
|
|
1821
|
-
|
|
1836
|
+
import { RangeSetBuilder as RangeSetBuilder2, EditorSelection } from "@codemirror/state";
|
|
1837
|
+
import { Decoration as Decoration4, EditorView as EditorView9, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
|
|
1838
|
+
import { useState, useMemo } from "react";
|
|
1839
|
+
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;
|
|
1840
|
+
var Inline;
|
|
1841
|
+
(function(Inline2) {
|
|
1842
|
+
Inline2[Inline2["Strong"] = 0] = "Strong";
|
|
1843
|
+
Inline2[Inline2["Emphasis"] = 1] = "Emphasis";
|
|
1844
|
+
Inline2[Inline2["Strikethrough"] = 2] = "Strikethrough";
|
|
1845
|
+
Inline2[Inline2["Code"] = 3] = "Code";
|
|
1846
|
+
})(Inline || (Inline = {}));
|
|
1847
|
+
var List;
|
|
1848
|
+
(function(List2) {
|
|
1849
|
+
List2[List2["Ordered"] = 0] = "Ordered";
|
|
1850
|
+
List2[List2["Bullet"] = 1] = "Bullet";
|
|
1851
|
+
List2[List2["Task"] = 2] = "Task";
|
|
1852
|
+
})(List || (List = {}));
|
|
1853
|
+
var setHeading = (level) => ({ state, dispatch }) => {
|
|
1854
|
+
const { selection: { ranges }, doc } = state;
|
|
1822
1855
|
const changes = [];
|
|
1856
|
+
let prevBlock = -1;
|
|
1823
1857
|
for (const range of ranges) {
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
from
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1858
|
+
syntaxTree2(state).iterate({
|
|
1859
|
+
from: range.from,
|
|
1860
|
+
to: range.to,
|
|
1861
|
+
enter: (node) => {
|
|
1862
|
+
if (!Object.hasOwn(Textblocks, node.name) || prevBlock === node.from) {
|
|
1863
|
+
return;
|
|
1864
|
+
}
|
|
1865
|
+
prevBlock = node.from;
|
|
1866
|
+
const blockType = Textblocks[node.name];
|
|
1867
|
+
const isHeading = /heading(\d)/.exec(blockType);
|
|
1868
|
+
const curLevel = isHeading ? +isHeading[1] : node.name === "Paragraph" ? 0 : -1;
|
|
1869
|
+
if (curLevel < 0 || curLevel === level) {
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
if (curLevel === 0) {
|
|
1873
|
+
changes.push({
|
|
1874
|
+
from: node.from,
|
|
1875
|
+
insert: "#".repeat(level) + " "
|
|
1876
|
+
});
|
|
1877
|
+
} else if (node.name === "SetextHeading1" || node.name === "SetextHeading2") {
|
|
1878
|
+
const nextLine = doc.lineAt(node.to);
|
|
1879
|
+
if (level) {
|
|
1880
|
+
changes.push({
|
|
1881
|
+
from: node.from,
|
|
1882
|
+
insert: "#".repeat(level) + " "
|
|
1883
|
+
});
|
|
1884
|
+
}
|
|
1885
|
+
changes.push({
|
|
1886
|
+
from: nextLine.from - 1,
|
|
1887
|
+
to: nextLine.to
|
|
1888
|
+
});
|
|
1889
|
+
} else {
|
|
1890
|
+
if (level === 0) {
|
|
1891
|
+
changes.push({
|
|
1892
|
+
from: node.from,
|
|
1893
|
+
to: Math.min(node.to, node.from + curLevel + 1)
|
|
1894
|
+
});
|
|
1895
|
+
} else if (level < curLevel) {
|
|
1896
|
+
changes.push({
|
|
1897
|
+
from: node.from,
|
|
1898
|
+
to: node.from + (curLevel - level)
|
|
1899
|
+
});
|
|
1900
|
+
} else {
|
|
1901
|
+
changes.push({
|
|
1902
|
+
from: node.from,
|
|
1903
|
+
insert: "#".repeat(level - curLevel)
|
|
1904
|
+
});
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1840
1908
|
});
|
|
1841
1909
|
}
|
|
1910
|
+
if (!changes.length) {
|
|
1911
|
+
return false;
|
|
1912
|
+
}
|
|
1913
|
+
dispatch(state.update({
|
|
1914
|
+
changes,
|
|
1915
|
+
userEvent: "format.setHeading",
|
|
1916
|
+
scrollIntoView: true
|
|
1917
|
+
}));
|
|
1842
1918
|
return true;
|
|
1843
1919
|
};
|
|
1844
|
-
var
|
|
1845
|
-
const
|
|
1846
|
-
|
|
1847
|
-
if (
|
|
1848
|
-
|
|
1920
|
+
var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
1921
|
+
const marker = inlineMarkerText(type);
|
|
1922
|
+
const changes = state.changeByRange((range) => {
|
|
1923
|
+
if (!enable && range.empty) {
|
|
1924
|
+
const after = state.doc.sliceString(range.head, range.head + 6);
|
|
1925
|
+
const found = after.indexOf(marker);
|
|
1926
|
+
if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
|
|
1927
|
+
const before = state.doc.sliceString(range.head - 6, range.head);
|
|
1928
|
+
if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
|
|
1929
|
+
...before.slice(before.length - found)
|
|
1930
|
+
].reverse().join("") === after.slice(0, found)) {
|
|
1931
|
+
return {
|
|
1932
|
+
changes: [
|
|
1933
|
+
{
|
|
1934
|
+
from: range.head - marker.length - found,
|
|
1935
|
+
to: range.head - found
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
from: range.head + found,
|
|
1939
|
+
to: range.head + found + marker.length
|
|
1940
|
+
}
|
|
1941
|
+
],
|
|
1942
|
+
range: EditorSelection.cursor(range.from - marker.length)
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1849
1946
|
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1947
|
+
const changes2 = [];
|
|
1948
|
+
const changesAtEnd = [];
|
|
1949
|
+
let blockStart = -1;
|
|
1950
|
+
let blockEnd = -1;
|
|
1951
|
+
let startCovered = false;
|
|
1952
|
+
let endCovered = false;
|
|
1953
|
+
let { from, to } = range;
|
|
1954
|
+
syntaxTree2(state).iterate({
|
|
1955
|
+
from,
|
|
1956
|
+
to,
|
|
1957
|
+
enter: (node) => {
|
|
1958
|
+
const { name } = node;
|
|
1959
|
+
if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
|
|
1960
|
+
blockStart = blockContentStart(node);
|
|
1961
|
+
blockEnd = blockContentEnd(node, state.doc);
|
|
1962
|
+
startCovered = endCovered = false;
|
|
1963
|
+
} else if (name === "Link" || name === "Image" && enable) {
|
|
1964
|
+
if (from < node.from && to > node.from && to <= node.to) {
|
|
1965
|
+
to = node.to;
|
|
1966
|
+
} else if (to > node.to && from >= node.from && from < node.to) {
|
|
1967
|
+
from = node.from;
|
|
1968
|
+
}
|
|
1969
|
+
} else if (IgnoreInline.has(name) && enable) {
|
|
1970
|
+
if (node.from < from && node.to > from) {
|
|
1971
|
+
if (to === from) {
|
|
1972
|
+
to = node.to;
|
|
1973
|
+
}
|
|
1974
|
+
from = node.to;
|
|
1975
|
+
}
|
|
1976
|
+
if (node.from < to && node.to > to) {
|
|
1977
|
+
to = node.from;
|
|
1978
|
+
}
|
|
1979
|
+
} else if (Object.hasOwn(InlineMarker, name)) {
|
|
1980
|
+
const markType = InlineMarker[name];
|
|
1981
|
+
const size = inlineMarkerText(markType).length;
|
|
1982
|
+
const openEnd = node.from + size;
|
|
1983
|
+
const closeStart = node.to - size;
|
|
1984
|
+
if (markType === type) {
|
|
1985
|
+
if (openEnd <= from && closeStart >= from) {
|
|
1986
|
+
startCovered = openEnd === from ? "adjacent" : true;
|
|
1987
|
+
}
|
|
1988
|
+
if (openEnd <= to && closeStart >= to) {
|
|
1989
|
+
endCovered = closeStart === to ? "adjacent" : true;
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
if (markType === type || type === 3 && enable) {
|
|
1993
|
+
if (node.from >= from && openEnd <= to) {
|
|
1994
|
+
changes2.push({
|
|
1995
|
+
from: node.from,
|
|
1996
|
+
to: openEnd
|
|
1997
|
+
});
|
|
1998
|
+
if (markType !== type && closeStart >= to) {
|
|
1999
|
+
changesAtEnd.push({
|
|
2000
|
+
from: skipSpaces(Math.min(to, blockEnd), state.doc, 1, blockEnd),
|
|
2001
|
+
insert: inlineMarkerText(markType)
|
|
2002
|
+
});
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
if (closeStart >= from && node.to <= to) {
|
|
2006
|
+
changes2.push({
|
|
2007
|
+
from: closeStart,
|
|
2008
|
+
to: node.to
|
|
2009
|
+
});
|
|
2010
|
+
if (markType !== type && openEnd <= from) {
|
|
2011
|
+
changes2.push({
|
|
2012
|
+
from: skipSpaces(Math.max(from, blockStart), state.doc, -1, blockStart),
|
|
2013
|
+
insert: inlineMarkerText(markType)
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
1859
2018
|
}
|
|
1860
|
-
|
|
2019
|
+
},
|
|
2020
|
+
leave: (node) => {
|
|
2021
|
+
if (Object.hasOwn(Textblocks, node.name) && Textblocks[node.name] !== "codeblock") {
|
|
2022
|
+
const rangeStart = Math.max(from, blockStart);
|
|
2023
|
+
const rangeEnd = Math.min(to, blockEnd);
|
|
2024
|
+
if (enable) {
|
|
2025
|
+
if (!startCovered) {
|
|
2026
|
+
changes2.push({
|
|
2027
|
+
from: rangeStart,
|
|
2028
|
+
insert: marker
|
|
2029
|
+
});
|
|
2030
|
+
}
|
|
2031
|
+
if (!endCovered) {
|
|
2032
|
+
changes2.push({
|
|
2033
|
+
from: rangeEnd,
|
|
2034
|
+
insert: marker
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
} else {
|
|
2038
|
+
if (startCovered === "adjacent") {
|
|
2039
|
+
changes2.push({
|
|
2040
|
+
from: from - marker.length,
|
|
2041
|
+
to: from
|
|
2042
|
+
});
|
|
2043
|
+
} else if (startCovered) {
|
|
2044
|
+
changes2.push({
|
|
2045
|
+
from: skipSpaces(rangeStart, state.doc, -1, blockStart),
|
|
2046
|
+
insert: marker
|
|
2047
|
+
});
|
|
2048
|
+
}
|
|
2049
|
+
if (endCovered === "adjacent") {
|
|
2050
|
+
changes2.push({
|
|
2051
|
+
from: to,
|
|
2052
|
+
to: to + marker.length
|
|
2053
|
+
});
|
|
2054
|
+
} else if (endCovered) {
|
|
2055
|
+
changes2.push({
|
|
2056
|
+
from: skipSpaces(rangeEnd, state.doc, 1, blockEnd),
|
|
2057
|
+
insert: marker
|
|
2058
|
+
});
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
1861
2063
|
});
|
|
1862
|
-
|
|
2064
|
+
const changeSet = state.changes(changes2.concat(changesAtEnd));
|
|
2065
|
+
return {
|
|
2066
|
+
changes: changeSet,
|
|
2067
|
+
range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
|
|
2068
|
+
};
|
|
2069
|
+
});
|
|
2070
|
+
dispatch(state.update(changes, {
|
|
2071
|
+
userEvent: enable ? "format.style.add" : "format.style.remove",
|
|
2072
|
+
scrollIntoView: true
|
|
2073
|
+
}));
|
|
1863
2074
|
return true;
|
|
1864
2075
|
};
|
|
2076
|
+
var blockContentStart = (node) => {
|
|
2077
|
+
const atx = /^ATXHeading(\d)/.exec(node.name);
|
|
2078
|
+
if (atx) {
|
|
2079
|
+
return Math.min(node.to, node.from + +atx[1] + 1);
|
|
2080
|
+
}
|
|
2081
|
+
return node.from;
|
|
2082
|
+
};
|
|
2083
|
+
var blockContentEnd = (node, doc) => {
|
|
2084
|
+
const setext = /^SetextHeading(\d)/.exec(node.name);
|
|
2085
|
+
const lastLine = doc.lineAt(node.to);
|
|
2086
|
+
if (setext || /^[\s>]*$/.exec(lastLine.text)) {
|
|
2087
|
+
return lastLine.from - 1;
|
|
2088
|
+
}
|
|
2089
|
+
return node.to;
|
|
2090
|
+
};
|
|
2091
|
+
var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
|
|
2092
|
+
var skipSpaces = (pos, doc, dir, limit) => {
|
|
2093
|
+
const line = doc.lineAt(pos);
|
|
2094
|
+
while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
|
|
2095
|
+
pos += dir;
|
|
2096
|
+
}
|
|
2097
|
+
return pos;
|
|
2098
|
+
};
|
|
2099
|
+
var addStyle = (style) => setStyle(style, true);
|
|
2100
|
+
var removeStyle = (style) => setStyle(style, false);
|
|
2101
|
+
var toggleStyle = (style) => (arg) => {
|
|
2102
|
+
const form = getFormatting(arg.state);
|
|
2103
|
+
return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
|
|
2104
|
+
};
|
|
1865
2105
|
var snippets = {
|
|
1866
2106
|
codeblock: snippet([
|
|
1867
2107
|
"```#{lang}",
|
|
1868
|
-
"
|
|
2108
|
+
"#{}",
|
|
1869
2109
|
"```"
|
|
1870
2110
|
].join("\n")),
|
|
1871
2111
|
table: snippet([
|
|
@@ -1875,29 +2115,537 @@ var snippets = {
|
|
|
1875
2115
|
"| #{val3} | #{val4} |"
|
|
1876
2116
|
].join("\n"))
|
|
1877
2117
|
};
|
|
1878
|
-
var insertCodeblock = (view) => {
|
|
1879
|
-
const { selection: { main }, doc } = view.state;
|
|
1880
|
-
const { number } = doc.lineAt(main.anchor);
|
|
1881
|
-
const { from } = doc.line(number);
|
|
1882
|
-
snippets.codeblock(view, null, from, from);
|
|
1883
|
-
};
|
|
1884
2118
|
var insertTable = (view) => {
|
|
1885
2119
|
const { selection: { main }, doc } = view.state;
|
|
1886
2120
|
const { number } = doc.lineAt(main.anchor);
|
|
1887
2121
|
const { from } = doc.line(number);
|
|
1888
2122
|
snippets.table(view, null, from, from);
|
|
1889
2123
|
};
|
|
1890
|
-
var
|
|
1891
|
-
var
|
|
1892
|
-
var toggleStrikethrough = toggleStyle(
|
|
1893
|
-
var
|
|
2124
|
+
var toggleStrong = toggleStyle(0);
|
|
2125
|
+
var toggleEmphasis = toggleStyle(1);
|
|
2126
|
+
var toggleStrikethrough = toggleStyle(2);
|
|
2127
|
+
var toggleInlineCode = toggleStyle(3);
|
|
2128
|
+
var removeLinkInner = (from, to, changes, state) => {
|
|
2129
|
+
syntaxTree2(state).iterate({
|
|
2130
|
+
from,
|
|
2131
|
+
to,
|
|
2132
|
+
enter: (node) => {
|
|
2133
|
+
if (node.name === "Link" && node.from < to && node.to > from) {
|
|
2134
|
+
node.node.cursor().iterate((node2) => {
|
|
2135
|
+
const { name } = node2;
|
|
2136
|
+
if (name === "LinkMark" || name === "LinkLabel") {
|
|
2137
|
+
changes.push({
|
|
2138
|
+
from: node2.from,
|
|
2139
|
+
to: node2.to
|
|
2140
|
+
});
|
|
2141
|
+
} else if (name === "LinkTitle" || name === "URL") {
|
|
2142
|
+
changes.push({
|
|
2143
|
+
from: skipSpaces(node2.from, state.doc, -1),
|
|
2144
|
+
to: skipSpaces(node2.to, state.doc, 1)
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
});
|
|
2148
|
+
return false;
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
});
|
|
2152
|
+
};
|
|
2153
|
+
var removeLink = ({ state, dispatch }) => {
|
|
2154
|
+
const changes = [];
|
|
2155
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2156
|
+
removeLinkInner(from, to, changes, state);
|
|
2157
|
+
}
|
|
2158
|
+
if (!changes) {
|
|
2159
|
+
return false;
|
|
2160
|
+
}
|
|
2161
|
+
dispatch(state.update({
|
|
2162
|
+
changes,
|
|
2163
|
+
userEvent: "format.link.remove",
|
|
2164
|
+
scrollIntoView: true
|
|
2165
|
+
}));
|
|
2166
|
+
return true;
|
|
2167
|
+
};
|
|
2168
|
+
var addLink = ({ state, dispatch }) => {
|
|
2169
|
+
const changes = state.changeByRange((range) => {
|
|
2170
|
+
let { from, to } = range;
|
|
2171
|
+
const cutStyles = [];
|
|
2172
|
+
let okay = null;
|
|
2173
|
+
syntaxTree2(state).iterate({
|
|
2174
|
+
from,
|
|
2175
|
+
to,
|
|
2176
|
+
enter: (node) => {
|
|
2177
|
+
if (Object.hasOwn(Textblocks, node.name)) {
|
|
2178
|
+
okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state.doc);
|
|
2179
|
+
} else if (Object.hasOwn(InlineMarker, node.name)) {
|
|
2180
|
+
const sNode = node.node;
|
|
2181
|
+
if (node.from < from && node.to <= to) {
|
|
2182
|
+
if (sNode.firstChild.to === from) {
|
|
2183
|
+
from = node.from;
|
|
2184
|
+
} else {
|
|
2185
|
+
cutStyles.push(sNode);
|
|
2186
|
+
}
|
|
2187
|
+
} else if (node.from >= from && node.to > to) {
|
|
2188
|
+
if (sNode.lastChild.from === to) {
|
|
2189
|
+
to = node.to;
|
|
2190
|
+
} else {
|
|
2191
|
+
cutStyles.push(sNode);
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
if (okay === null) {
|
|
2198
|
+
const line = state.doc.lineAt(from);
|
|
2199
|
+
okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
|
|
2200
|
+
}
|
|
2201
|
+
if (!okay) {
|
|
2202
|
+
return {
|
|
2203
|
+
range
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
const changes2 = [];
|
|
2207
|
+
const changesAfter = [];
|
|
2208
|
+
removeLinkInner(from, to, changesAfter, state);
|
|
2209
|
+
let cursorOffset = 1;
|
|
2210
|
+
for (const style of cutStyles) {
|
|
2211
|
+
const type = InlineMarker[style.name];
|
|
2212
|
+
const mark2 = inlineMarkerText(type);
|
|
2213
|
+
if (style.from < from) {
|
|
2214
|
+
changes2.push({
|
|
2215
|
+
from: skipSpaces(from, state.doc, -1),
|
|
2216
|
+
insert: mark2
|
|
2217
|
+
});
|
|
2218
|
+
changesAfter.push({
|
|
2219
|
+
from: skipSpaces(from, state.doc, 1, to),
|
|
2220
|
+
insert: mark2
|
|
2221
|
+
});
|
|
2222
|
+
} else {
|
|
2223
|
+
changes2.push({
|
|
2224
|
+
from: skipSpaces(to, state.doc, -1, from),
|
|
2225
|
+
insert: mark2
|
|
2226
|
+
});
|
|
2227
|
+
const after = skipSpaces(to, state.doc, 1);
|
|
2228
|
+
if (after === to) {
|
|
2229
|
+
cursorOffset += mark2.length;
|
|
2230
|
+
}
|
|
2231
|
+
changesAfter.push({
|
|
2232
|
+
from: after,
|
|
2233
|
+
insert: mark2
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
changes2.push({
|
|
2238
|
+
from,
|
|
2239
|
+
insert: "["
|
|
2240
|
+
}, {
|
|
2241
|
+
from: to,
|
|
2242
|
+
insert: "]()"
|
|
2243
|
+
});
|
|
2244
|
+
const changeSet = state.changes(changes2.concat(changesAfter));
|
|
2245
|
+
return {
|
|
2246
|
+
changes: changeSet,
|
|
2247
|
+
range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
|
|
2248
|
+
};
|
|
2249
|
+
});
|
|
2250
|
+
if (changes.changes.empty) {
|
|
2251
|
+
return false;
|
|
2252
|
+
}
|
|
2253
|
+
dispatch(state.update(changes, {
|
|
2254
|
+
userEvent: "format.link.add",
|
|
2255
|
+
scrollIntoView: true
|
|
2256
|
+
}));
|
|
2257
|
+
return true;
|
|
2258
|
+
};
|
|
2259
|
+
var addList = (type) => ({ state, dispatch }) => {
|
|
2260
|
+
let lastBlock = -1;
|
|
2261
|
+
let counter = 1;
|
|
2262
|
+
let first = true;
|
|
2263
|
+
let parentColumn = null;
|
|
2264
|
+
const blocks = [];
|
|
2265
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2266
|
+
syntaxTree2(state).iterate({
|
|
2267
|
+
from,
|
|
2268
|
+
to,
|
|
2269
|
+
enter: (node) => {
|
|
2270
|
+
if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
|
|
2271
|
+
if (first) {
|
|
2272
|
+
let before = node.node.prevSibling;
|
|
2273
|
+
while (before && /Mark$/.test(before.name)) {
|
|
2274
|
+
before = before.prevSibling;
|
|
2275
|
+
}
|
|
2276
|
+
if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
|
|
2277
|
+
const item = before.lastChild;
|
|
2278
|
+
const itemLine = state.doc.lineAt(item.from);
|
|
2279
|
+
const itemText = itemLine.text.slice(item.from - itemLine.from);
|
|
2280
|
+
parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
|
|
2281
|
+
if (type === 0) {
|
|
2282
|
+
const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
|
|
2283
|
+
if (mark2) {
|
|
2284
|
+
parentColumn += mark2[1].length;
|
|
2285
|
+
counter = +mark2[1] + 1;
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
first = false;
|
|
2290
|
+
}
|
|
2291
|
+
if (node.from === lastBlock) {
|
|
2292
|
+
return;
|
|
2293
|
+
}
|
|
2294
|
+
lastBlock = node.from;
|
|
2295
|
+
blocks.push({
|
|
2296
|
+
node: node.node,
|
|
2297
|
+
counter,
|
|
2298
|
+
parentColumn
|
|
2299
|
+
});
|
|
2300
|
+
counter++;
|
|
2301
|
+
return false;
|
|
2302
|
+
}
|
|
2303
|
+
},
|
|
2304
|
+
leave: (node) => {
|
|
2305
|
+
if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
|
|
2306
|
+
counter = 1;
|
|
2307
|
+
parentColumn = null;
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
});
|
|
2311
|
+
}
|
|
2312
|
+
if (!blocks.length) {
|
|
2313
|
+
return false;
|
|
2314
|
+
}
|
|
2315
|
+
const changes = [];
|
|
2316
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
2317
|
+
const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
|
|
2318
|
+
const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
|
|
2319
|
+
let padding = nodeFrom > 0 && !/\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
|
|
2320
|
+
if (type === 0) {
|
|
2321
|
+
padding += String(counter2).length;
|
|
2322
|
+
}
|
|
2323
|
+
let line = state.doc.lineAt(nodeFrom);
|
|
2324
|
+
const column = nodeFrom - line.from;
|
|
2325
|
+
if (parentColumn2 !== null && parentColumn2 > column) {
|
|
2326
|
+
padding = Math.max(padding, parentColumn2 - column);
|
|
2327
|
+
}
|
|
2328
|
+
let mark2;
|
|
2329
|
+
if (type === 0) {
|
|
2330
|
+
let max = counter2;
|
|
2331
|
+
for (let j = i + 1; j < blocks.length; j++) {
|
|
2332
|
+
if (blocks[j].counter !== max + 1) {
|
|
2333
|
+
break;
|
|
2334
|
+
}
|
|
2335
|
+
max++;
|
|
2336
|
+
}
|
|
2337
|
+
const num = String(counter2);
|
|
2338
|
+
padding = Math.max(String(max).length, padding);
|
|
2339
|
+
mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
|
|
2340
|
+
} else {
|
|
2341
|
+
mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
|
|
2342
|
+
}
|
|
2343
|
+
changes.push({
|
|
2344
|
+
from: nodeFrom,
|
|
2345
|
+
insert: mark2
|
|
2346
|
+
});
|
|
2347
|
+
while (line.to < node.to) {
|
|
2348
|
+
line = state.doc.lineAt(line.to + 1);
|
|
2349
|
+
const open = /^[\s>]*/.exec(line.text)[0].length;
|
|
2350
|
+
changes.push({
|
|
2351
|
+
from: line.from + Math.min(open, column),
|
|
2352
|
+
insert: " ".repeat(mark2.length)
|
|
2353
|
+
});
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
if (type === 0) {
|
|
2357
|
+
const last = blocks[blocks.length - 1];
|
|
2358
|
+
let next = last.node.nextSibling;
|
|
2359
|
+
while (next && /Mark$/.test(next.name)) {
|
|
2360
|
+
next = next.nextSibling;
|
|
2361
|
+
}
|
|
2362
|
+
if (next?.name === "OrderedList") {
|
|
2363
|
+
renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
dispatch(state.update({
|
|
2367
|
+
changes,
|
|
2368
|
+
userEvent: "format.list.add",
|
|
2369
|
+
scrollIntoView: true
|
|
2370
|
+
}));
|
|
2371
|
+
return true;
|
|
2372
|
+
};
|
|
2373
|
+
var removeList = (type) => ({ state, dispatch }) => {
|
|
2374
|
+
let lastBlock = -1;
|
|
2375
|
+
const changes = [];
|
|
2376
|
+
const stack = [];
|
|
2377
|
+
const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
|
|
2378
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2379
|
+
syntaxTree2(state).iterate({
|
|
2380
|
+
from,
|
|
2381
|
+
to,
|
|
2382
|
+
enter: (node) => {
|
|
2383
|
+
const { name } = node;
|
|
2384
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2385
|
+
stack.push(name);
|
|
2386
|
+
} else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
|
|
2387
|
+
stack[stack.length - 1] = "TaskList";
|
|
2388
|
+
}
|
|
2389
|
+
},
|
|
2390
|
+
leave: (node) => {
|
|
2391
|
+
const { name } = node;
|
|
2392
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2393
|
+
stack.pop();
|
|
2394
|
+
} else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
|
|
2395
|
+
lastBlock = node.from;
|
|
2396
|
+
let line = state.doc.lineAt(node.from);
|
|
2397
|
+
const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
|
|
2398
|
+
if (!mark2) {
|
|
2399
|
+
return false;
|
|
2400
|
+
}
|
|
2401
|
+
const column = node.from - line.from;
|
|
2402
|
+
changes.push({
|
|
2403
|
+
from: node.from,
|
|
2404
|
+
to: node.from + mark2[0].length
|
|
2405
|
+
});
|
|
2406
|
+
while (line.to < node.to) {
|
|
2407
|
+
line = state.doc.lineAt(line.to + 1);
|
|
2408
|
+
const open = /^[\s>]*/.exec(line.text)[0].length;
|
|
2409
|
+
if (open > column) {
|
|
2410
|
+
changes.push({
|
|
2411
|
+
from: line.from + column,
|
|
2412
|
+
to: line.from + Math.min(column + mark2[0].length, open)
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
}
|
|
2416
|
+
if (node.to >= to) {
|
|
2417
|
+
renumberListItems(node.node.nextSibling, 1, changes, state.doc);
|
|
2418
|
+
}
|
|
2419
|
+
return false;
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
});
|
|
2423
|
+
}
|
|
2424
|
+
if (!changes.length) {
|
|
2425
|
+
return false;
|
|
2426
|
+
}
|
|
2427
|
+
dispatch(state.update({
|
|
2428
|
+
changes,
|
|
2429
|
+
userEvent: "format.list.remove",
|
|
2430
|
+
scrollIntoView: true
|
|
2431
|
+
}));
|
|
2432
|
+
return true;
|
|
2433
|
+
};
|
|
2434
|
+
var toggleList = (type) => (target) => {
|
|
2435
|
+
const formatting2 = getFormatting(target.state);
|
|
2436
|
+
const active = formatting2.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
|
|
2437
|
+
return (active ? removeList(type) : addList(type))(target);
|
|
2438
|
+
};
|
|
2439
|
+
var renumberListItems = (item, counter, changes, doc) => {
|
|
2440
|
+
for (; item; item = item.nextSibling) {
|
|
2441
|
+
if (item.name === "ListItem") {
|
|
2442
|
+
const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
|
|
2443
|
+
if (!number || +number[2] === counter) {
|
|
2444
|
+
break;
|
|
2445
|
+
}
|
|
2446
|
+
const size = number[1].length + number[2].length;
|
|
2447
|
+
const newNum = String(counter);
|
|
2448
|
+
changes.push({
|
|
2449
|
+
from: item.from + Math.max(0, size - newNum.length),
|
|
2450
|
+
to: item.from + size,
|
|
2451
|
+
insert: newNum
|
|
2452
|
+
});
|
|
2453
|
+
counter++;
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
var setBlockquote = (enable) => ({ state, dispatch }) => {
|
|
2458
|
+
const lines = [];
|
|
2459
|
+
let lastBlock = -1;
|
|
2460
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2461
|
+
syntaxTree2(state).iterate({
|
|
2462
|
+
from,
|
|
2463
|
+
to,
|
|
2464
|
+
enter: (node) => {
|
|
2465
|
+
if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
|
|
2466
|
+
if (node.from === lastBlock) {
|
|
2467
|
+
return false;
|
|
2468
|
+
}
|
|
2469
|
+
lastBlock = node.from;
|
|
2470
|
+
let line = state.doc.lineAt(node.from);
|
|
2471
|
+
if (line.number > 1) {
|
|
2472
|
+
const prevLine = state.doc.line(line.number - 1);
|
|
2473
|
+
if (/^[>\s]*$/.test(prevLine.text)) {
|
|
2474
|
+
if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
|
|
2475
|
+
lines.push(prevLine);
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
for (; ; ) {
|
|
2480
|
+
lines.push(line);
|
|
2481
|
+
if (line.to >= node.to) {
|
|
2482
|
+
break;
|
|
2483
|
+
}
|
|
2484
|
+
line = state.doc.line(line.number + 1);
|
|
2485
|
+
}
|
|
2486
|
+
if (!enable && line.number < state.doc.lines) {
|
|
2487
|
+
const nextLine = state.doc.line(line.number + 1);
|
|
2488
|
+
if (/^[>\s]*$/.test(nextLine.text)) {
|
|
2489
|
+
lines.push(nextLine);
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
return false;
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
});
|
|
2496
|
+
}
|
|
2497
|
+
const changes = [];
|
|
2498
|
+
for (const line of lines) {
|
|
2499
|
+
if (enable) {
|
|
2500
|
+
changes.push({
|
|
2501
|
+
from: line.from,
|
|
2502
|
+
insert: /\S/.test(line.text) ? "> " : ">"
|
|
2503
|
+
});
|
|
2504
|
+
} else {
|
|
2505
|
+
const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
|
|
2506
|
+
if (quote) {
|
|
2507
|
+
changes.push({
|
|
2508
|
+
from: line.from + quote[1].length,
|
|
2509
|
+
to: line.from + quote[0].length
|
|
2510
|
+
});
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
if (!changes.length) {
|
|
2515
|
+
return false;
|
|
2516
|
+
}
|
|
2517
|
+
dispatch(state.update({
|
|
2518
|
+
changes,
|
|
2519
|
+
userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
|
|
2520
|
+
scrollIntoView: true
|
|
2521
|
+
}));
|
|
2522
|
+
return true;
|
|
2523
|
+
};
|
|
2524
|
+
var addBlockquote = setBlockquote(true);
|
|
2525
|
+
var removeBlockquote = setBlockquote(false);
|
|
2526
|
+
var toggleBlockquote = (target) => {
|
|
2527
|
+
return (getFormatting(target.state).blockquote ? removeBlockquote : addBlockquote)(target);
|
|
2528
|
+
};
|
|
2529
|
+
var addCodeblock = (target) => {
|
|
2530
|
+
const { state, dispatch } = target;
|
|
2531
|
+
const { selection } = state;
|
|
2532
|
+
if (selection.ranges.length === 1 && selection.main.empty) {
|
|
2533
|
+
const { head } = selection.main;
|
|
2534
|
+
const line = state.doc.lineAt(head);
|
|
2535
|
+
if (!/\S/.test(line.text) && head === line.from) {
|
|
2536
|
+
snippets.codeblock(target, null, line.from, line.to);
|
|
2537
|
+
return true;
|
|
2538
|
+
}
|
|
2539
|
+
}
|
|
2540
|
+
const ranges = [];
|
|
2541
|
+
for (const { from, to } of selection.ranges) {
|
|
2542
|
+
let blockFrom = from;
|
|
2543
|
+
let blockTo = to;
|
|
2544
|
+
syntaxTree2(state).iterate({
|
|
2545
|
+
from,
|
|
2546
|
+
to,
|
|
2547
|
+
enter: (node) => {
|
|
2548
|
+
if (Object.hasOwn(Textblocks, node.name)) {
|
|
2549
|
+
if (from >= node.from && to <= node.to) {
|
|
2550
|
+
blockFrom = node.from;
|
|
2551
|
+
blockTo = node.to;
|
|
2552
|
+
} else {
|
|
2553
|
+
blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);
|
|
2554
|
+
blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
});
|
|
2559
|
+
if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
|
|
2560
|
+
ranges[ranges.length - 1].to = blockTo;
|
|
2561
|
+
} else {
|
|
2562
|
+
ranges.push({
|
|
2563
|
+
from: blockFrom,
|
|
2564
|
+
to: blockTo
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
if (!ranges.length) {
|
|
2569
|
+
return false;
|
|
2570
|
+
}
|
|
2571
|
+
const changes = ranges.map(({ from, to }) => {
|
|
2572
|
+
const column = from - state.doc.lineAt(from).from;
|
|
2573
|
+
return [
|
|
2574
|
+
{
|
|
2575
|
+
from,
|
|
2576
|
+
insert: "```\n" + " ".repeat(column)
|
|
2577
|
+
},
|
|
2578
|
+
{
|
|
2579
|
+
from: to,
|
|
2580
|
+
insert: "\n" + " ".repeat(column) + "```"
|
|
2581
|
+
}
|
|
2582
|
+
];
|
|
2583
|
+
});
|
|
2584
|
+
dispatch(state.update({
|
|
2585
|
+
changes,
|
|
2586
|
+
userEvent: "format.codeblock.add",
|
|
2587
|
+
scrollIntoView: true
|
|
2588
|
+
}));
|
|
2589
|
+
return true;
|
|
2590
|
+
};
|
|
2591
|
+
var removeCodeblock = ({ state, dispatch }) => {
|
|
2592
|
+
const changes = [];
|
|
2593
|
+
let lastBlock = -1;
|
|
2594
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2595
|
+
syntaxTree2(state).iterate({
|
|
2596
|
+
from,
|
|
2597
|
+
to,
|
|
2598
|
+
enter: (node) => {
|
|
2599
|
+
if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
|
|
2600
|
+
lastBlock = node.from;
|
|
2601
|
+
const firstLine = state.doc.lineAt(node.from);
|
|
2602
|
+
if (node.name === "FencedCode") {
|
|
2603
|
+
changes.push({
|
|
2604
|
+
from: node.from,
|
|
2605
|
+
to: firstLine.to + 1 + node.from - firstLine.from
|
|
2606
|
+
});
|
|
2607
|
+
const lastLine = state.doc.lineAt(node.to);
|
|
2608
|
+
if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
|
|
2609
|
+
changes.push({
|
|
2610
|
+
from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
|
|
2611
|
+
to: lastLine.to
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
} else {
|
|
2615
|
+
const column = node.from - firstLine.from;
|
|
2616
|
+
for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
|
|
2617
|
+
changes.push({
|
|
2618
|
+
from: line.from + column - 4,
|
|
2619
|
+
to: line.from + column
|
|
2620
|
+
});
|
|
2621
|
+
if (line.to >= node.to) {
|
|
2622
|
+
break;
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
}
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
if (!changes.length) {
|
|
2631
|
+
return false;
|
|
2632
|
+
}
|
|
2633
|
+
dispatch(state.update({
|
|
2634
|
+
changes,
|
|
2635
|
+
userEvent: "format.codeblock.remove",
|
|
2636
|
+
scrollIntoView: true
|
|
2637
|
+
}));
|
|
2638
|
+
return true;
|
|
2639
|
+
};
|
|
2640
|
+
var toggleCodeblock = (target) => {
|
|
2641
|
+
return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
|
|
1894
2642
|
};
|
|
1895
2643
|
var formatting = (options = {}) => {
|
|
1896
2644
|
return [
|
|
1897
2645
|
keymap5.of([
|
|
1898
2646
|
{
|
|
1899
2647
|
key: "meta-b",
|
|
1900
|
-
run:
|
|
2648
|
+
run: toggleStrong
|
|
1901
2649
|
}
|
|
1902
2650
|
]),
|
|
1903
2651
|
styling()
|
|
@@ -1951,6 +2699,206 @@ var styling = () => {
|
|
|
1951
2699
|
})
|
|
1952
2700
|
];
|
|
1953
2701
|
};
|
|
2702
|
+
var InlineMarker = {
|
|
2703
|
+
Emphasis: 1,
|
|
2704
|
+
StrongEmphasis: 0,
|
|
2705
|
+
InlineCode: 3,
|
|
2706
|
+
Strikethrough: 2
|
|
2707
|
+
};
|
|
2708
|
+
var IgnoreInline = /* @__PURE__ */ new Set([
|
|
2709
|
+
"Hardbreak",
|
|
2710
|
+
"HTMLTag",
|
|
2711
|
+
"Comment",
|
|
2712
|
+
"ProcessingInstruction",
|
|
2713
|
+
"Autolink",
|
|
2714
|
+
"HeaderMark",
|
|
2715
|
+
"QuoteMark",
|
|
2716
|
+
"ListMark",
|
|
2717
|
+
"LinkMark",
|
|
2718
|
+
"EmphasisMark",
|
|
2719
|
+
"CodeMark",
|
|
2720
|
+
"CodeText",
|
|
2721
|
+
"StrikethroughMark",
|
|
2722
|
+
"TaskMarker",
|
|
2723
|
+
"SuperscriptMark",
|
|
2724
|
+
"SubscriptMark"
|
|
2725
|
+
]);
|
|
2726
|
+
var Textblocks = {
|
|
2727
|
+
Paragraph: "paragraph",
|
|
2728
|
+
Task: "paragraph",
|
|
2729
|
+
CodeBlock: "codeblock",
|
|
2730
|
+
FencedCode: "codeblock",
|
|
2731
|
+
ATXHeading1: "heading1",
|
|
2732
|
+
ATXHeading2: "heading2",
|
|
2733
|
+
ATXHeading3: "heading3",
|
|
2734
|
+
ATXHeading4: "heading4",
|
|
2735
|
+
ATXHeading5: "heading5",
|
|
2736
|
+
ATXHeading6: "heading6",
|
|
2737
|
+
SetextHeading1: "heading1",
|
|
2738
|
+
SetextHeading2: "heading2",
|
|
2739
|
+
TableCell: "tablecell"
|
|
2740
|
+
};
|
|
2741
|
+
var getFormatting = (state) => {
|
|
2742
|
+
let blockType = null;
|
|
2743
|
+
const inline = [
|
|
2744
|
+
null,
|
|
2745
|
+
null,
|
|
2746
|
+
null,
|
|
2747
|
+
null
|
|
2748
|
+
];
|
|
2749
|
+
let link2 = false;
|
|
2750
|
+
let blockquote2 = null;
|
|
2751
|
+
let listStyle = null;
|
|
2752
|
+
const stack = [];
|
|
2753
|
+
let currentBlock = null;
|
|
2754
|
+
const advanceInline = (upto) => {
|
|
2755
|
+
if (!currentBlock) {
|
|
2756
|
+
return;
|
|
2757
|
+
}
|
|
2758
|
+
upto = Math.min(upto, currentBlock.end);
|
|
2759
|
+
if (upto <= currentBlock.pos) {
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
for (let i = 0; i < currentBlock.active.length; i++) {
|
|
2763
|
+
if (inline[i] === false) {
|
|
2764
|
+
continue;
|
|
2765
|
+
} else if (currentBlock.active[i]) {
|
|
2766
|
+
inline[i] = true;
|
|
2767
|
+
} else if (/\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {
|
|
2768
|
+
inline[i] = false;
|
|
2769
|
+
}
|
|
2770
|
+
}
|
|
2771
|
+
currentBlock.pos = upto;
|
|
2772
|
+
};
|
|
2773
|
+
const skipInline = (upto) => {
|
|
2774
|
+
if (currentBlock && upto > currentBlock.pos) {
|
|
2775
|
+
currentBlock.pos = Math.min(upto, currentBlock.end);
|
|
2776
|
+
}
|
|
2777
|
+
};
|
|
2778
|
+
const { selection } = state;
|
|
2779
|
+
for (const range of selection.ranges) {
|
|
2780
|
+
if (range.empty && inline.some((v) => v === null)) {
|
|
2781
|
+
const contextSize = Math.min(range.head, 6);
|
|
2782
|
+
const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);
|
|
2783
|
+
let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);
|
|
2784
|
+
for (let i = 0; i < contextSize; i++) {
|
|
2785
|
+
const ch = contextAfter[i];
|
|
2786
|
+
if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
|
|
2787
|
+
contextAfter = contextAfter.slice(0, i);
|
|
2788
|
+
break;
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
for (let i = 0; i < inline.length; i++) {
|
|
2792
|
+
const mark2 = inlineMarkerText(i);
|
|
2793
|
+
const found = contextAfter.indexOf(mark2);
|
|
2794
|
+
if (found > -1) {
|
|
2795
|
+
contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
|
|
2796
|
+
if (inline[i] === null) {
|
|
2797
|
+
inline[i] = true;
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2802
|
+
syntaxTree2(state).iterate({
|
|
2803
|
+
from: range.from,
|
|
2804
|
+
to: range.to,
|
|
2805
|
+
enter: (node) => {
|
|
2806
|
+
advanceInline(node.from);
|
|
2807
|
+
const { name } = node;
|
|
2808
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2809
|
+
stack.push(name);
|
|
2810
|
+
} else if (name === "Link") {
|
|
2811
|
+
link2 = true;
|
|
2812
|
+
} else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
|
|
2813
|
+
if (name === "Task" && stack[stack.length - 1] === "BulletList") {
|
|
2814
|
+
stack[stack.length - 1] = "TaskList";
|
|
2815
|
+
}
|
|
2816
|
+
const blockCode = Textblocks[name];
|
|
2817
|
+
if (blockType === null) {
|
|
2818
|
+
blockType = blockCode;
|
|
2819
|
+
} else if (blockType !== blockCode) {
|
|
2820
|
+
blockType = false;
|
|
2821
|
+
}
|
|
2822
|
+
if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
|
|
2823
|
+
currentBlock = {
|
|
2824
|
+
pos: Math.max(range.from, node.from),
|
|
2825
|
+
end: Math.min(range.to, node.to),
|
|
2826
|
+
active: [
|
|
2827
|
+
false,
|
|
2828
|
+
false,
|
|
2829
|
+
false,
|
|
2830
|
+
false
|
|
2831
|
+
]
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
} else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
|
|
2835
|
+
const index = InlineMarker[name];
|
|
2836
|
+
if (range.empty && inline[index] === null) {
|
|
2837
|
+
inline[index] = true;
|
|
2838
|
+
}
|
|
2839
|
+
currentBlock.active[index] = true;
|
|
2840
|
+
} else if (IgnoreInline.has(name)) {
|
|
2841
|
+
skipInline(node.to);
|
|
2842
|
+
}
|
|
2843
|
+
},
|
|
2844
|
+
leave: (node) => {
|
|
2845
|
+
advanceInline(node.to);
|
|
2846
|
+
const { name } = node;
|
|
2847
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2848
|
+
stack.pop();
|
|
2849
|
+
} else if (Object.hasOwn(Textblocks, name)) {
|
|
2850
|
+
let hasList = false;
|
|
2851
|
+
let hasQuote = false;
|
|
2852
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
2853
|
+
if (stack[i] === "Blockquote") {
|
|
2854
|
+
hasQuote = true;
|
|
2855
|
+
} else if (!hasList) {
|
|
2856
|
+
hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
|
|
2857
|
+
}
|
|
2858
|
+
}
|
|
2859
|
+
if (blockquote2 === null) {
|
|
2860
|
+
blockquote2 = hasQuote;
|
|
2861
|
+
} else if (!hasQuote && blockquote2) {
|
|
2862
|
+
blockquote2 = false;
|
|
2863
|
+
}
|
|
2864
|
+
if (listStyle === null) {
|
|
2865
|
+
listStyle = hasList;
|
|
2866
|
+
} else if (listStyle !== hasList) {
|
|
2867
|
+
listStyle = false;
|
|
2868
|
+
}
|
|
2869
|
+
currentBlock = null;
|
|
2870
|
+
} else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
|
|
2871
|
+
currentBlock.active[InlineMarker[name]] = false;
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
});
|
|
2875
|
+
}
|
|
2876
|
+
return {
|
|
2877
|
+
blockType: blockType || null,
|
|
2878
|
+
strong: inline[0] ?? false,
|
|
2879
|
+
emphasis: inline[1] ?? false,
|
|
2880
|
+
code: inline[3] ?? false,
|
|
2881
|
+
strikethrough: inline[2] ?? false,
|
|
2882
|
+
link: link2,
|
|
2883
|
+
blockquote: blockquote2 ?? false,
|
|
2884
|
+
listStyle: listStyle || null
|
|
2885
|
+
};
|
|
2886
|
+
};
|
|
2887
|
+
var useFormattingState = () => {
|
|
2888
|
+
const [state, setState] = useState(null);
|
|
2889
|
+
const observer = useMemo(() => EditorView9.updateListener.of((update2) => {
|
|
2890
|
+
if (update2.docChanged || update2.selectionSet) {
|
|
2891
|
+
const newState = getFormatting(update2.state);
|
|
2892
|
+
if (!state || !compareFormatting(state, newState)) {
|
|
2893
|
+
setState(newState);
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
}), []);
|
|
2897
|
+
return [
|
|
2898
|
+
state,
|
|
2899
|
+
observer
|
|
2900
|
+
];
|
|
2901
|
+
};
|
|
1954
2902
|
|
|
1955
2903
|
// packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
|
|
1956
2904
|
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
@@ -2003,8 +2951,8 @@ var heading2 = () => {
|
|
|
2003
2951
|
// packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
|
|
2004
2952
|
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
2005
2953
|
import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
|
|
2006
|
-
import { Decoration as Decoration6, EditorView as
|
|
2007
|
-
var styles4 =
|
|
2954
|
+
import { Decoration as Decoration6, EditorView as EditorView10, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
2955
|
+
var styles4 = EditorView10.baseTheme({
|
|
2008
2956
|
"& .cm-hr": {
|
|
2009
2957
|
// Note that block-level decorations should not have vertical margins,
|
|
2010
2958
|
borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
@@ -2058,7 +3006,7 @@ var hr = () => {
|
|
|
2058
3006
|
// packages/ui/react-ui-editor/src/extensions/markdown/image.ts
|
|
2059
3007
|
import { syntaxTree as syntaxTree5 } from "@codemirror/language";
|
|
2060
3008
|
import { StateField as StateField3 } from "@codemirror/state";
|
|
2061
|
-
import { Decoration as Decoration7, EditorView as
|
|
3009
|
+
import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
2062
3010
|
var image = (options = {}) => {
|
|
2063
3011
|
return StateField3.define({
|
|
2064
3012
|
create: (state) => {
|
|
@@ -2085,7 +3033,7 @@ var image = (options = {}) => {
|
|
|
2085
3033
|
add: buildDecorations4(from, to, tr.state)
|
|
2086
3034
|
});
|
|
2087
3035
|
},
|
|
2088
|
-
provide: (field) =>
|
|
3036
|
+
provide: (field) => EditorView11.decorations.from(field)
|
|
2089
3037
|
});
|
|
2090
3038
|
};
|
|
2091
3039
|
var buildDecorations4 = (from, to, state) => {
|
|
@@ -2267,12 +3215,12 @@ var buildDecorations5 = (view, options) => {
|
|
|
2267
3215
|
// packages/ui/react-ui-editor/src/extensions/markdown/table.ts
|
|
2268
3216
|
import { syntaxTree as syntaxTree7 } from "@codemirror/language";
|
|
2269
3217
|
import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
|
|
2270
|
-
import { Decoration as Decoration9, EditorView as
|
|
3218
|
+
import { Decoration as Decoration9, EditorView as EditorView12, WidgetType as WidgetType6 } from "@codemirror/view";
|
|
2271
3219
|
var table = (options = {}) => {
|
|
2272
3220
|
return StateField4.define({
|
|
2273
3221
|
create: (state) => update(state, options),
|
|
2274
3222
|
update: (_, tr) => update(tr.state, options),
|
|
2275
|
-
provide: (field) =>
|
|
3223
|
+
provide: (field) => EditorView12.decorations.from(field)
|
|
2276
3224
|
});
|
|
2277
3225
|
};
|
|
2278
3226
|
var update = (state, options) => {
|
|
@@ -2365,8 +3313,8 @@ var TableWidget = class extends WidgetType6 {
|
|
|
2365
3313
|
// packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
|
|
2366
3314
|
import { syntaxTree as syntaxTree8 } from "@codemirror/language";
|
|
2367
3315
|
import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
|
|
2368
|
-
import { EditorView as
|
|
2369
|
-
var styles5 =
|
|
3316
|
+
import { EditorView as EditorView13, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
|
|
3317
|
+
var styles5 = EditorView13.baseTheme({
|
|
2370
3318
|
"& .cm-task": {
|
|
2371
3319
|
color: getToken("extend.colors.blue.500")
|
|
2372
3320
|
},
|
|
@@ -2591,11 +3539,11 @@ var defaultTheme = {
|
|
|
2591
3539
|
fontSize: "16px"
|
|
2592
3540
|
},
|
|
2593
3541
|
"&light .cm-content": {
|
|
2594
|
-
color: get3(tokens, "extend.
|
|
3542
|
+
color: get3(tokens, "extend.semanticColors.base.fg.light", "black"),
|
|
2595
3543
|
caretColor: "black"
|
|
2596
3544
|
},
|
|
2597
3545
|
"&dark .cm-content": {
|
|
2598
|
-
color: get3(tokens, "extend.
|
|
3546
|
+
color: get3(tokens, "extend.semanticColors.base.fg.dark", "white"),
|
|
2599
3547
|
caretColor: "white"
|
|
2600
3548
|
},
|
|
2601
3549
|
//
|
|
@@ -2799,7 +3747,7 @@ var EditorModes = [
|
|
|
2799
3747
|
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
|
|
2800
3748
|
const { themeMode } = useThemeContext();
|
|
2801
3749
|
const rootRef = useRef(null);
|
|
2802
|
-
const [view, setView] =
|
|
3750
|
+
const [view, setView] = useState2(null);
|
|
2803
3751
|
useImperativeHandle(forwardedRef, () => view, [
|
|
2804
3752
|
view
|
|
2805
3753
|
]);
|
|
@@ -2828,7 +3776,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
|
|
|
2828
3776
|
selection,
|
|
2829
3777
|
extensions: [
|
|
2830
3778
|
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
2831
|
-
|
|
3779
|
+
EditorView14.exceptionSink.of((err) => {
|
|
2832
3780
|
log2.catch(err, void 0, {
|
|
2833
3781
|
F: __dxlog_file3,
|
|
2834
3782
|
L: 134,
|
|
@@ -2838,17 +3786,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
|
|
|
2838
3786
|
}),
|
|
2839
3787
|
// Theme.
|
|
2840
3788
|
// TODO(burdon): Make configurable.
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
3789
|
+
EditorView14.baseTheme(defaultTheme),
|
|
3790
|
+
EditorView14.theme(theme ?? {}),
|
|
3791
|
+
EditorView14.darkTheme.of(themeMode === "dark"),
|
|
3792
|
+
EditorView14.editorAttributes.of({
|
|
2845
3793
|
class: slots.editor?.className ?? ""
|
|
2846
3794
|
}),
|
|
2847
|
-
|
|
3795
|
+
EditorView14.contentAttributes.of({
|
|
2848
3796
|
class: slots.content?.className ?? ""
|
|
2849
3797
|
}),
|
|
2850
3798
|
// State.
|
|
2851
|
-
|
|
3799
|
+
EditorView14.editable.of(!readonly),
|
|
2852
3800
|
EditorState2.readOnly.of(!!readonly),
|
|
2853
3801
|
// Storage and replication.
|
|
2854
3802
|
// NOTE: This must come before user extensions.
|
|
@@ -2859,7 +3807,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
|
|
|
2859
3807
|
...extensions
|
|
2860
3808
|
].filter(isNotFalsy3)
|
|
2861
3809
|
});
|
|
2862
|
-
const newView = new
|
|
3810
|
+
const newView = new EditorView14({
|
|
2863
3811
|
state,
|
|
2864
3812
|
parent: rootRef.current,
|
|
2865
3813
|
scrollTo,
|
|
@@ -2970,11 +3918,12 @@ import { At, CaretRight, ChatText, Code, Image, Link, ListBullets, ListChecks, L
|
|
|
2970
3918
|
import { createContext } from "@radix-ui/react-context";
|
|
2971
3919
|
import React2 from "react";
|
|
2972
3920
|
import { Button, DensityProvider, Select } from "@dxos/react-ui";
|
|
2973
|
-
import { getSize } from "@dxos/react-ui-theme";
|
|
3921
|
+
import { getSize, mx as mx4 } from "@dxos/react-ui-theme";
|
|
2974
3922
|
var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
|
|
2975
|
-
var ToolbarRoot = ({ children, onAction }) => {
|
|
3923
|
+
var ToolbarRoot = ({ children, onAction, state }) => {
|
|
2976
3924
|
return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
|
|
2977
|
-
onAction
|
|
3925
|
+
onAction,
|
|
3926
|
+
state
|
|
2978
3927
|
}, /* @__PURE__ */ React2.createElement(DensityProvider, {
|
|
2979
3928
|
density: "fine"
|
|
2980
3929
|
}, /* @__PURE__ */ React2.createElement("div", {
|
|
@@ -2982,19 +3931,23 @@ var ToolbarRoot = ({ children, onAction }) => {
|
|
|
2982
3931
|
className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
|
|
2983
3932
|
}, children)));
|
|
2984
3933
|
};
|
|
2985
|
-
var ToolbarButton = ({ Icon, onClick, title }) => {
|
|
2986
|
-
const { onAction } = useToolbarContext("ToolbarButton");
|
|
2987
|
-
const handleClick = () => {
|
|
2988
|
-
const action = onClick();
|
|
3934
|
+
var ToolbarButton = ({ Icon, onClick, title, getState, disable }) => {
|
|
3935
|
+
const { onAction, state } = useToolbarContext("ToolbarButton");
|
|
3936
|
+
const handleClick = (event) => {
|
|
3937
|
+
const action = onClick(state);
|
|
2989
3938
|
if (action) {
|
|
2990
3939
|
onAction?.(action);
|
|
3940
|
+
event.preventDefault();
|
|
2991
3941
|
}
|
|
2992
3942
|
};
|
|
3943
|
+
const active = getState && state ? getState(state) : false;
|
|
3944
|
+
const disabled = disable && state ? disable(state) : false;
|
|
2993
3945
|
return /* @__PURE__ */ React2.createElement(Button, {
|
|
2994
3946
|
variant: "ghost",
|
|
2995
|
-
classNames: "p-2",
|
|
2996
|
-
|
|
2997
|
-
title
|
|
3947
|
+
classNames: mx4("p-2", active && "ring"),
|
|
3948
|
+
onMouseDown: handleClick,
|
|
3949
|
+
title,
|
|
3950
|
+
disabled
|
|
2998
3951
|
}, /* @__PURE__ */ React2.createElement(Icon, {
|
|
2999
3952
|
className: getSize(5)
|
|
3000
3953
|
}));
|
|
@@ -3003,12 +3956,16 @@ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
|
|
|
3003
3956
|
className: "grow"
|
|
3004
3957
|
});
|
|
3005
3958
|
var MarkdownHeading = () => {
|
|
3006
|
-
const { onAction } = useToolbarContext("MarkdownFormatting");
|
|
3959
|
+
const { onAction, state } = useToolbarContext("MarkdownFormatting");
|
|
3960
|
+
const blockType = state ? state.blockType : "paragraph";
|
|
3961
|
+
const header = blockType && /heading(\d)/.exec(blockType);
|
|
3962
|
+
const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : null;
|
|
3007
3963
|
return /* @__PURE__ */ React2.createElement(Select.Root, {
|
|
3008
|
-
|
|
3009
|
-
|
|
3964
|
+
disabled: value === null,
|
|
3965
|
+
value: value ?? "0",
|
|
3966
|
+
onValueChange: (value2) => onAction?.({
|
|
3010
3967
|
type: "heading",
|
|
3011
|
-
data: parseInt(
|
|
3968
|
+
data: parseInt(value2)
|
|
3012
3969
|
})
|
|
3013
3970
|
}, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
|
|
3014
3971
|
classNames: "w-[8rem]"
|
|
@@ -3030,58 +3987,86 @@ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
|
|
|
3030
3987
|
role: "none"
|
|
3031
3988
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3032
3989
|
Icon: TextB,
|
|
3033
|
-
title: "
|
|
3034
|
-
onClick: () => ({
|
|
3035
|
-
type: "
|
|
3036
|
-
|
|
3990
|
+
title: "String",
|
|
3991
|
+
onClick: (s) => ({
|
|
3992
|
+
type: "strong",
|
|
3993
|
+
data: s ? !s.strong : null
|
|
3994
|
+
}),
|
|
3995
|
+
getState: (s) => s.strong,
|
|
3996
|
+
disable: (s) => s.blockType === "codeblock"
|
|
3037
3997
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3038
3998
|
Icon: TextItalic,
|
|
3039
|
-
title: "
|
|
3040
|
-
onClick: () => ({
|
|
3041
|
-
type: "
|
|
3042
|
-
|
|
3999
|
+
title: "Emphasis",
|
|
4000
|
+
onClick: (s) => ({
|
|
4001
|
+
type: "emphasis",
|
|
4002
|
+
data: s ? !s.emphasis : null
|
|
4003
|
+
}),
|
|
4004
|
+
getState: (s) => s.emphasis,
|
|
4005
|
+
disable: (s) => s.blockType === "codeblock"
|
|
3043
4006
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3044
4007
|
Icon: TextStrikethrough,
|
|
3045
4008
|
title: "Strike-through",
|
|
3046
|
-
onClick: () => ({
|
|
3047
|
-
type: "strikethrough"
|
|
3048
|
-
|
|
4009
|
+
onClick: (s) => ({
|
|
4010
|
+
type: "strikethrough",
|
|
4011
|
+
data: s ? !s.strikethrough : null
|
|
4012
|
+
}),
|
|
4013
|
+
getState: (s) => s.strikethrough,
|
|
4014
|
+
disable: (s) => s.blockType === "codeblock"
|
|
4015
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
4016
|
+
Icon: Code,
|
|
4017
|
+
title: "Inline code",
|
|
4018
|
+
onClick: (s) => ({
|
|
4019
|
+
type: "code",
|
|
4020
|
+
data: s ? !s.code : null
|
|
4021
|
+
}),
|
|
4022
|
+
getState: (s) => s.code,
|
|
4023
|
+
disable: (s) => s.blockType === "codeblock"
|
|
3049
4024
|
}));
|
|
3050
4025
|
var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3051
4026
|
role: "none"
|
|
3052
4027
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3053
4028
|
Icon: ListBullets,
|
|
3054
4029
|
title: "Bullet list",
|
|
3055
|
-
onClick: () => ({
|
|
3056
|
-
type: "list"
|
|
3057
|
-
|
|
4030
|
+
onClick: (s) => ({
|
|
4031
|
+
type: "list-bullet",
|
|
4032
|
+
data: s ? s.listStyle !== "bullet" : null
|
|
4033
|
+
}),
|
|
4034
|
+
getState: (s) => s.listStyle === "bullet"
|
|
3058
4035
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3059
4036
|
Icon: ListNumbers,
|
|
3060
4037
|
title: "Numbered list",
|
|
3061
|
-
onClick: () => ({
|
|
3062
|
-
type: "list-ordered"
|
|
3063
|
-
|
|
4038
|
+
onClick: (s) => ({
|
|
4039
|
+
type: "list-ordered",
|
|
4040
|
+
data: s ? s.listStyle !== "ordered" : null
|
|
4041
|
+
}),
|
|
4042
|
+
getState: (s) => s.listStyle === "ordered"
|
|
3064
4043
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3065
4044
|
Icon: ListChecks,
|
|
3066
4045
|
title: "Task list",
|
|
3067
|
-
onClick: () => ({
|
|
3068
|
-
type: "list-tasks"
|
|
3069
|
-
|
|
4046
|
+
onClick: (s) => ({
|
|
4047
|
+
type: "list-tasks",
|
|
4048
|
+
data: s ? s.listStyle !== "task" : null
|
|
4049
|
+
}),
|
|
4050
|
+
getState: (s) => s.listStyle === "task"
|
|
3070
4051
|
}));
|
|
3071
4052
|
var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3072
4053
|
role: "none"
|
|
3073
4054
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3074
4055
|
Icon: CaretRight,
|
|
3075
|
-
title: "Block
|
|
3076
|
-
onClick: () => ({
|
|
3077
|
-
type: "blockquote"
|
|
3078
|
-
|
|
4056
|
+
title: "Block quote",
|
|
4057
|
+
onClick: (s) => ({
|
|
4058
|
+
type: "blockquote",
|
|
4059
|
+
data: s ? !s.blockquote : null
|
|
4060
|
+
}),
|
|
4061
|
+
getState: (s) => s.blockquote
|
|
3079
4062
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3080
4063
|
Icon: Code,
|
|
3081
4064
|
title: "Code block",
|
|
3082
|
-
onClick: () => ({
|
|
3083
|
-
type: "codeblock"
|
|
3084
|
-
|
|
4065
|
+
onClick: (s) => ({
|
|
4066
|
+
type: "codeblock",
|
|
4067
|
+
data: s ? s.blockType !== "codeblock" : null
|
|
4068
|
+
}),
|
|
4069
|
+
getState: (s) => s.blockType === "codeblock"
|
|
3085
4070
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3086
4071
|
Icon: Table2,
|
|
3087
4072
|
title: "Table",
|
|
@@ -3094,9 +4079,11 @@ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
|
|
|
3094
4079
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3095
4080
|
Icon: Link,
|
|
3096
4081
|
title: "Link",
|
|
3097
|
-
onClick: () => ({
|
|
3098
|
-
type: "link"
|
|
3099
|
-
|
|
4082
|
+
onClick: (s) => ({
|
|
4083
|
+
type: "link",
|
|
4084
|
+
data: s ? !s.link : null
|
|
4085
|
+
}),
|
|
4086
|
+
getState: (s) => s.link
|
|
3100
4087
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3101
4088
|
Icon: At,
|
|
3102
4089
|
title: "Mention",
|
|
@@ -3243,24 +4230,32 @@ var useActionHandler = (view) => {
|
|
|
3243
4230
|
if (!view) {
|
|
3244
4231
|
return;
|
|
3245
4232
|
}
|
|
4233
|
+
let inlineType, listType;
|
|
3246
4234
|
switch (action.type) {
|
|
3247
4235
|
case "heading":
|
|
3248
4236
|
setHeading(parseInt(action.data))(view);
|
|
3249
4237
|
break;
|
|
3250
|
-
case "
|
|
3251
|
-
|
|
4238
|
+
case "strong":
|
|
4239
|
+
case "emphasis":
|
|
4240
|
+
case "strikethrough":
|
|
4241
|
+
case "code":
|
|
4242
|
+
inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
|
|
4243
|
+
(typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
|
|
3252
4244
|
break;
|
|
3253
|
-
case "
|
|
3254
|
-
|
|
4245
|
+
case "link":
|
|
4246
|
+
(action.data === false ? removeLink : addLink)(view);
|
|
3255
4247
|
break;
|
|
3256
|
-
case "
|
|
3257
|
-
|
|
4248
|
+
case "list-ordered":
|
|
4249
|
+
case "list-bullet":
|
|
4250
|
+
case "list-tasks":
|
|
4251
|
+
listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
|
|
4252
|
+
(action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
|
|
3258
4253
|
break;
|
|
3259
|
-
case "
|
|
3260
|
-
|
|
4254
|
+
case "blockquote":
|
|
4255
|
+
(action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
|
|
3261
4256
|
break;
|
|
3262
4257
|
case "codeblock":
|
|
3263
|
-
|
|
4258
|
+
(action.data === false ? removeCodeblock : addCodeblock)(view);
|
|
3264
4259
|
break;
|
|
3265
4260
|
case "table":
|
|
3266
4261
|
insertTable(view);
|
|
@@ -3270,18 +4265,17 @@ var useActionHandler = (view) => {
|
|
|
3270
4265
|
break;
|
|
3271
4266
|
}
|
|
3272
4267
|
setTimeout(() => {
|
|
3273
|
-
view.
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
});
|
|
4268
|
+
if (!view.hasFocus) {
|
|
4269
|
+
view.focus();
|
|
4270
|
+
}
|
|
3277
4271
|
});
|
|
3278
4272
|
};
|
|
3279
4273
|
};
|
|
3280
4274
|
|
|
3281
4275
|
// packages/ui/react-ui-editor/src/hooks/useEditorView.ts
|
|
3282
|
-
import { useState as
|
|
4276
|
+
import { useState as useState3 } from "react";
|
|
3283
4277
|
var useEditorView = () => {
|
|
3284
|
-
const [view, setView] =
|
|
4278
|
+
const [view, setView] = useState3(null);
|
|
3285
4279
|
return [
|
|
3286
4280
|
(ref) => {
|
|
3287
4281
|
setView(ref);
|
|
@@ -3291,15 +4285,15 @@ var useEditorView = () => {
|
|
|
3291
4285
|
};
|
|
3292
4286
|
|
|
3293
4287
|
// packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
|
|
3294
|
-
import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
|
|
3295
|
-
import { EditorView as
|
|
3296
|
-
import { useEffect as useEffect3, useRef as useRef2, useState as
|
|
4288
|
+
import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
|
|
4289
|
+
import { EditorView as EditorView15 } from "@codemirror/view";
|
|
4290
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
|
|
3297
4291
|
import { log as log4 } from "@dxos/log";
|
|
3298
4292
|
import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
|
|
3299
4293
|
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
|
|
3300
4294
|
var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
|
|
3301
4295
|
const onUpdate = useRef2();
|
|
3302
|
-
const [view, setView] =
|
|
4296
|
+
const [view, setView] = useState4();
|
|
3303
4297
|
const parentRef = useRef2(null);
|
|
3304
4298
|
useEffect3(() => {
|
|
3305
4299
|
if (parentRef.current) {
|
|
@@ -3308,7 +4302,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
|
|
|
3308
4302
|
selection,
|
|
3309
4303
|
extensions: [
|
|
3310
4304
|
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
3311
|
-
|
|
4305
|
+
EditorView15.exceptionSink.of((err) => {
|
|
3312
4306
|
log4.catch(err, void 0, {
|
|
3313
4307
|
F: __dxlog_file5,
|
|
3314
4308
|
L: 57,
|
|
@@ -3317,12 +4311,12 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
|
|
|
3317
4311
|
});
|
|
3318
4312
|
}),
|
|
3319
4313
|
extensions,
|
|
3320
|
-
|
|
4314
|
+
EditorView15.updateListener.of(() => {
|
|
3321
4315
|
onUpdate.current?.();
|
|
3322
4316
|
})
|
|
3323
4317
|
].filter(isNotFalsy4)
|
|
3324
4318
|
});
|
|
3325
|
-
const view2 = new
|
|
4319
|
+
const view2 = new EditorView15({
|
|
3326
4320
|
parent: parentRef.current,
|
|
3327
4321
|
scrollTo,
|
|
3328
4322
|
state,
|
|
@@ -3346,7 +4340,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
|
|
|
3346
4340
|
useEffect3(() => {
|
|
3347
4341
|
if (view) {
|
|
3348
4342
|
if (!selection && !view.state.selection.main.anchor) {
|
|
3349
|
-
selection =
|
|
4343
|
+
selection = EditorSelection2.single(view.state.doc.line(1).to);
|
|
3350
4344
|
}
|
|
3351
4345
|
if (selection || scrollTo) {
|
|
3352
4346
|
onUpdate.current = () => {
|
|
@@ -3379,19 +4373,19 @@ var createDataExtensions = ({ readonly = false } = {}) => {
|
|
|
3379
4373
|
return [
|
|
3380
4374
|
//
|
|
3381
4375
|
EditorState3.readOnly.of(readonly),
|
|
3382
|
-
|
|
4376
|
+
EditorView15.editable.of(!readonly)
|
|
3383
4377
|
];
|
|
3384
4378
|
};
|
|
3385
4379
|
var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
|
|
3386
4380
|
return [
|
|
3387
4381
|
//
|
|
3388
|
-
|
|
3389
|
-
theme &&
|
|
3390
|
-
|
|
3391
|
-
|
|
4382
|
+
EditorView15.baseTheme(defaultTheme),
|
|
4383
|
+
theme && EditorView15.theme(theme),
|
|
4384
|
+
EditorView15.darkTheme.of(themeMode === "dark"),
|
|
4385
|
+
EditorView15.editorAttributes.of({
|
|
3392
4386
|
class: slots?.editor?.className ?? ""
|
|
3393
4387
|
}),
|
|
3394
|
-
|
|
4388
|
+
EditorView15.contentAttributes.of({
|
|
3395
4389
|
class: slots?.content?.className ?? ""
|
|
3396
4390
|
})
|
|
3397
4391
|
].filter(isNotFalsy4);
|
|
@@ -3399,7 +4393,7 @@ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
|
|
|
3399
4393
|
|
|
3400
4394
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
3401
4395
|
import get4 from "lodash.get";
|
|
3402
|
-
import { useEffect as useEffect4, useState as
|
|
4396
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
3403
4397
|
import { generateName } from "@dxos/display-name";
|
|
3404
4398
|
import { getRawDoc } from "@dxos/echo-schema";
|
|
3405
4399
|
import { invariant as invariant3 } from "@dxos/invariant";
|
|
@@ -3407,7 +4401,7 @@ import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
|
3407
4401
|
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
3408
4402
|
var useTextModel = (props) => {
|
|
3409
4403
|
const { identity, space, text } = props;
|
|
3410
|
-
const [model, setModel] =
|
|
4404
|
+
const [model, setModel] = useState5();
|
|
3411
4405
|
useEffect4(() => setModel(createModel(props)), [
|
|
3412
4406
|
identity,
|
|
3413
4407
|
space,
|
|
@@ -3416,7 +4410,7 @@ var useTextModel = (props) => {
|
|
|
3416
4410
|
return model;
|
|
3417
4411
|
};
|
|
3418
4412
|
var useInMemoryTextModel = ({ id, defaultContent }) => {
|
|
3419
|
-
const [content, setContent] =
|
|
4413
|
+
const [content, setContent] = useState5(defaultContent ?? "");
|
|
3420
4414
|
return {
|
|
3421
4415
|
id,
|
|
3422
4416
|
content,
|
|
@@ -3476,6 +4470,8 @@ export {
|
|
|
3476
4470
|
Cursor,
|
|
3477
4471
|
Doc,
|
|
3478
4472
|
EditorModes,
|
|
4473
|
+
Inline,
|
|
4474
|
+
List,
|
|
3479
4475
|
MarkdownEditor,
|
|
3480
4476
|
RemoteSelectionsDecorator,
|
|
3481
4477
|
SpaceAwarenessProvider,
|
|
@@ -3484,6 +4480,11 @@ export {
|
|
|
3484
4480
|
Toolbar,
|
|
3485
4481
|
YText,
|
|
3486
4482
|
YXmlFragment,
|
|
4483
|
+
addBlockquote,
|
|
4484
|
+
addCodeblock,
|
|
4485
|
+
addLink,
|
|
4486
|
+
addList,
|
|
4487
|
+
addStyle,
|
|
3487
4488
|
autocomplete,
|
|
3488
4489
|
automerge,
|
|
3489
4490
|
awareness,
|
|
@@ -3493,6 +4494,7 @@ export {
|
|
|
3493
4494
|
code2 as code,
|
|
3494
4495
|
codeTheme,
|
|
3495
4496
|
comments,
|
|
4497
|
+
compareFormatting,
|
|
3496
4498
|
createBasicBundle,
|
|
3497
4499
|
createComment,
|
|
3498
4500
|
createDataExtensions,
|
|
@@ -3505,11 +4507,11 @@ export {
|
|
|
3505
4507
|
defaultTheme,
|
|
3506
4508
|
focusComment,
|
|
3507
4509
|
formatting,
|
|
4510
|
+
getFormatting,
|
|
3508
4511
|
getToken,
|
|
3509
4512
|
heading2 as heading,
|
|
3510
4513
|
hr,
|
|
3511
4514
|
image,
|
|
3512
|
-
insertCodeblock,
|
|
3513
4515
|
insertTable,
|
|
3514
4516
|
keymap7 as keymap,
|
|
3515
4517
|
link,
|
|
@@ -3522,22 +4524,33 @@ export {
|
|
|
3522
4524
|
mention,
|
|
3523
4525
|
modelState,
|
|
3524
4526
|
outliner,
|
|
4527
|
+
removeBlockquote,
|
|
4528
|
+
removeCodeblock,
|
|
4529
|
+
removeLink,
|
|
4530
|
+
removeList,
|
|
4531
|
+
removeStyle,
|
|
4532
|
+
setBlockquote,
|
|
3525
4533
|
setComments,
|
|
3526
4534
|
setHeading,
|
|
3527
4535
|
setSelection,
|
|
4536
|
+
setStyle,
|
|
3528
4537
|
table,
|
|
3529
4538
|
tags2 as tags,
|
|
3530
4539
|
tasklist,
|
|
3531
4540
|
textTheme,
|
|
3532
|
-
|
|
3533
|
-
|
|
4541
|
+
toggleBlockquote,
|
|
4542
|
+
toggleCodeblock,
|
|
4543
|
+
toggleEmphasis,
|
|
4544
|
+
toggleInlineCode,
|
|
3534
4545
|
toggleList,
|
|
3535
4546
|
toggleStrikethrough,
|
|
4547
|
+
toggleStrong,
|
|
3536
4548
|
toggleStyle,
|
|
3537
4549
|
typewriter,
|
|
3538
4550
|
useActionHandler,
|
|
3539
4551
|
useComments,
|
|
3540
4552
|
useEditorView,
|
|
4553
|
+
useFormattingState,
|
|
3541
4554
|
useInMemoryTextModel,
|
|
3542
4555
|
useTextEditor,
|
|
3543
4556
|
useTextModel,
|