@dxos/react-ui-editor 0.3.11-next.ee2b64c → 0.4.0
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 +1178 -159
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +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 +55 -10
- 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 +3 -3
- package/src/components/TextEditor/hooks.stories.tsx +6 -1
- package/src/components/Toolbar/Toolbar.stories.tsx +16 -4
- package/src/components/Toolbar/Toolbar.tsx +128 -30
- package/src/extensions/comments.ts +2 -2
- package/src/extensions/markdown/formatting.test.ts +482 -0
- package/src/extensions/markdown/formatting.ts +1118 -69
- package/src/hooks/useActionHandler.ts +47 -16
|
@@ -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";
|
|
@@ -1301,7 +1301,11 @@ var createComment = (view) => {
|
|
|
1301
1301
|
to
|
|
1302
1302
|
});
|
|
1303
1303
|
if (cursor) {
|
|
1304
|
-
const id = options.onCreate?.(
|
|
1304
|
+
const id = options.onCreate?.({
|
|
1305
|
+
cursor,
|
|
1306
|
+
from,
|
|
1307
|
+
location: view.coordsAtPos(from)
|
|
1308
|
+
});
|
|
1305
1309
|
if (id) {
|
|
1306
1310
|
view.dispatch({
|
|
1307
1311
|
effects: setSelection.of({
|
|
@@ -1829,89 +1833,836 @@ var code2 = () => {
|
|
|
1829
1833
|
// packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
|
|
1830
1834
|
import { snippet } from "@codemirror/autocomplete";
|
|
1831
1835
|
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1832
|
-
import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
|
|
1833
|
-
import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
|
|
1834
|
-
|
|
1835
|
-
|
|
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;
|
|
1836
1855
|
const changes = [];
|
|
1856
|
+
let prevBlock = -1;
|
|
1837
1857
|
for (const range of ranges) {
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
from
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
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
|
+
}
|
|
1854
1908
|
});
|
|
1855
1909
|
}
|
|
1910
|
+
if (!changes.length) {
|
|
1911
|
+
return false;
|
|
1912
|
+
}
|
|
1913
|
+
dispatch(state.update({
|
|
1914
|
+
changes,
|
|
1915
|
+
userEvent: "format.setHeading",
|
|
1916
|
+
scrollIntoView: true
|
|
1917
|
+
}));
|
|
1856
1918
|
return true;
|
|
1857
1919
|
};
|
|
1858
|
-
var
|
|
1859
|
-
const
|
|
1860
|
-
|
|
1861
|
-
if (
|
|
1862
|
-
|
|
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
|
+
}
|
|
1863
1946
|
}
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
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
|
+
}
|
|
1873
2018
|
}
|
|
1874
|
-
|
|
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
|
+
}
|
|
1875
2063
|
});
|
|
1876
|
-
|
|
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
|
+
}));
|
|
1877
2074
|
return true;
|
|
1878
2075
|
};
|
|
2076
|
+
var addStyle = (style) => setStyle(style, true);
|
|
2077
|
+
var removeStyle = (style) => setStyle(style, false);
|
|
2078
|
+
var toggleStyle = (style) => (arg) => {
|
|
2079
|
+
const form = getFormatting(arg.state);
|
|
2080
|
+
return setStyle(style, style === 0 ? !form.strong : style === 1 ? !form.emphasis : style === 2 ? !form.strikethrough : !form.code)(arg);
|
|
2081
|
+
};
|
|
2082
|
+
var toggleStrong = toggleStyle(0);
|
|
2083
|
+
var toggleEmphasis = toggleStyle(1);
|
|
2084
|
+
var toggleStrikethrough = toggleStyle(2);
|
|
2085
|
+
var toggleInlineCode = toggleStyle(3);
|
|
2086
|
+
var inlineMarkerText = (type) => type === 0 ? "**" : type === 2 ? "~~" : type === 1 ? "*" : "`";
|
|
2087
|
+
var blockContentStart = (node) => {
|
|
2088
|
+
const atx = /^ATXHeading(\d)/.exec(node.name);
|
|
2089
|
+
if (atx) {
|
|
2090
|
+
return Math.min(node.to, node.from + +atx[1] + 1);
|
|
2091
|
+
}
|
|
2092
|
+
return node.from;
|
|
2093
|
+
};
|
|
2094
|
+
var blockContentEnd = (node, doc) => {
|
|
2095
|
+
const setext = /^SetextHeading(\d)/.exec(node.name);
|
|
2096
|
+
const lastLine = doc.lineAt(node.to);
|
|
2097
|
+
if (setext || /^[\s>]*$/.exec(lastLine.text)) {
|
|
2098
|
+
return lastLine.from - 1;
|
|
2099
|
+
}
|
|
2100
|
+
return node.to;
|
|
2101
|
+
};
|
|
2102
|
+
var skipSpaces = (pos, doc, dir, limit) => {
|
|
2103
|
+
const line = doc.lineAt(pos);
|
|
2104
|
+
while (pos !== limit && line.text[pos - line.from - (dir < 0 ? 1 : 0)] === " ") {
|
|
2105
|
+
pos += dir;
|
|
2106
|
+
}
|
|
2107
|
+
return pos;
|
|
2108
|
+
};
|
|
1879
2109
|
var snippets = {
|
|
1880
2110
|
codeblock: snippet([
|
|
1881
|
-
|
|
1882
|
-
"
|
|
2111
|
+
//
|
|
2112
|
+
"```#{}",
|
|
2113
|
+
"#{}",
|
|
1883
2114
|
"```"
|
|
1884
2115
|
].join("\n")),
|
|
1885
2116
|
table: snippet([
|
|
2117
|
+
//
|
|
1886
2118
|
"| #{col1} | #{col2} |",
|
|
1887
2119
|
"| ---- | ---- |",
|
|
1888
2120
|
"| #{val1} | #{val2} |",
|
|
1889
|
-
"| #{val3} | #{val4} |"
|
|
2121
|
+
"| #{val3} | #{val4} |",
|
|
2122
|
+
""
|
|
1890
2123
|
].join("\n"))
|
|
1891
2124
|
};
|
|
1892
|
-
var insertCodeblock = (view) => {
|
|
1893
|
-
const { selection: { main }, doc } = view.state;
|
|
1894
|
-
const { number } = doc.lineAt(main.anchor);
|
|
1895
|
-
const { from } = doc.line(number);
|
|
1896
|
-
snippets.codeblock(view, null, from, from);
|
|
1897
|
-
};
|
|
1898
2125
|
var insertTable = (view) => {
|
|
1899
2126
|
const { selection: { main }, doc } = view.state;
|
|
1900
2127
|
const { number } = doc.lineAt(main.anchor);
|
|
1901
2128
|
const { from } = doc.line(number);
|
|
1902
2129
|
snippets.table(view, null, from, from);
|
|
1903
2130
|
};
|
|
1904
|
-
var
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
2131
|
+
var removeLinkInner = (from, to, changes, state) => {
|
|
2132
|
+
syntaxTree2(state).iterate({
|
|
2133
|
+
from,
|
|
2134
|
+
to,
|
|
2135
|
+
enter: (node) => {
|
|
2136
|
+
if (node.name === "Link" && node.from < to && node.to > from) {
|
|
2137
|
+
node.node.cursor().iterate((node2) => {
|
|
2138
|
+
const { name } = node2;
|
|
2139
|
+
if (name === "LinkMark" || name === "LinkLabel") {
|
|
2140
|
+
changes.push({
|
|
2141
|
+
from: node2.from,
|
|
2142
|
+
to: node2.to
|
|
2143
|
+
});
|
|
2144
|
+
} else if (name === "LinkTitle" || name === "URL") {
|
|
2145
|
+
changes.push({
|
|
2146
|
+
from: skipSpaces(node2.from, state.doc, -1),
|
|
2147
|
+
to: skipSpaces(node2.to, state.doc, 1)
|
|
2148
|
+
});
|
|
2149
|
+
}
|
|
2150
|
+
});
|
|
2151
|
+
return false;
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
});
|
|
2155
|
+
};
|
|
2156
|
+
var removeLink = ({ state, dispatch }) => {
|
|
2157
|
+
const changes = [];
|
|
2158
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2159
|
+
removeLinkInner(from, to, changes, state);
|
|
2160
|
+
}
|
|
2161
|
+
if (!changes) {
|
|
2162
|
+
return false;
|
|
2163
|
+
}
|
|
2164
|
+
dispatch(state.update({
|
|
2165
|
+
changes,
|
|
2166
|
+
userEvent: "format.link.remove",
|
|
2167
|
+
scrollIntoView: true
|
|
2168
|
+
}));
|
|
2169
|
+
return true;
|
|
2170
|
+
};
|
|
2171
|
+
var addLink = ({ state, dispatch }) => {
|
|
2172
|
+
const changes = state.changeByRange((range) => {
|
|
2173
|
+
let { from, to } = range;
|
|
2174
|
+
const cutStyles = [];
|
|
2175
|
+
let okay = null;
|
|
2176
|
+
syntaxTree2(state).iterate({
|
|
2177
|
+
from,
|
|
2178
|
+
to,
|
|
2179
|
+
enter: (node) => {
|
|
2180
|
+
if (Object.hasOwn(Textblocks, node.name)) {
|
|
2181
|
+
okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state.doc);
|
|
2182
|
+
} else if (Object.hasOwn(InlineMarker, node.name)) {
|
|
2183
|
+
const sNode = node.node;
|
|
2184
|
+
if (node.from < from && node.to <= to) {
|
|
2185
|
+
if (sNode.firstChild.to === from) {
|
|
2186
|
+
from = node.from;
|
|
2187
|
+
} else {
|
|
2188
|
+
cutStyles.push(sNode);
|
|
2189
|
+
}
|
|
2190
|
+
} else if (node.from >= from && node.to > to) {
|
|
2191
|
+
if (sNode.lastChild.from === to) {
|
|
2192
|
+
to = node.to;
|
|
2193
|
+
} else {
|
|
2194
|
+
cutStyles.push(sNode);
|
|
2195
|
+
}
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
2200
|
+
if (okay === null) {
|
|
2201
|
+
const line = state.doc.lineAt(from);
|
|
2202
|
+
okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
|
|
2203
|
+
}
|
|
2204
|
+
if (!okay) {
|
|
2205
|
+
return {
|
|
2206
|
+
range
|
|
2207
|
+
};
|
|
2208
|
+
}
|
|
2209
|
+
const changes2 = [];
|
|
2210
|
+
const changesAfter = [];
|
|
2211
|
+
removeLinkInner(from, to, changesAfter, state);
|
|
2212
|
+
let cursorOffset = 1;
|
|
2213
|
+
for (const style of cutStyles) {
|
|
2214
|
+
const type = InlineMarker[style.name];
|
|
2215
|
+
const mark2 = inlineMarkerText(type);
|
|
2216
|
+
if (style.from < from) {
|
|
2217
|
+
changes2.push({
|
|
2218
|
+
from: skipSpaces(from, state.doc, -1),
|
|
2219
|
+
insert: mark2
|
|
2220
|
+
});
|
|
2221
|
+
changesAfter.push({
|
|
2222
|
+
from: skipSpaces(from, state.doc, 1, to),
|
|
2223
|
+
insert: mark2
|
|
2224
|
+
});
|
|
2225
|
+
} else {
|
|
2226
|
+
changes2.push({
|
|
2227
|
+
from: skipSpaces(to, state.doc, -1, from),
|
|
2228
|
+
insert: mark2
|
|
2229
|
+
});
|
|
2230
|
+
const after = skipSpaces(to, state.doc, 1);
|
|
2231
|
+
if (after === to) {
|
|
2232
|
+
cursorOffset += mark2.length;
|
|
2233
|
+
}
|
|
2234
|
+
changesAfter.push({
|
|
2235
|
+
from: after,
|
|
2236
|
+
insert: mark2
|
|
2237
|
+
});
|
|
2238
|
+
}
|
|
2239
|
+
}
|
|
2240
|
+
changes2.push({
|
|
2241
|
+
from,
|
|
2242
|
+
insert: "["
|
|
2243
|
+
}, {
|
|
2244
|
+
from: to,
|
|
2245
|
+
insert: "]()"
|
|
2246
|
+
});
|
|
2247
|
+
const changeSet = state.changes(changes2.concat(changesAfter));
|
|
2248
|
+
return {
|
|
2249
|
+
changes: changeSet,
|
|
2250
|
+
range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
|
|
2251
|
+
};
|
|
2252
|
+
});
|
|
2253
|
+
if (changes.changes.empty) {
|
|
2254
|
+
return false;
|
|
2255
|
+
}
|
|
2256
|
+
dispatch(state.update(changes, {
|
|
2257
|
+
userEvent: "format.link.add",
|
|
2258
|
+
scrollIntoView: true
|
|
2259
|
+
}));
|
|
2260
|
+
return true;
|
|
2261
|
+
};
|
|
2262
|
+
var addList = (type) => ({ state, dispatch }) => {
|
|
2263
|
+
let lastBlock = -1;
|
|
2264
|
+
let counter = 1;
|
|
2265
|
+
let first = true;
|
|
2266
|
+
let parentColumn = null;
|
|
2267
|
+
const blocks = [];
|
|
2268
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2269
|
+
syntaxTree2(state).iterate({
|
|
2270
|
+
from,
|
|
2271
|
+
to,
|
|
2272
|
+
enter: (node) => {
|
|
2273
|
+
if (Object.hasOwn(Textblocks, node.name) && node.name !== "TableCell" || node.name === "Table") {
|
|
2274
|
+
if (first) {
|
|
2275
|
+
let before = node.node.prevSibling;
|
|
2276
|
+
while (before && /Mark$/.test(before.name)) {
|
|
2277
|
+
before = before.prevSibling;
|
|
2278
|
+
}
|
|
2279
|
+
if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
|
|
2280
|
+
const item = before.lastChild;
|
|
2281
|
+
const itemLine = state.doc.lineAt(item.from);
|
|
2282
|
+
const itemText = itemLine.text.slice(item.from - itemLine.from);
|
|
2283
|
+
parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
|
|
2284
|
+
if (type === 0) {
|
|
2285
|
+
const mark2 = /^\s*(\d+)[.)]/.exec(itemText);
|
|
2286
|
+
if (mark2) {
|
|
2287
|
+
parentColumn += mark2[1].length;
|
|
2288
|
+
counter = +mark2[1] + 1;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
first = false;
|
|
2293
|
+
}
|
|
2294
|
+
if (node.from === lastBlock) {
|
|
2295
|
+
return;
|
|
2296
|
+
}
|
|
2297
|
+
lastBlock = node.from;
|
|
2298
|
+
blocks.push({
|
|
2299
|
+
node: node.node,
|
|
2300
|
+
counter,
|
|
2301
|
+
parentColumn
|
|
2302
|
+
});
|
|
2303
|
+
counter++;
|
|
2304
|
+
return false;
|
|
2305
|
+
}
|
|
2306
|
+
},
|
|
2307
|
+
leave: (node) => {
|
|
2308
|
+
if (node.name === "BulletList" || node.name === "OrderedList" || node.name === "Blockquote") {
|
|
2309
|
+
counter = 1;
|
|
2310
|
+
parentColumn = null;
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
});
|
|
2314
|
+
}
|
|
2315
|
+
if (!blocks.length) {
|
|
2316
|
+
const { from, to } = state.doc.lineAt(state.selection.main.anchor);
|
|
2317
|
+
if (from === to) {
|
|
2318
|
+
dispatch(state.update({
|
|
2319
|
+
changes: [
|
|
2320
|
+
{
|
|
2321
|
+
from,
|
|
2322
|
+
insert: type === 1 ? "- " : type === 0 ? "1. " : "- [ ] "
|
|
2323
|
+
}
|
|
2324
|
+
],
|
|
2325
|
+
userEvent: "format.list.add",
|
|
2326
|
+
scrollIntoView: true
|
|
2327
|
+
}));
|
|
2328
|
+
return true;
|
|
2329
|
+
}
|
|
2330
|
+
return false;
|
|
2331
|
+
}
|
|
2332
|
+
const changes = [];
|
|
2333
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
2334
|
+
const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
|
|
2335
|
+
const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
|
|
2336
|
+
let padding = nodeFrom > 0 && !/\s/.test(state.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
|
|
2337
|
+
if (type === 0) {
|
|
2338
|
+
padding += String(counter2).length;
|
|
2339
|
+
}
|
|
2340
|
+
let line = state.doc.lineAt(nodeFrom);
|
|
2341
|
+
const column = nodeFrom - line.from;
|
|
2342
|
+
if (parentColumn2 !== null && parentColumn2 > column) {
|
|
2343
|
+
padding = Math.max(padding, parentColumn2 - column);
|
|
2344
|
+
}
|
|
2345
|
+
let mark2;
|
|
2346
|
+
if (type === 0) {
|
|
2347
|
+
let max = counter2;
|
|
2348
|
+
for (let j = i + 1; j < blocks.length; j++) {
|
|
2349
|
+
if (blocks[j].counter !== max + 1) {
|
|
2350
|
+
break;
|
|
2351
|
+
}
|
|
2352
|
+
max++;
|
|
2353
|
+
}
|
|
2354
|
+
const num = String(counter2);
|
|
2355
|
+
padding = Math.max(String(max).length, padding);
|
|
2356
|
+
mark2 = " ".repeat(Math.max(0, padding - num.length)) + num + ". ";
|
|
2357
|
+
} else {
|
|
2358
|
+
mark2 = " ".repeat(padding) + "- " + (type === 2 ? "[ ] " : "");
|
|
2359
|
+
}
|
|
2360
|
+
changes.push({
|
|
2361
|
+
from: nodeFrom,
|
|
2362
|
+
insert: mark2
|
|
2363
|
+
});
|
|
2364
|
+
while (line.to < node.to) {
|
|
2365
|
+
line = state.doc.lineAt(line.to + 1);
|
|
2366
|
+
const open = /^[\s>]*/.exec(line.text)[0].length;
|
|
2367
|
+
changes.push({
|
|
2368
|
+
from: line.from + Math.min(open, column),
|
|
2369
|
+
insert: " ".repeat(mark2.length)
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
if (type === 0) {
|
|
2374
|
+
const last = blocks[blocks.length - 1];
|
|
2375
|
+
let next = last.node.nextSibling;
|
|
2376
|
+
while (next && /Mark$/.test(next.name)) {
|
|
2377
|
+
next = next.nextSibling;
|
|
2378
|
+
}
|
|
2379
|
+
if (next?.name === "OrderedList") {
|
|
2380
|
+
renumberListItems(next.firstChild, last.counter + 1, changes, state.doc);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
dispatch(state.update({
|
|
2384
|
+
changes,
|
|
2385
|
+
userEvent: "format.list.add",
|
|
2386
|
+
scrollIntoView: true
|
|
2387
|
+
}));
|
|
2388
|
+
return true;
|
|
2389
|
+
};
|
|
2390
|
+
var removeList = (type) => ({ state, dispatch }) => {
|
|
2391
|
+
let lastBlock = -1;
|
|
2392
|
+
const changes = [];
|
|
2393
|
+
const stack = [];
|
|
2394
|
+
const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
|
|
2395
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2396
|
+
syntaxTree2(state).iterate({
|
|
2397
|
+
from,
|
|
2398
|
+
to,
|
|
2399
|
+
enter: (node) => {
|
|
2400
|
+
const { name } = node;
|
|
2401
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2402
|
+
stack.push(name);
|
|
2403
|
+
} else if (name === "Task" && stack[stack.length - 1] === "BulletList") {
|
|
2404
|
+
stack[stack.length - 1] = "TaskList";
|
|
2405
|
+
}
|
|
2406
|
+
},
|
|
2407
|
+
leave: (node) => {
|
|
2408
|
+
const { name } = node;
|
|
2409
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2410
|
+
stack.pop();
|
|
2411
|
+
} else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
|
|
2412
|
+
lastBlock = node.from;
|
|
2413
|
+
let line = state.doc.lineAt(node.from);
|
|
2414
|
+
const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
|
|
2415
|
+
if (!mark2) {
|
|
2416
|
+
return false;
|
|
2417
|
+
}
|
|
2418
|
+
const column = node.from - line.from;
|
|
2419
|
+
changes.push({
|
|
2420
|
+
from: node.from,
|
|
2421
|
+
to: node.from + mark2[0].length
|
|
2422
|
+
});
|
|
2423
|
+
while (line.to < node.to) {
|
|
2424
|
+
line = state.doc.lineAt(line.to + 1);
|
|
2425
|
+
const open = /^[\s>]*/.exec(line.text)[0].length;
|
|
2426
|
+
if (open > column) {
|
|
2427
|
+
changes.push({
|
|
2428
|
+
from: line.from + column,
|
|
2429
|
+
to: line.from + Math.min(column + mark2[0].length, open)
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
if (node.to >= to) {
|
|
2434
|
+
renumberListItems(node.node.nextSibling, 1, changes, state.doc);
|
|
2435
|
+
}
|
|
2436
|
+
return false;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
if (!changes.length) {
|
|
2442
|
+
return false;
|
|
2443
|
+
}
|
|
2444
|
+
dispatch(state.update({
|
|
2445
|
+
changes,
|
|
2446
|
+
userEvent: "format.list.remove",
|
|
2447
|
+
scrollIntoView: true
|
|
2448
|
+
}));
|
|
2449
|
+
return true;
|
|
2450
|
+
};
|
|
2451
|
+
var toggleList = (type) => (target) => {
|
|
2452
|
+
const formatting2 = getFormatting(target.state);
|
|
2453
|
+
const active = formatting2.listStyle === (type === 1 ? "bullet" : type === 0 ? "ordered" : "task");
|
|
2454
|
+
return (active ? removeList(type) : addList(type))(target);
|
|
2455
|
+
};
|
|
2456
|
+
var renumberListItems = (item, counter, changes, doc) => {
|
|
2457
|
+
for (; item; item = item.nextSibling) {
|
|
2458
|
+
if (item.name === "ListItem") {
|
|
2459
|
+
const number = /(\s*)(\d+)[.)]/.exec(doc.sliceString(item.from, item.from + 10));
|
|
2460
|
+
if (!number || +number[2] === counter) {
|
|
2461
|
+
break;
|
|
2462
|
+
}
|
|
2463
|
+
const size = number[1].length + number[2].length;
|
|
2464
|
+
const newNum = String(counter);
|
|
2465
|
+
changes.push({
|
|
2466
|
+
from: item.from + Math.max(0, size - newNum.length),
|
|
2467
|
+
to: item.from + size,
|
|
2468
|
+
insert: newNum
|
|
2469
|
+
});
|
|
2470
|
+
counter++;
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
};
|
|
2474
|
+
var setBlockquote = (enable) => ({ state, dispatch }) => {
|
|
2475
|
+
const lines = [];
|
|
2476
|
+
let lastBlock = -1;
|
|
2477
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2478
|
+
syntaxTree2(state).iterate({
|
|
2479
|
+
from,
|
|
2480
|
+
to,
|
|
2481
|
+
enter: (node) => {
|
|
2482
|
+
if (Object.hasOwn(Textblocks, node.name) || node.name === "Table") {
|
|
2483
|
+
if (node.from === lastBlock) {
|
|
2484
|
+
return false;
|
|
2485
|
+
}
|
|
2486
|
+
lastBlock = node.from;
|
|
2487
|
+
let line = state.doc.lineAt(node.from);
|
|
2488
|
+
if (line.number > 1) {
|
|
2489
|
+
const prevLine = state.doc.line(line.number - 1);
|
|
2490
|
+
if (/^[>\s]*$/.test(prevLine.text)) {
|
|
2491
|
+
if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
|
|
2492
|
+
lines.push(prevLine);
|
|
2493
|
+
}
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
for (; ; ) {
|
|
2497
|
+
lines.push(line);
|
|
2498
|
+
if (line.to >= node.to) {
|
|
2499
|
+
break;
|
|
2500
|
+
}
|
|
2501
|
+
line = state.doc.line(line.number + 1);
|
|
2502
|
+
}
|
|
2503
|
+
if (!enable && line.number < state.doc.lines) {
|
|
2504
|
+
const nextLine = state.doc.line(line.number + 1);
|
|
2505
|
+
if (/^[>\s]*$/.test(nextLine.text)) {
|
|
2506
|
+
lines.push(nextLine);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
return false;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
});
|
|
2513
|
+
}
|
|
2514
|
+
const changes = [];
|
|
2515
|
+
for (const line of lines) {
|
|
2516
|
+
if (enable) {
|
|
2517
|
+
changes.push({
|
|
2518
|
+
from: line.from,
|
|
2519
|
+
insert: /\S/.test(line.text) ? "> " : ">"
|
|
2520
|
+
});
|
|
2521
|
+
} else {
|
|
2522
|
+
const quote = /((?:[\s>\-+*]|\d+[.)])*?)> ?/.exec(line.text);
|
|
2523
|
+
if (quote) {
|
|
2524
|
+
changes.push({
|
|
2525
|
+
from: line.from + quote[1].length,
|
|
2526
|
+
to: line.from + quote[0].length
|
|
2527
|
+
});
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
if (!changes.length) {
|
|
2532
|
+
return false;
|
|
2533
|
+
}
|
|
2534
|
+
dispatch(state.update({
|
|
2535
|
+
changes,
|
|
2536
|
+
userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
|
|
2537
|
+
scrollIntoView: true
|
|
2538
|
+
}));
|
|
2539
|
+
return true;
|
|
2540
|
+
};
|
|
2541
|
+
var addBlockquote = setBlockquote(true);
|
|
2542
|
+
var removeBlockquote = setBlockquote(false);
|
|
2543
|
+
var toggleBlockquote = (target) => {
|
|
2544
|
+
return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
|
|
2545
|
+
};
|
|
2546
|
+
var addCodeblock = (target) => {
|
|
2547
|
+
const { state, dispatch } = target;
|
|
2548
|
+
const { selection } = state;
|
|
2549
|
+
if (selection.ranges.length === 1 && selection.main.empty) {
|
|
2550
|
+
const { head } = selection.main;
|
|
2551
|
+
const line = state.doc.lineAt(head);
|
|
2552
|
+
if (!/\S/.test(line.text) && head === line.from) {
|
|
2553
|
+
snippets.codeblock(target, null, line.from, line.to);
|
|
2554
|
+
return true;
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
const ranges = [];
|
|
2558
|
+
for (const { from, to } of selection.ranges) {
|
|
2559
|
+
let blockFrom = from;
|
|
2560
|
+
let blockTo = to;
|
|
2561
|
+
syntaxTree2(state).iterate({
|
|
2562
|
+
from,
|
|
2563
|
+
to,
|
|
2564
|
+
enter: (node) => {
|
|
2565
|
+
if (Object.hasOwn(Textblocks, node.name)) {
|
|
2566
|
+
if (from >= node.from && to <= node.to) {
|
|
2567
|
+
blockFrom = node.from;
|
|
2568
|
+
blockTo = node.to;
|
|
2569
|
+
} else {
|
|
2570
|
+
blockFrom = Math.min(blockFrom, state.doc.lineAt(node.from).from);
|
|
2571
|
+
blockTo = Math.max(blockTo, state.doc.lineAt(node.to).to);
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
});
|
|
2576
|
+
if (ranges.length && ranges[ranges.length - 1].to >= blockFrom - 1) {
|
|
2577
|
+
ranges[ranges.length - 1].to = blockTo;
|
|
2578
|
+
} else {
|
|
2579
|
+
ranges.push({
|
|
2580
|
+
from: blockFrom,
|
|
2581
|
+
to: blockTo
|
|
2582
|
+
});
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
if (!ranges.length) {
|
|
2586
|
+
return false;
|
|
2587
|
+
}
|
|
2588
|
+
const changes = ranges.map(({ from, to }) => {
|
|
2589
|
+
const column = from - state.doc.lineAt(from).from;
|
|
2590
|
+
return [
|
|
2591
|
+
{
|
|
2592
|
+
from,
|
|
2593
|
+
insert: "```\n" + " ".repeat(column)
|
|
2594
|
+
},
|
|
2595
|
+
{
|
|
2596
|
+
from: to,
|
|
2597
|
+
insert: "\n" + " ".repeat(column) + "```"
|
|
2598
|
+
}
|
|
2599
|
+
];
|
|
2600
|
+
});
|
|
2601
|
+
dispatch(state.update({
|
|
2602
|
+
changes,
|
|
2603
|
+
userEvent: "format.codeblock.add",
|
|
2604
|
+
scrollIntoView: true
|
|
2605
|
+
}));
|
|
2606
|
+
return true;
|
|
2607
|
+
};
|
|
2608
|
+
var removeCodeblock = ({ state, dispatch }) => {
|
|
2609
|
+
const changes = [];
|
|
2610
|
+
let lastBlock = -1;
|
|
2611
|
+
for (const { from, to } of state.selection.ranges) {
|
|
2612
|
+
syntaxTree2(state).iterate({
|
|
2613
|
+
from,
|
|
2614
|
+
to,
|
|
2615
|
+
enter: (node) => {
|
|
2616
|
+
if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
|
|
2617
|
+
lastBlock = node.from;
|
|
2618
|
+
const firstLine = state.doc.lineAt(node.from);
|
|
2619
|
+
if (node.name === "FencedCode") {
|
|
2620
|
+
changes.push({
|
|
2621
|
+
from: node.from,
|
|
2622
|
+
to: firstLine.to + 1 + node.from - firstLine.from
|
|
2623
|
+
});
|
|
2624
|
+
const lastLine = state.doc.lineAt(node.to);
|
|
2625
|
+
if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
|
|
2626
|
+
changes.push({
|
|
2627
|
+
from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
|
|
2628
|
+
to: lastLine.to
|
|
2629
|
+
});
|
|
2630
|
+
}
|
|
2631
|
+
} else {
|
|
2632
|
+
const column = node.from - firstLine.from;
|
|
2633
|
+
for (let line = firstLine; ; line = state.doc.line(line.number + 1)) {
|
|
2634
|
+
changes.push({
|
|
2635
|
+
from: line.from + column - 4,
|
|
2636
|
+
to: line.from + column
|
|
2637
|
+
});
|
|
2638
|
+
if (line.to >= node.to) {
|
|
2639
|
+
break;
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
}
|
|
2645
|
+
});
|
|
2646
|
+
}
|
|
2647
|
+
if (!changes.length) {
|
|
2648
|
+
return false;
|
|
2649
|
+
}
|
|
2650
|
+
dispatch(state.update({
|
|
2651
|
+
changes,
|
|
2652
|
+
userEvent: "format.codeblock.remove",
|
|
2653
|
+
scrollIntoView: true
|
|
2654
|
+
}));
|
|
2655
|
+
return true;
|
|
2656
|
+
};
|
|
2657
|
+
var toggleCodeblock = (target) => {
|
|
2658
|
+
return (getFormatting(target.state).blockType === "codeblock" ? removeCodeblock : addCodeblock)(target);
|
|
1908
2659
|
};
|
|
1909
2660
|
var formatting = (options = {}) => {
|
|
1910
2661
|
return [
|
|
1911
2662
|
keymap5.of([
|
|
1912
2663
|
{
|
|
1913
2664
|
key: "meta-b",
|
|
1914
|
-
run:
|
|
2665
|
+
run: toggleStrong
|
|
1915
2666
|
}
|
|
1916
2667
|
]),
|
|
1917
2668
|
styling()
|
|
@@ -1965,6 +2716,209 @@ var styling = () => {
|
|
|
1965
2716
|
})
|
|
1966
2717
|
];
|
|
1967
2718
|
};
|
|
2719
|
+
var InlineMarker = {
|
|
2720
|
+
Emphasis: 1,
|
|
2721
|
+
StrongEmphasis: 0,
|
|
2722
|
+
InlineCode: 3,
|
|
2723
|
+
Strikethrough: 2
|
|
2724
|
+
};
|
|
2725
|
+
var IgnoreInline = /* @__PURE__ */ new Set([
|
|
2726
|
+
"Autolink",
|
|
2727
|
+
"CodeMark",
|
|
2728
|
+
"CodeText",
|
|
2729
|
+
"Comment",
|
|
2730
|
+
"EmphasisMark",
|
|
2731
|
+
"Hardbreak",
|
|
2732
|
+
"HeaderMark",
|
|
2733
|
+
"HTMLTag",
|
|
2734
|
+
"LinkMark",
|
|
2735
|
+
"ListMark",
|
|
2736
|
+
"ProcessingInstruction",
|
|
2737
|
+
"QuoteMark",
|
|
2738
|
+
"StrikethroughMark",
|
|
2739
|
+
"SubscriptMark",
|
|
2740
|
+
"SuperscriptMark",
|
|
2741
|
+
"TaskMarker"
|
|
2742
|
+
]);
|
|
2743
|
+
var Textblocks = {
|
|
2744
|
+
ATXHeading1: "heading1",
|
|
2745
|
+
ATXHeading2: "heading2",
|
|
2746
|
+
ATXHeading3: "heading3",
|
|
2747
|
+
ATXHeading4: "heading4",
|
|
2748
|
+
ATXHeading5: "heading5",
|
|
2749
|
+
ATXHeading6: "heading6",
|
|
2750
|
+
CodeBlock: "codeblock",
|
|
2751
|
+
FencedCode: "codeblock",
|
|
2752
|
+
Paragraph: "paragraph",
|
|
2753
|
+
SetextHeading1: "heading1",
|
|
2754
|
+
SetextHeading2: "heading2",
|
|
2755
|
+
TableCell: "tablecell",
|
|
2756
|
+
Task: "paragraph"
|
|
2757
|
+
};
|
|
2758
|
+
var getFormatting = (state) => {
|
|
2759
|
+
let blockType = null;
|
|
2760
|
+
const inline = [
|
|
2761
|
+
null,
|
|
2762
|
+
null,
|
|
2763
|
+
null,
|
|
2764
|
+
null
|
|
2765
|
+
];
|
|
2766
|
+
let link2 = false;
|
|
2767
|
+
let blockQuote = null;
|
|
2768
|
+
let listStyle = null;
|
|
2769
|
+
const stack = [];
|
|
2770
|
+
let currentBlock = null;
|
|
2771
|
+
const advanceInline = (upto) => {
|
|
2772
|
+
if (!currentBlock) {
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
upto = Math.min(upto, currentBlock.end);
|
|
2776
|
+
if (upto <= currentBlock.pos) {
|
|
2777
|
+
return;
|
|
2778
|
+
}
|
|
2779
|
+
for (let i = 0; i < currentBlock.active.length; i++) {
|
|
2780
|
+
if (inline[i] === false) {
|
|
2781
|
+
continue;
|
|
2782
|
+
} else if (currentBlock.active[i]) {
|
|
2783
|
+
inline[i] = true;
|
|
2784
|
+
} else if (/\S/.test(state.doc.sliceString(currentBlock.pos, upto))) {
|
|
2785
|
+
inline[i] = false;
|
|
2786
|
+
}
|
|
2787
|
+
}
|
|
2788
|
+
currentBlock.pos = upto;
|
|
2789
|
+
};
|
|
2790
|
+
const skipInline = (upto) => {
|
|
2791
|
+
if (currentBlock && upto > currentBlock.pos) {
|
|
2792
|
+
currentBlock.pos = Math.min(upto, currentBlock.end);
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
const { selection } = state;
|
|
2796
|
+
for (const range of selection.ranges) {
|
|
2797
|
+
if (range.empty && inline.some((v) => v === null)) {
|
|
2798
|
+
const contextSize = Math.min(range.head, 6);
|
|
2799
|
+
const contextBefore = state.doc.sliceString(range.head - contextSize, range.head);
|
|
2800
|
+
let contextAfter = state.doc.sliceString(range.head, range.head + contextSize);
|
|
2801
|
+
for (let i = 0; i < contextSize; i++) {
|
|
2802
|
+
const ch = contextAfter[i];
|
|
2803
|
+
if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
|
|
2804
|
+
contextAfter = contextAfter.slice(0, i);
|
|
2805
|
+
break;
|
|
2806
|
+
}
|
|
2807
|
+
}
|
|
2808
|
+
for (let i = 0; i < inline.length; i++) {
|
|
2809
|
+
const mark2 = inlineMarkerText(i);
|
|
2810
|
+
const found = contextAfter.indexOf(mark2);
|
|
2811
|
+
if (found > -1) {
|
|
2812
|
+
contextAfter = contextAfter.slice(0, found) + contextAfter.slice(found + mark2.length);
|
|
2813
|
+
if (inline[i] === null) {
|
|
2814
|
+
inline[i] = true;
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
syntaxTree2(state).iterate({
|
|
2820
|
+
from: range.from,
|
|
2821
|
+
to: range.to,
|
|
2822
|
+
enter: (node) => {
|
|
2823
|
+
advanceInline(node.from);
|
|
2824
|
+
const { name } = node;
|
|
2825
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2826
|
+
stack.push(name);
|
|
2827
|
+
} else if (name === "Link") {
|
|
2828
|
+
link2 = true;
|
|
2829
|
+
} else if (Object.hasOwn(Textblocks, name) && (range.empty || node.to > range.from || node.from < range.to)) {
|
|
2830
|
+
if (name === "Task" && stack[stack.length - 1] === "BulletList") {
|
|
2831
|
+
stack[stack.length - 1] = "TaskList";
|
|
2832
|
+
}
|
|
2833
|
+
const blockCode = Textblocks[name];
|
|
2834
|
+
if (blockType === null) {
|
|
2835
|
+
blockType = blockCode;
|
|
2836
|
+
} else if (blockType !== blockCode) {
|
|
2837
|
+
blockType = false;
|
|
2838
|
+
}
|
|
2839
|
+
if (blockCode !== "codeblock" && inline.some((i) => i !== false)) {
|
|
2840
|
+
currentBlock = {
|
|
2841
|
+
pos: Math.max(range.from, node.from),
|
|
2842
|
+
end: Math.min(range.to, node.to),
|
|
2843
|
+
active: [
|
|
2844
|
+
false,
|
|
2845
|
+
false,
|
|
2846
|
+
false,
|
|
2847
|
+
false
|
|
2848
|
+
]
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
} else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
|
|
2852
|
+
const index = InlineMarker[name];
|
|
2853
|
+
if (range.empty && inline[index] === null) {
|
|
2854
|
+
inline[index] = true;
|
|
2855
|
+
}
|
|
2856
|
+
currentBlock.active[index] = true;
|
|
2857
|
+
} else if (IgnoreInline.has(name)) {
|
|
2858
|
+
skipInline(node.to);
|
|
2859
|
+
}
|
|
2860
|
+
},
|
|
2861
|
+
leave: (node) => {
|
|
2862
|
+
advanceInline(node.to);
|
|
2863
|
+
const { name } = node;
|
|
2864
|
+
if (name === "BulletList" || name === "OrderedList" || name === "Blockquote") {
|
|
2865
|
+
stack.pop();
|
|
2866
|
+
} else if (Object.hasOwn(Textblocks, name)) {
|
|
2867
|
+
let hasList = false;
|
|
2868
|
+
let hasQuote = false;
|
|
2869
|
+
for (let i = stack.length - 1; i >= 0; i--) {
|
|
2870
|
+
if (stack[i] === "Blockquote") {
|
|
2871
|
+
hasQuote = true;
|
|
2872
|
+
} else if (!hasList) {
|
|
2873
|
+
hasList = stack[i] === "TaskList" ? "task" : stack[i] === "BulletList" ? "bullet" : "ordered";
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
if (blockQuote === null) {
|
|
2877
|
+
blockQuote = hasQuote;
|
|
2878
|
+
} else if (!hasQuote && blockQuote) {
|
|
2879
|
+
blockQuote = false;
|
|
2880
|
+
}
|
|
2881
|
+
if (listStyle === null) {
|
|
2882
|
+
listStyle = hasList;
|
|
2883
|
+
} else if (listStyle !== hasList) {
|
|
2884
|
+
listStyle = false;
|
|
2885
|
+
}
|
|
2886
|
+
currentBlock = null;
|
|
2887
|
+
} else if (Object.hasOwn(InlineMarker, name) && currentBlock) {
|
|
2888
|
+
currentBlock.active[InlineMarker[name]] = false;
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
});
|
|
2892
|
+
}
|
|
2893
|
+
const { from, to } = state.doc.lineAt(selection.main.anchor);
|
|
2894
|
+
const blankLine = from === to;
|
|
2895
|
+
return {
|
|
2896
|
+
blankLine,
|
|
2897
|
+
blockType: blockType || null,
|
|
2898
|
+
blockQuote: blockQuote ?? false,
|
|
2899
|
+
code: inline[3] ?? false,
|
|
2900
|
+
emphasis: inline[1] ?? false,
|
|
2901
|
+
strong: inline[0] ?? false,
|
|
2902
|
+
strikethrough: inline[2] ?? false,
|
|
2903
|
+
link: link2,
|
|
2904
|
+
listStyle: listStyle || null
|
|
2905
|
+
};
|
|
2906
|
+
};
|
|
2907
|
+
var useFormattingState = () => {
|
|
2908
|
+
const [state, setState] = useState(null);
|
|
2909
|
+
const observer = useMemo(() => EditorView9.updateListener.of((update2) => {
|
|
2910
|
+
if (update2.docChanged || update2.selectionSet) {
|
|
2911
|
+
const newState = getFormatting(update2.state);
|
|
2912
|
+
if (!state || !compareFormatting(state, newState)) {
|
|
2913
|
+
setState(newState);
|
|
2914
|
+
}
|
|
2915
|
+
}
|
|
2916
|
+
}), []);
|
|
2917
|
+
return [
|
|
2918
|
+
state,
|
|
2919
|
+
observer
|
|
2920
|
+
];
|
|
2921
|
+
};
|
|
1968
2922
|
|
|
1969
2923
|
// packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
|
|
1970
2924
|
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
@@ -2017,8 +2971,8 @@ var heading2 = () => {
|
|
|
2017
2971
|
// packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
|
|
2018
2972
|
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
2019
2973
|
import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
|
|
2020
|
-
import { Decoration as Decoration6, EditorView as
|
|
2021
|
-
var styles4 =
|
|
2974
|
+
import { Decoration as Decoration6, EditorView as EditorView10, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
2975
|
+
var styles4 = EditorView10.baseTheme({
|
|
2022
2976
|
"& .cm-hr": {
|
|
2023
2977
|
// Note that block-level decorations should not have vertical margins,
|
|
2024
2978
|
borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
@@ -2072,7 +3026,7 @@ var hr = () => {
|
|
|
2072
3026
|
// packages/ui/react-ui-editor/src/extensions/markdown/image.ts
|
|
2073
3027
|
import { syntaxTree as syntaxTree5 } from "@codemirror/language";
|
|
2074
3028
|
import { StateField as StateField3 } from "@codemirror/state";
|
|
2075
|
-
import { Decoration as Decoration7, EditorView as
|
|
3029
|
+
import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
2076
3030
|
var image = (options = {}) => {
|
|
2077
3031
|
return StateField3.define({
|
|
2078
3032
|
create: (state) => {
|
|
@@ -2099,7 +3053,7 @@ var image = (options = {}) => {
|
|
|
2099
3053
|
add: buildDecorations4(from, to, tr.state)
|
|
2100
3054
|
});
|
|
2101
3055
|
},
|
|
2102
|
-
provide: (field) =>
|
|
3056
|
+
provide: (field) => EditorView11.decorations.from(field)
|
|
2103
3057
|
});
|
|
2104
3058
|
};
|
|
2105
3059
|
var buildDecorations4 = (from, to, state) => {
|
|
@@ -2281,12 +3235,12 @@ var buildDecorations5 = (view, options) => {
|
|
|
2281
3235
|
// packages/ui/react-ui-editor/src/extensions/markdown/table.ts
|
|
2282
3236
|
import { syntaxTree as syntaxTree7 } from "@codemirror/language";
|
|
2283
3237
|
import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
|
|
2284
|
-
import { Decoration as Decoration9, EditorView as
|
|
3238
|
+
import { Decoration as Decoration9, EditorView as EditorView12, WidgetType as WidgetType6 } from "@codemirror/view";
|
|
2285
3239
|
var table = (options = {}) => {
|
|
2286
3240
|
return StateField4.define({
|
|
2287
3241
|
create: (state) => update(state, options),
|
|
2288
3242
|
update: (_, tr) => update(tr.state, options),
|
|
2289
|
-
provide: (field) =>
|
|
3243
|
+
provide: (field) => EditorView12.decorations.from(field)
|
|
2290
3244
|
});
|
|
2291
3245
|
};
|
|
2292
3246
|
var update = (state, options) => {
|
|
@@ -2379,8 +3333,8 @@ var TableWidget = class extends WidgetType6 {
|
|
|
2379
3333
|
// packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
|
|
2380
3334
|
import { syntaxTree as syntaxTree8 } from "@codemirror/language";
|
|
2381
3335
|
import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
|
|
2382
|
-
import { EditorView as
|
|
2383
|
-
var styles5 =
|
|
3336
|
+
import { EditorView as EditorView13, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
|
|
3337
|
+
var styles5 = EditorView13.baseTheme({
|
|
2384
3338
|
"& .cm-task": {
|
|
2385
3339
|
color: getToken("extend.colors.blue.500")
|
|
2386
3340
|
},
|
|
@@ -2813,7 +3767,7 @@ var EditorModes = [
|
|
|
2813
3767
|
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
|
|
2814
3768
|
const { themeMode } = useThemeContext();
|
|
2815
3769
|
const rootRef = useRef(null);
|
|
2816
|
-
const [view, setView] =
|
|
3770
|
+
const [view, setView] = useState2(null);
|
|
2817
3771
|
useImperativeHandle(forwardedRef, () => view, [
|
|
2818
3772
|
view
|
|
2819
3773
|
]);
|
|
@@ -2842,7 +3796,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
|
|
|
2842
3796
|
selection,
|
|
2843
3797
|
extensions: [
|
|
2844
3798
|
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
2845
|
-
|
|
3799
|
+
EditorView14.exceptionSink.of((err) => {
|
|
2846
3800
|
log2.catch(err, void 0, {
|
|
2847
3801
|
F: __dxlog_file3,
|
|
2848
3802
|
L: 134,
|
|
@@ -2852,17 +3806,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
|
|
|
2852
3806
|
}),
|
|
2853
3807
|
// Theme.
|
|
2854
3808
|
// TODO(burdon): Make configurable.
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
3809
|
+
EditorView14.baseTheme(defaultTheme),
|
|
3810
|
+
EditorView14.theme(theme ?? {}),
|
|
3811
|
+
EditorView14.darkTheme.of(themeMode === "dark"),
|
|
3812
|
+
EditorView14.editorAttributes.of({
|
|
2859
3813
|
class: slots.editor?.className ?? ""
|
|
2860
3814
|
}),
|
|
2861
|
-
|
|
3815
|
+
EditorView14.contentAttributes.of({
|
|
2862
3816
|
class: slots.content?.className ?? ""
|
|
2863
3817
|
}),
|
|
2864
3818
|
// State.
|
|
2865
|
-
|
|
3819
|
+
EditorView14.editable.of(!readonly),
|
|
2866
3820
|
EditorState2.readOnly.of(!!readonly),
|
|
2867
3821
|
// Storage and replication.
|
|
2868
3822
|
// NOTE: This must come before user extensions.
|
|
@@ -2873,7 +3827,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
|
|
|
2873
3827
|
...extensions
|
|
2874
3828
|
].filter(isNotFalsy3)
|
|
2875
3829
|
});
|
|
2876
|
-
const newView = new
|
|
3830
|
+
const newView = new EditorView14({
|
|
2877
3831
|
state,
|
|
2878
3832
|
parent: rootRef.current,
|
|
2879
3833
|
scrollTo,
|
|
@@ -2980,35 +3934,49 @@ var defaultMarkdownSlots = {
|
|
|
2980
3934
|
};
|
|
2981
3935
|
|
|
2982
3936
|
// packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
|
|
2983
|
-
import {
|
|
3937
|
+
import { CaretRight, ChatText, Code, CodeBlock, Link, ListBullets, ListChecks, ListNumbers, Paragraph, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic } from "@phosphor-icons/react";
|
|
2984
3938
|
import { createContext } from "@radix-ui/react-context";
|
|
2985
3939
|
import React2 from "react";
|
|
2986
3940
|
import { Button, DensityProvider, Select } from "@dxos/react-ui";
|
|
2987
|
-
import { getSize } from "@dxos/react-ui-theme";
|
|
3941
|
+
import { getSize, mx as mx4 } from "@dxos/react-ui-theme";
|
|
3942
|
+
var HeadingIcons = {
|
|
3943
|
+
"0": Paragraph,
|
|
3944
|
+
"1": TextHOne,
|
|
3945
|
+
"2": TextHTwo,
|
|
3946
|
+
"3": TextHThree,
|
|
3947
|
+
"4": TextHFour,
|
|
3948
|
+
"5": TextHFive,
|
|
3949
|
+
"6": TextHSix
|
|
3950
|
+
};
|
|
2988
3951
|
var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
|
|
2989
|
-
var ToolbarRoot = ({ children, onAction }) => {
|
|
3952
|
+
var ToolbarRoot = ({ children, onAction, state }) => {
|
|
2990
3953
|
return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
|
|
2991
|
-
onAction
|
|
3954
|
+
onAction,
|
|
3955
|
+
state
|
|
2992
3956
|
}, /* @__PURE__ */ React2.createElement(DensityProvider, {
|
|
2993
3957
|
density: "fine"
|
|
2994
3958
|
}, /* @__PURE__ */ React2.createElement("div", {
|
|
2995
3959
|
role: "toolbar",
|
|
2996
|
-
className: "flex w-full shrink-0 p-
|
|
3960
|
+
className: "flex w-full shrink-0 p-1 gap-4 items-center whitespace-nowrap overflow-hidden"
|
|
2997
3961
|
}, children)));
|
|
2998
3962
|
};
|
|
2999
|
-
var ToolbarButton = ({ Icon, onClick, title }) => {
|
|
3000
|
-
const { onAction } = useToolbarContext("ToolbarButton");
|
|
3001
|
-
const
|
|
3002
|
-
|
|
3963
|
+
var ToolbarButton = ({ Icon, onClick, title, getState, disable }) => {
|
|
3964
|
+
const { onAction, state } = useToolbarContext("ToolbarButton");
|
|
3965
|
+
const active = getState && state ? getState(state) : false;
|
|
3966
|
+
const disabled = disable && state ? disable(state) : false;
|
|
3967
|
+
const handleClick = (event) => {
|
|
3968
|
+
const action = onClick(state);
|
|
3003
3969
|
if (action) {
|
|
3004
3970
|
onAction?.(action);
|
|
3971
|
+
event.preventDefault();
|
|
3005
3972
|
}
|
|
3006
3973
|
};
|
|
3007
3974
|
return /* @__PURE__ */ React2.createElement(Button, {
|
|
3008
3975
|
variant: "ghost",
|
|
3009
|
-
classNames: "p-2",
|
|
3010
|
-
|
|
3011
|
-
title
|
|
3976
|
+
classNames: mx4("p-2", active && "ring-[1px]"),
|
|
3977
|
+
onMouseDown: handleClick,
|
|
3978
|
+
title,
|
|
3979
|
+
disabled
|
|
3012
3980
|
}, /* @__PURE__ */ React2.createElement(Icon, {
|
|
3013
3981
|
className: getSize(5)
|
|
3014
3982
|
}));
|
|
@@ -3017,16 +3985,21 @@ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
|
|
|
3017
3985
|
className: "grow"
|
|
3018
3986
|
});
|
|
3019
3987
|
var MarkdownHeading = () => {
|
|
3020
|
-
const { onAction } = useToolbarContext("MarkdownFormatting");
|
|
3988
|
+
const { onAction, state } = useToolbarContext("MarkdownFormatting");
|
|
3989
|
+
const blockType = state ? state.blockType : "paragraph";
|
|
3990
|
+
const header = blockType && /heading(\d)/.exec(blockType);
|
|
3991
|
+
const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
|
|
3992
|
+
const HeadingIcon = HeadingIcons[value ?? "0"];
|
|
3021
3993
|
return /* @__PURE__ */ React2.createElement(Select.Root, {
|
|
3022
|
-
|
|
3023
|
-
|
|
3994
|
+
disabled: value === null,
|
|
3995
|
+
value: value ?? "0",
|
|
3996
|
+
onValueChange: (value2) => onAction?.({
|
|
3024
3997
|
type: "heading",
|
|
3025
|
-
data: parseInt(
|
|
3998
|
+
data: parseInt(value2)
|
|
3026
3999
|
})
|
|
3027
|
-
}, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
|
|
3028
|
-
|
|
3029
|
-
}), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
|
|
4000
|
+
}, /* @__PURE__ */ React2.createElement(Select.TriggerButton, null, /* @__PURE__ */ React2.createElement(HeadingIcon, {
|
|
4001
|
+
className: getSize(5)
|
|
4002
|
+
})), /* @__PURE__ */ React2.createElement(Select.Portal, null, /* @__PURE__ */ React2.createElement(Select.Content, null, /* @__PURE__ */ React2.createElement(Select.Viewport, null, /* @__PURE__ */ React2.createElement(Select.Option, {
|
|
3030
4003
|
value: "0"
|
|
3031
4004
|
}, "Paragraph"), [
|
|
3032
4005
|
1,
|
|
@@ -3044,21 +4017,39 @@ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
|
|
|
3044
4017
|
role: "none"
|
|
3045
4018
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3046
4019
|
Icon: TextB,
|
|
3047
|
-
title: "
|
|
3048
|
-
|
|
3049
|
-
|
|
4020
|
+
title: "String",
|
|
4021
|
+
disable: (s) => s.blockType === "codeblock",
|
|
4022
|
+
getState: (s) => s.strong,
|
|
4023
|
+
onClick: (s) => ({
|
|
4024
|
+
type: "strong",
|
|
4025
|
+
data: s ? !s.strong : null
|
|
3050
4026
|
})
|
|
3051
4027
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3052
4028
|
Icon: TextItalic,
|
|
3053
|
-
title: "
|
|
3054
|
-
|
|
3055
|
-
|
|
4029
|
+
title: "Emphasis",
|
|
4030
|
+
disable: (s) => s.blockType === "codeblock",
|
|
4031
|
+
getState: (s) => s.emphasis,
|
|
4032
|
+
onClick: (s) => ({
|
|
4033
|
+
type: "emphasis",
|
|
4034
|
+
data: s ? !s.emphasis : null
|
|
3056
4035
|
})
|
|
3057
4036
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3058
4037
|
Icon: TextStrikethrough,
|
|
3059
4038
|
title: "Strike-through",
|
|
3060
|
-
|
|
3061
|
-
|
|
4039
|
+
disable: (s) => s.blockType === "codeblock",
|
|
4040
|
+
getState: (s) => s.strikethrough,
|
|
4041
|
+
onClick: (s) => ({
|
|
4042
|
+
type: "strikethrough",
|
|
4043
|
+
data: s ? !s.strikethrough : null
|
|
4044
|
+
})
|
|
4045
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
4046
|
+
Icon: Code,
|
|
4047
|
+
title: "Inline code",
|
|
4048
|
+
disable: (s) => s.blockType === "codeblock",
|
|
4049
|
+
getState: (s) => s.code,
|
|
4050
|
+
onClick: (s) => ({
|
|
4051
|
+
type: "code",
|
|
4052
|
+
data: s ? !s.code : null
|
|
3062
4053
|
})
|
|
3063
4054
|
}));
|
|
3064
4055
|
var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
|
|
@@ -3066,39 +4057,51 @@ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
|
|
|
3066
4057
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3067
4058
|
Icon: ListBullets,
|
|
3068
4059
|
title: "Bullet list",
|
|
3069
|
-
|
|
3070
|
-
|
|
4060
|
+
getState: (s) => s.listStyle === "bullet",
|
|
4061
|
+
onClick: (s) => ({
|
|
4062
|
+
type: "list-bullet",
|
|
4063
|
+
data: s ? s.listStyle !== "bullet" : null
|
|
3071
4064
|
})
|
|
3072
4065
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3073
4066
|
Icon: ListNumbers,
|
|
3074
4067
|
title: "Numbered list",
|
|
3075
|
-
|
|
3076
|
-
|
|
4068
|
+
getState: (s) => s.listStyle === "ordered",
|
|
4069
|
+
onClick: (s) => ({
|
|
4070
|
+
type: "list-ordered",
|
|
4071
|
+
data: s ? s.listStyle !== "ordered" : null
|
|
3077
4072
|
})
|
|
3078
4073
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3079
4074
|
Icon: ListChecks,
|
|
3080
4075
|
title: "Task list",
|
|
3081
|
-
|
|
3082
|
-
|
|
4076
|
+
getState: (s) => s.listStyle === "task",
|
|
4077
|
+
onClick: (s) => ({
|
|
4078
|
+
type: "list-tasks",
|
|
4079
|
+
data: s ? s.listStyle !== "task" : null
|
|
3083
4080
|
})
|
|
3084
4081
|
}));
|
|
3085
4082
|
var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3086
4083
|
role: "none"
|
|
3087
4084
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3088
4085
|
Icon: CaretRight,
|
|
3089
|
-
title: "Block
|
|
3090
|
-
|
|
3091
|
-
|
|
4086
|
+
title: "Block quote",
|
|
4087
|
+
getState: (s) => s.blockQuote,
|
|
4088
|
+
onClick: (s) => ({
|
|
4089
|
+
type: "blockquote",
|
|
4090
|
+
data: s ? !s.blockQuote : null
|
|
3092
4091
|
})
|
|
3093
4092
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3094
|
-
Icon:
|
|
4093
|
+
Icon: CodeBlock,
|
|
3095
4094
|
title: "Code block",
|
|
3096
|
-
|
|
3097
|
-
|
|
4095
|
+
disable: (s) => !s.blankLine,
|
|
4096
|
+
getState: (s) => s.blockType === "codeblock",
|
|
4097
|
+
onClick: (s) => ({
|
|
4098
|
+
type: "codeblock",
|
|
4099
|
+
data: s ? s.blockType !== "codeblock" : null
|
|
3098
4100
|
})
|
|
3099
4101
|
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3100
4102
|
Icon: Table2,
|
|
3101
4103
|
title: "Table",
|
|
4104
|
+
disable: (s) => !s.blankLine,
|
|
3102
4105
|
onClick: () => ({
|
|
3103
4106
|
type: "table"
|
|
3104
4107
|
})
|
|
@@ -3108,20 +4111,10 @@ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
|
|
|
3108
4111
|
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3109
4112
|
Icon: Link,
|
|
3110
4113
|
title: "Link",
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
Icon: At,
|
|
3116
|
-
title: "Mention",
|
|
3117
|
-
onClick: () => ({
|
|
3118
|
-
type: "mention"
|
|
3119
|
-
})
|
|
3120
|
-
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3121
|
-
Icon: Image,
|
|
3122
|
-
title: "Image",
|
|
3123
|
-
onClick: () => ({
|
|
3124
|
-
type: "image"
|
|
4114
|
+
getState: (s) => s.link,
|
|
4115
|
+
onClick: (s) => ({
|
|
4116
|
+
type: "link",
|
|
4117
|
+
data: s ? !s.link : null
|
|
3125
4118
|
})
|
|
3126
4119
|
}));
|
|
3127
4120
|
var MarkdownStandard = () => /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(MarkdownHeading, null), /* @__PURE__ */ React2.createElement(MarkdownStyles, null), /* @__PURE__ */ React2.createElement(MarkdownLists, null), /* @__PURE__ */ React2.createElement(MarkdownBlocks, null), /* @__PURE__ */ React2.createElement(MarkdownLinks, null));
|
|
@@ -3257,45 +4250,52 @@ var useActionHandler = (view) => {
|
|
|
3257
4250
|
if (!view) {
|
|
3258
4251
|
return;
|
|
3259
4252
|
}
|
|
4253
|
+
let inlineType, listType;
|
|
3260
4254
|
switch (action.type) {
|
|
3261
4255
|
case "heading":
|
|
3262
4256
|
setHeading(parseInt(action.data))(view);
|
|
3263
4257
|
break;
|
|
3264
|
-
case "
|
|
3265
|
-
|
|
3266
|
-
break;
|
|
3267
|
-
case "italic":
|
|
3268
|
-
toggleItalic(view);
|
|
3269
|
-
break;
|
|
4258
|
+
case "strong":
|
|
4259
|
+
case "emphasis":
|
|
3270
4260
|
case "strikethrough":
|
|
3271
|
-
|
|
4261
|
+
case "code":
|
|
4262
|
+
inlineType = action.type === "strong" ? Inline.Strong : action.type === "emphasis" ? Inline.Emphasis : action.type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
|
|
4263
|
+
(typeof action.data === "boolean" ? setStyle(inlineType, action.data) : toggleStyle(inlineType))(view);
|
|
3272
4264
|
break;
|
|
3273
|
-
case "list":
|
|
3274
|
-
|
|
4265
|
+
case "list-ordered":
|
|
4266
|
+
case "list-bullet":
|
|
4267
|
+
case "list-tasks":
|
|
4268
|
+
listType = action.type === "list-ordered" ? List.Ordered : action.type === "list-bullet" ? List.Bullet : List.Task;
|
|
4269
|
+
(action.data === false ? removeList(listType) : action.data === true ? addList(listType) : toggleList(listType))(view);
|
|
4270
|
+
break;
|
|
4271
|
+
case "blockquote":
|
|
4272
|
+
(action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
|
|
3275
4273
|
break;
|
|
3276
4274
|
case "codeblock":
|
|
3277
|
-
|
|
4275
|
+
(action.data === false ? removeCodeblock : addCodeblock)(view);
|
|
3278
4276
|
break;
|
|
3279
4277
|
case "table":
|
|
3280
4278
|
insertTable(view);
|
|
3281
4279
|
break;
|
|
4280
|
+
case "link":
|
|
4281
|
+
(action.data === false ? removeLink : addLink)(view);
|
|
4282
|
+
break;
|
|
3282
4283
|
case "comment":
|
|
3283
4284
|
createComment(view);
|
|
3284
4285
|
break;
|
|
3285
4286
|
}
|
|
3286
4287
|
setTimeout(() => {
|
|
3287
|
-
view.
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
});
|
|
4288
|
+
if (!view.hasFocus) {
|
|
4289
|
+
view.focus();
|
|
4290
|
+
}
|
|
3291
4291
|
});
|
|
3292
4292
|
};
|
|
3293
4293
|
};
|
|
3294
4294
|
|
|
3295
4295
|
// packages/ui/react-ui-editor/src/hooks/useEditorView.ts
|
|
3296
|
-
import { useState as
|
|
4296
|
+
import { useState as useState3 } from "react";
|
|
3297
4297
|
var useEditorView = () => {
|
|
3298
|
-
const [view, setView] =
|
|
4298
|
+
const [view, setView] = useState3(null);
|
|
3299
4299
|
return [
|
|
3300
4300
|
(ref) => {
|
|
3301
4301
|
setView(ref);
|
|
@@ -3305,15 +4305,15 @@ var useEditorView = () => {
|
|
|
3305
4305
|
};
|
|
3306
4306
|
|
|
3307
4307
|
// packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
|
|
3308
|
-
import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
|
|
3309
|
-
import { EditorView as
|
|
3310
|
-
import { useEffect as useEffect3, useRef as useRef2, useState as
|
|
4308
|
+
import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
|
|
4309
|
+
import { EditorView as EditorView15 } from "@codemirror/view";
|
|
4310
|
+
import { useEffect as useEffect3, useRef as useRef2, useState as useState4 } from "react";
|
|
3311
4311
|
import { log as log4 } from "@dxos/log";
|
|
3312
4312
|
import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
|
|
3313
4313
|
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
|
|
3314
4314
|
var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
|
|
3315
4315
|
const onUpdate = useRef2();
|
|
3316
|
-
const [view, setView] =
|
|
4316
|
+
const [view, setView] = useState4();
|
|
3317
4317
|
const parentRef = useRef2(null);
|
|
3318
4318
|
useEffect3(() => {
|
|
3319
4319
|
if (parentRef.current) {
|
|
@@ -3322,7 +4322,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
|
|
|
3322
4322
|
selection,
|
|
3323
4323
|
extensions: [
|
|
3324
4324
|
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
3325
|
-
|
|
4325
|
+
EditorView15.exceptionSink.of((err) => {
|
|
3326
4326
|
log4.catch(err, void 0, {
|
|
3327
4327
|
F: __dxlog_file5,
|
|
3328
4328
|
L: 57,
|
|
@@ -3331,12 +4331,12 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
|
|
|
3331
4331
|
});
|
|
3332
4332
|
}),
|
|
3333
4333
|
extensions,
|
|
3334
|
-
|
|
4334
|
+
EditorView15.updateListener.of(() => {
|
|
3335
4335
|
onUpdate.current?.();
|
|
3336
4336
|
})
|
|
3337
4337
|
].filter(isNotFalsy4)
|
|
3338
4338
|
});
|
|
3339
|
-
const view2 = new
|
|
4339
|
+
const view2 = new EditorView15({
|
|
3340
4340
|
parent: parentRef.current,
|
|
3341
4341
|
scrollTo,
|
|
3342
4342
|
state,
|
|
@@ -3360,7 +4360,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
|
|
|
3360
4360
|
useEffect3(() => {
|
|
3361
4361
|
if (view) {
|
|
3362
4362
|
if (!selection && !view.state.selection.main.anchor) {
|
|
3363
|
-
selection =
|
|
4363
|
+
selection = EditorSelection2.single(view.state.doc.line(1).to);
|
|
3364
4364
|
}
|
|
3365
4365
|
if (selection || scrollTo) {
|
|
3366
4366
|
onUpdate.current = () => {
|
|
@@ -3393,19 +4393,19 @@ var createDataExtensions = ({ readonly = false } = {}) => {
|
|
|
3393
4393
|
return [
|
|
3394
4394
|
//
|
|
3395
4395
|
EditorState3.readOnly.of(readonly),
|
|
3396
|
-
|
|
4396
|
+
EditorView15.editable.of(!readonly)
|
|
3397
4397
|
];
|
|
3398
4398
|
};
|
|
3399
4399
|
var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
|
|
3400
4400
|
return [
|
|
3401
4401
|
//
|
|
3402
|
-
|
|
3403
|
-
theme &&
|
|
3404
|
-
|
|
3405
|
-
|
|
4402
|
+
EditorView15.baseTheme(defaultTheme),
|
|
4403
|
+
theme && EditorView15.theme(theme),
|
|
4404
|
+
EditorView15.darkTheme.of(themeMode === "dark"),
|
|
4405
|
+
EditorView15.editorAttributes.of({
|
|
3406
4406
|
class: slots?.editor?.className ?? ""
|
|
3407
4407
|
}),
|
|
3408
|
-
|
|
4408
|
+
EditorView15.contentAttributes.of({
|
|
3409
4409
|
class: slots?.content?.className ?? ""
|
|
3410
4410
|
})
|
|
3411
4411
|
].filter(isNotFalsy4);
|
|
@@ -3413,7 +4413,7 @@ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
|
|
|
3413
4413
|
|
|
3414
4414
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
3415
4415
|
import get4 from "lodash.get";
|
|
3416
|
-
import { useEffect as useEffect4, useState as
|
|
4416
|
+
import { useEffect as useEffect4, useState as useState5 } from "react";
|
|
3417
4417
|
import { generateName } from "@dxos/display-name";
|
|
3418
4418
|
import { getRawDoc } from "@dxos/echo-schema";
|
|
3419
4419
|
import { invariant as invariant3 } from "@dxos/invariant";
|
|
@@ -3421,7 +4421,7 @@ import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
|
3421
4421
|
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
3422
4422
|
var useTextModel = (props) => {
|
|
3423
4423
|
const { identity, space, text } = props;
|
|
3424
|
-
const [model, setModel] =
|
|
4424
|
+
const [model, setModel] = useState5();
|
|
3425
4425
|
useEffect4(() => setModel(createModel(props)), [
|
|
3426
4426
|
identity,
|
|
3427
4427
|
space,
|
|
@@ -3430,7 +4430,7 @@ var useTextModel = (props) => {
|
|
|
3430
4430
|
return model;
|
|
3431
4431
|
};
|
|
3432
4432
|
var useInMemoryTextModel = ({ id, defaultContent }) => {
|
|
3433
|
-
const [content, setContent] =
|
|
4433
|
+
const [content, setContent] = useState5(defaultContent ?? "");
|
|
3434
4434
|
return {
|
|
3435
4435
|
id,
|
|
3436
4436
|
content,
|
|
@@ -3490,6 +4490,8 @@ export {
|
|
|
3490
4490
|
Cursor,
|
|
3491
4491
|
Doc,
|
|
3492
4492
|
EditorModes,
|
|
4493
|
+
Inline,
|
|
4494
|
+
List,
|
|
3493
4495
|
MarkdownEditor,
|
|
3494
4496
|
RemoteSelectionsDecorator,
|
|
3495
4497
|
SpaceAwarenessProvider,
|
|
@@ -3498,6 +4500,11 @@ export {
|
|
|
3498
4500
|
Toolbar,
|
|
3499
4501
|
YText,
|
|
3500
4502
|
YXmlFragment,
|
|
4503
|
+
addBlockquote,
|
|
4504
|
+
addCodeblock,
|
|
4505
|
+
addLink,
|
|
4506
|
+
addList,
|
|
4507
|
+
addStyle,
|
|
3501
4508
|
autocomplete,
|
|
3502
4509
|
automerge,
|
|
3503
4510
|
awareness,
|
|
@@ -3507,6 +4514,7 @@ export {
|
|
|
3507
4514
|
code2 as code,
|
|
3508
4515
|
codeTheme,
|
|
3509
4516
|
comments,
|
|
4517
|
+
compareFormatting,
|
|
3510
4518
|
createBasicBundle,
|
|
3511
4519
|
createComment,
|
|
3512
4520
|
createDataExtensions,
|
|
@@ -3519,11 +4527,11 @@ export {
|
|
|
3519
4527
|
defaultTheme,
|
|
3520
4528
|
focusComment,
|
|
3521
4529
|
formatting,
|
|
4530
|
+
getFormatting,
|
|
3522
4531
|
getToken,
|
|
3523
4532
|
heading2 as heading,
|
|
3524
4533
|
hr,
|
|
3525
4534
|
image,
|
|
3526
|
-
insertCodeblock,
|
|
3527
4535
|
insertTable,
|
|
3528
4536
|
keymap7 as keymap,
|
|
3529
4537
|
link,
|
|
@@ -3536,22 +4544,33 @@ export {
|
|
|
3536
4544
|
mention,
|
|
3537
4545
|
modelState,
|
|
3538
4546
|
outliner,
|
|
4547
|
+
removeBlockquote,
|
|
4548
|
+
removeCodeblock,
|
|
4549
|
+
removeLink,
|
|
4550
|
+
removeList,
|
|
4551
|
+
removeStyle,
|
|
4552
|
+
setBlockquote,
|
|
3539
4553
|
setComments,
|
|
3540
4554
|
setHeading,
|
|
3541
4555
|
setSelection,
|
|
4556
|
+
setStyle,
|
|
3542
4557
|
table,
|
|
3543
4558
|
tags2 as tags,
|
|
3544
4559
|
tasklist,
|
|
3545
4560
|
textTheme,
|
|
3546
|
-
|
|
3547
|
-
|
|
4561
|
+
toggleBlockquote,
|
|
4562
|
+
toggleCodeblock,
|
|
4563
|
+
toggleEmphasis,
|
|
4564
|
+
toggleInlineCode,
|
|
3548
4565
|
toggleList,
|
|
3549
4566
|
toggleStrikethrough,
|
|
4567
|
+
toggleStrong,
|
|
3550
4568
|
toggleStyle,
|
|
3551
4569
|
typewriter,
|
|
3552
4570
|
useActionHandler,
|
|
3553
4571
|
useComments,
|
|
3554
4572
|
useEditorView,
|
|
4573
|
+
useFormattingState,
|
|
3555
4574
|
useInMemoryTextModel,
|
|
3556
4575
|
useTextEditor,
|
|
3557
4576
|
useTextModel,
|