@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.
@@ -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 EditorView13 } from "@codemirror/view";
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?.(cursor, view.coordsAtPos(from));
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
- var setHeading = (level) => (view) => {
1821
- const { selection: { ranges }, doc } = view.state;
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
- const { number } = doc.lineAt(range.anchor);
1825
- const { from, to } = doc.line(number);
1826
- const line = doc.sliceString(from, to);
1827
- const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1828
- const current = marks?.length ?? 0;
1829
- if (level !== current) {
1830
- changes.push({
1831
- from,
1832
- to: from + current + (spaces?.length ?? 0),
1833
- insert: "#".repeat(level) + (level > 0 ? " " : "")
1834
- });
1835
- }
1836
- }
1837
- if (changes.length) {
1838
- view.dispatch({
1839
- changes
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 toggleStyle = (mark2) => (view) => {
1845
- const { ranges } = view.state.selection;
1846
- for (const range of ranges) {
1847
- if (range.from === range.to) {
1848
- return false;
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
- view.dispatch({
1851
- changes: [
1852
- {
1853
- from: range.from,
1854
- insert: mark2
1855
- },
1856
- {
1857
- from: range.to,
1858
- insert: mark2
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 toggleBold = toggleStyle("**");
1891
- var toggleItalic = toggleStyle("_");
1892
- var toggleStrikethrough = toggleStyle("~~");
1893
- var toggleList = (view) => {
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: toggleBold
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 EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
2007
- var styles4 = EditorView9.baseTheme({
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 EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
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) => EditorView10.decorations.from(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 EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
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) => EditorView11.decorations.from(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 EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2369
- var styles5 = EditorView12.baseTheme({
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.colors.neutral.900", "black"),
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.colors.neutral.100", "white"),
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] = useState(null);
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
- EditorView13.exceptionSink.of((err) => {
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
- EditorView13.baseTheme(defaultTheme),
2842
- EditorView13.theme(theme ?? {}),
2843
- EditorView13.darkTheme.of(themeMode === "dark"),
2844
- EditorView13.editorAttributes.of({
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
- EditorView13.contentAttributes.of({
3795
+ EditorView14.contentAttributes.of({
2848
3796
  class: slots.content?.className ?? ""
2849
3797
  }),
2850
3798
  // State.
2851
- EditorView13.editable.of(!readonly),
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 EditorView13({
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
- onClick: handleClick,
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
- defaultValue: "0",
3009
- onValueChange: (value) => onAction?.({
3964
+ disabled: value === null,
3965
+ value: value ?? "0",
3966
+ onValueChange: (value2) => onAction?.({
3010
3967
  type: "heading",
3011
- data: parseInt(value)
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: "Bold",
3034
- onClick: () => ({
3035
- type: "bold"
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: "Italic",
3040
- onClick: () => ({
3041
- type: "italic"
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 quite",
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 "bold":
3251
- toggleBold(view);
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 "italic":
3254
- toggleItalic(view);
4245
+ case "link":
4246
+ (action.data === false ? removeLink : addLink)(view);
3255
4247
  break;
3256
- case "strikethrough":
3257
- toggleStrikethrough(view);
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 "list":
3260
- toggleList(view);
4254
+ case "blockquote":
4255
+ (action.data === false ? removeBlockquote : action.data === true ? addBlockquote : toggleBlockquote)(view);
3261
4256
  break;
3262
4257
  case "codeblock":
3263
- insertCodeblock(view);
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.focus();
3274
- view.dispatch({
3275
- selection: view.state.selection
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 useState2 } from "react";
4276
+ import { useState as useState3 } from "react";
3283
4277
  var useEditorView = () => {
3284
- const [view, setView] = useState2(null);
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 EditorView14 } from "@codemirror/view";
3296
- import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
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] = useState3();
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
- EditorView14.exceptionSink.of((err) => {
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
- EditorView14.updateListener.of(() => {
4314
+ EditorView15.updateListener.of(() => {
3321
4315
  onUpdate.current?.();
3322
4316
  })
3323
4317
  ].filter(isNotFalsy4)
3324
4318
  });
3325
- const view2 = new EditorView14({
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 = EditorSelection.single(view.state.doc.line(1).to);
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
- EditorView14.editable.of(!readonly)
4376
+ EditorView15.editable.of(!readonly)
3383
4377
  ];
3384
4378
  };
3385
4379
  var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3386
4380
  return [
3387
4381
  //
3388
- EditorView14.baseTheme(defaultTheme),
3389
- theme && EditorView14.theme(theme),
3390
- EditorView14.darkTheme.of(themeMode === "dark"),
3391
- EditorView14.editorAttributes.of({
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
- EditorView14.contentAttributes.of({
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 useState4 } from "react";
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] = useState4();
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] = useState4(defaultContent ?? "");
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
- toggleBold,
3533
- toggleItalic,
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,