@dxos/react-ui-editor 0.3.11-next.e28df4f → 0.3.11-next.f4c1077

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/lib/browser/index.mjs +1218 -171
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  5. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
  7. package/dist/types/src/components/Toolbar/Toolbar.d.ts +8 -4
  8. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
  9. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +5 -3
  10. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
  11. package/dist/types/src/extensions/comments.d.ts +5 -1
  12. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  13. package/dist/types/src/extensions/markdown/formatting.d.ts +55 -10
  14. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  15. package/dist/types/src/extensions/markdown/formatting.test.d.ts +3 -0
  16. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -0
  17. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
  18. package/dist/types/src/hooks/useTextModel.d.ts +12 -0
  19. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  20. package/dist/types/src/index.d.ts +1 -1
  21. package/dist/types/src/index.d.ts.map +1 -1
  22. package/dist/types/src/themes/default.d.ts.map +1 -1
  23. package/package.json +25 -25
  24. package/src/components/TextEditor/MarkdownEditor.stories.tsx +3 -3
  25. package/src/components/TextEditor/TextEditor.tsx +3 -4
  26. package/src/components/TextEditor/hooks.stories.tsx +6 -1
  27. package/src/components/Toolbar/Toolbar.stories.tsx +16 -4
  28. package/src/components/Toolbar/Toolbar.tsx +128 -30
  29. package/src/extensions/awareness.ts +4 -1
  30. package/src/extensions/blast.ts +4 -1
  31. package/src/extensions/comments.ts +14 -4
  32. package/src/extensions/markdown/formatting.test.ts +482 -0
  33. package/src/extensions/markdown/formatting.ts +1118 -69
  34. package/src/extensions/markdown/link.ts +8 -2
  35. package/src/hooks/useActionHandler.ts +47 -16
  36. package/src/hooks/useTextModel.ts +17 -1
  37. package/src/index.ts +1 -1
  38. package/src/themes/default.ts +7 -4
  39. package/dist/types/src/components/TextEditor/comments.stories.d.ts +0 -26
  40. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +0 -1
  41. package/src/components/TextEditor/comments.stories.tsx +0 -357
@@ -1,19 +1,20 @@
1
1
  // packages/ui/react-ui-editor/src/index.ts
2
2
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
3
3
  import { Doc, YText, YXmlFragment } from "@dxos/text-model";
4
+ import { keymap as keymap7 } from "@codemirror/view";
4
5
 
5
6
  // packages/ui/react-ui-editor/src/components/TextEditor/index.ts
6
7
  import { tags as tags2 } from "@lezer/highlight";
7
8
 
8
9
  // packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
9
10
  import { EditorState as EditorState2 } from "@codemirror/state";
10
- import { EditorView as EditorView13 } from "@codemirror/view";
11
+ import { EditorView as EditorView14 } from "@codemirror/view";
11
12
  import { vim } from "@replit/codemirror-vim";
12
13
  import defaultsDeep2 from "lodash.defaultsdeep";
13
- 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";
14
15
  import { log as log2 } from "@dxos/log";
15
16
  import { useThemeContext } from "@dxos/react-ui";
16
- import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
17
+ import { focusRing, mx as mx3 } from "@dxos/react-ui-theme";
17
18
  import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
18
19
 
19
20
  // packages/ui/react-ui-editor/src/extensions/autocomplete.ts
@@ -782,7 +783,7 @@ var Blaster = class {
782
783
  initialize() {
783
784
  invariant(!this._canvas && !this._ctx, void 0, {
784
785
  F: __dxlog_file,
785
- L: 138,
786
+ L: 141,
786
787
  S: this,
787
788
  A: [
788
789
  "!this._canvas && !this._ctx",
@@ -819,7 +820,7 @@ var Blaster = class {
819
820
  start() {
820
821
  invariant(this._canvas && this._ctx, void 0, {
821
822
  F: __dxlog_file,
822
- L: 177,
823
+ L: 180,
823
824
  S: this,
824
825
  A: [
825
826
  "this._canvas && this._ctx",
@@ -1073,11 +1074,25 @@ var logChanges = (trs) => {
1073
1074
 
1074
1075
  // packages/ui/react-ui-editor/src/extensions/comments.ts
1075
1076
  var styles2 = EditorView5.baseTheme({
1076
- "& .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": {
1077
1084
  backgroundColor: getToken("extend.colors.yellow.50")
1078
1085
  },
1079
- "& .cm-comment-current": {
1086
+ "&light .cm-comment-current": {
1080
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")
1081
1096
  }
1082
1097
  });
1083
1098
  var commentMark = Decoration2.mark({
@@ -1286,7 +1301,11 @@ var createComment = (view) => {
1286
1301
  to
1287
1302
  });
1288
1303
  if (cursor) {
1289
- const id = options.onCreate?.(cursor, view.coordsAtPos(from));
1304
+ const id = options.onCreate?.({
1305
+ cursor,
1306
+ from,
1307
+ location: view.coordsAtPos(from)
1308
+ });
1290
1309
  if (id) {
1291
1310
  view.dispatch({
1292
1311
  effects: setSelection.of({
@@ -1814,89 +1833,836 @@ var code2 = () => {
1814
1833
  // packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
1815
1834
  import { snippet } from "@codemirror/autocomplete";
1816
1835
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
1817
- import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
1818
- import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
1819
- var setHeading = (level) => (view) => {
1820
- 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;
1821
1855
  const changes = [];
1856
+ let prevBlock = -1;
1822
1857
  for (const range of ranges) {
1823
- const { number } = doc.lineAt(range.anchor);
1824
- const { from, to } = doc.line(number);
1825
- const line = doc.sliceString(from, to);
1826
- const [_, marks, spaces] = line.match(/(#+)(\s+)/) ?? [];
1827
- const current = marks?.length ?? 0;
1828
- if (level !== current) {
1829
- changes.push({
1830
- from,
1831
- to: from + current + (spaces?.length ?? 0),
1832
- insert: "#".repeat(level) + (level > 0 ? " " : "")
1833
- });
1834
- }
1835
- }
1836
- if (changes.length) {
1837
- view.dispatch({
1838
- 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
+ }
1839
1908
  });
1840
1909
  }
1910
+ if (!changes.length) {
1911
+ return false;
1912
+ }
1913
+ dispatch(state.update({
1914
+ changes,
1915
+ userEvent: "format.setHeading",
1916
+ scrollIntoView: true
1917
+ }));
1841
1918
  return true;
1842
1919
  };
1843
- var toggleStyle = (mark2) => (view) => {
1844
- const { ranges } = view.state.selection;
1845
- for (const range of ranges) {
1846
- if (range.from === range.to) {
1847
- 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
+ }
1848
1946
  }
1849
- view.dispatch({
1850
- changes: [
1851
- {
1852
- from: range.from,
1853
- insert: mark2
1854
- },
1855
- {
1856
- from: range.to,
1857
- 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
+ }
1858
2018
  }
1859
- ]
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
+ }
1860
2063
  });
1861
- }
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
+ }));
1862
2074
  return true;
1863
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
+ };
1864
2109
  var snippets = {
1865
2110
  codeblock: snippet([
1866
- "```#{lang}",
1867
- " #{}",
2111
+ //
2112
+ "```#{}",
2113
+ "#{}",
1868
2114
  "```"
1869
2115
  ].join("\n")),
1870
2116
  table: snippet([
2117
+ //
1871
2118
  "| #{col1} | #{col2} |",
1872
2119
  "| ---- | ---- |",
1873
2120
  "| #{val1} | #{val2} |",
1874
- "| #{val3} | #{val4} |"
2121
+ "| #{val3} | #{val4} |",
2122
+ ""
1875
2123
  ].join("\n"))
1876
2124
  };
1877
- var insertCodeblock = (view) => {
1878
- const { selection: { main }, doc } = view.state;
1879
- const { number } = doc.lineAt(main.anchor);
1880
- const { from } = doc.line(number);
1881
- snippets.codeblock(view, null, from, from);
1882
- };
1883
2125
  var insertTable = (view) => {
1884
2126
  const { selection: { main }, doc } = view.state;
1885
2127
  const { number } = doc.lineAt(main.anchor);
1886
2128
  const { from } = doc.line(number);
1887
2129
  snippets.table(view, null, from, from);
1888
2130
  };
1889
- var toggleBold = toggleStyle("**");
1890
- var toggleItalic = toggleStyle("_");
1891
- var toggleStrikethrough = toggleStyle("~~");
1892
- var toggleList = (view) => {
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);
1893
2659
  };
1894
2660
  var formatting = (options = {}) => {
1895
2661
  return [
1896
2662
  keymap5.of([
1897
2663
  {
1898
2664
  key: "meta-b",
1899
- run: toggleBold
2665
+ run: toggleStrong
1900
2666
  }
1901
2667
  ]),
1902
2668
  styling()
@@ -1950,6 +2716,209 @@ var styling = () => {
1950
2716
  })
1951
2717
  ];
1952
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
+ };
1953
2922
 
1954
2923
  // packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
1955
2924
  import { syntaxTree as syntaxTree3 } from "@codemirror/language";
@@ -2002,8 +2971,8 @@ var heading2 = () => {
2002
2971
  // packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
2003
2972
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
2004
2973
  import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
2005
- import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
2006
- var styles4 = EditorView9.baseTheme({
2974
+ import { Decoration as Decoration6, EditorView as EditorView10, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
2975
+ var styles4 = EditorView10.baseTheme({
2007
2976
  "& .cm-hr": {
2008
2977
  // Note that block-level decorations should not have vertical margins,
2009
2978
  borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
@@ -2057,7 +3026,7 @@ var hr = () => {
2057
3026
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
2058
3027
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
2059
3028
  import { StateField as StateField3 } from "@codemirror/state";
2060
- import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
3029
+ import { Decoration as Decoration7, EditorView as EditorView11, WidgetType as WidgetType4 } from "@codemirror/view";
2061
3030
  var image = (options = {}) => {
2062
3031
  return StateField3.define({
2063
3032
  create: (state) => {
@@ -2084,7 +3053,7 @@ var image = (options = {}) => {
2084
3053
  add: buildDecorations4(from, to, tr.state)
2085
3054
  });
2086
3055
  },
2087
- provide: (field) => EditorView10.decorations.from(field)
3056
+ provide: (field) => EditorView11.decorations.from(field)
2088
3057
  });
2089
3058
  };
2090
3059
  var buildDecorations4 = (from, to, state) => {
@@ -2266,12 +3235,12 @@ var buildDecorations5 = (view, options) => {
2266
3235
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
2267
3236
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
2268
3237
  import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
2269
- import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
3238
+ import { Decoration as Decoration9, EditorView as EditorView12, WidgetType as WidgetType6 } from "@codemirror/view";
2270
3239
  var table = (options = {}) => {
2271
3240
  return StateField4.define({
2272
3241
  create: (state) => update(state, options),
2273
3242
  update: (_, tr) => update(tr.state, options),
2274
- provide: (field) => EditorView11.decorations.from(field)
3243
+ provide: (field) => EditorView12.decorations.from(field)
2275
3244
  });
2276
3245
  };
2277
3246
  var update = (state, options) => {
@@ -2364,8 +3333,8 @@ var TableWidget = class extends WidgetType6 {
2364
3333
  // packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
2365
3334
  import { syntaxTree as syntaxTree8 } from "@codemirror/language";
2366
3335
  import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
2367
- import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
2368
- var styles5 = EditorView12.baseTheme({
3336
+ import { EditorView as EditorView13, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
3337
+ var styles5 = EditorView13.baseTheme({
2369
3338
  "& .cm-task": {
2370
3339
  color: getToken("extend.colors.blue.500")
2371
3340
  },
@@ -2590,11 +3559,11 @@ var defaultTheme = {
2590
3559
  fontSize: "16px"
2591
3560
  },
2592
3561
  "&light .cm-content": {
2593
- color: get3(tokens, "extend.colors.neutral.900", "black"),
3562
+ color: get3(tokens, "extend.semanticColors.base.fg.light", "black"),
2594
3563
  caretColor: "black"
2595
3564
  },
2596
3565
  "&dark .cm-content": {
2597
- color: get3(tokens, "extend.colors.neutral.100", "white"),
3566
+ color: get3(tokens, "extend.semanticColors.base.fg.dark", "white"),
2598
3567
  caretColor: "white"
2599
3568
  },
2600
3569
  //
@@ -2606,8 +3575,11 @@ var defaultTheme = {
2606
3575
  "&dark .cm-cursor, &dark .cm-dropCursor": {
2607
3576
  borderLeft: "2px solid white"
2608
3577
  },
2609
- ".cm-placeholder": {
2610
- fontWeight: 100
3578
+ "&light .cm-placeholder": {
3579
+ color: get3(tokens, "extend.semanticColors.description.light", "rgba(0,0,0,.2)")
3580
+ },
3581
+ "&dark .cm-placeholder": {
3582
+ color: get3(tokens, "extend.semanticColors.description.dark", "rgba(255,255,255,.2)")
2611
3583
  },
2612
3584
  //
2613
3585
  // line
@@ -2795,7 +3767,7 @@ var EditorModes = [
2795
3767
  var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
2796
3768
  const { themeMode } = useThemeContext();
2797
3769
  const rootRef = useRef(null);
2798
- const [view, setView] = useState(null);
3770
+ const [view, setView] = useState2(null);
2799
3771
  useImperativeHandle(forwardedRef, () => view, [
2800
3772
  view
2801
3773
  ]);
@@ -2824,7 +3796,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2824
3796
  selection,
2825
3797
  extensions: [
2826
3798
  // TODO(burdon): Doesn't catch errors in keymap functions.
2827
- EditorView13.exceptionSink.of((err) => {
3799
+ EditorView14.exceptionSink.of((err) => {
2828
3800
  log2.catch(err, void 0, {
2829
3801
  F: __dxlog_file3,
2830
3802
  L: 134,
@@ -2834,17 +3806,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2834
3806
  }),
2835
3807
  // Theme.
2836
3808
  // TODO(burdon): Make configurable.
2837
- EditorView13.baseTheme(defaultTheme),
2838
- EditorView13.theme(theme ?? {}),
2839
- EditorView13.darkTheme.of(themeMode === "dark"),
2840
- EditorView13.editorAttributes.of({
3809
+ EditorView14.baseTheme(defaultTheme),
3810
+ EditorView14.theme(theme ?? {}),
3811
+ EditorView14.darkTheme.of(themeMode === "dark"),
3812
+ EditorView14.editorAttributes.of({
2841
3813
  class: slots.editor?.className ?? ""
2842
3814
  }),
2843
- EditorView13.contentAttributes.of({
3815
+ EditorView14.contentAttributes.of({
2844
3816
  class: slots.content?.className ?? ""
2845
3817
  }),
2846
3818
  // State.
2847
- EditorView13.editable.of(!readonly),
3819
+ EditorView14.editable.of(!readonly),
2848
3820
  EditorState2.readOnly.of(!!readonly),
2849
3821
  // Storage and replication.
2850
3822
  // NOTE: This must come before user extensions.
@@ -2855,7 +3827,7 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, s
2855
3827
  ...extensions
2856
3828
  ].filter(isNotFalsy3)
2857
3829
  });
2858
- const newView = new EditorView13({
3830
+ const newView = new EditorView14({
2859
3831
  state,
2860
3832
  parent: rootRef.current,
2861
3833
  scrollTo,
@@ -2948,11 +3920,10 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
2948
3920
  });
2949
3921
  var defaultSlots = {
2950
3922
  root: {
2951
- // TODO(burdon): Add focusRing by default/as property?
2952
- className: mx3("flex flex-col grow overflow-y-auto", attentionSurface)
3923
+ className: mx3("grow", focusRing)
2953
3924
  },
2954
3925
  editor: {
2955
- className: "h-full p-2"
3926
+ className: "bs-full"
2956
3927
  }
2957
3928
  };
2958
3929
  var defaultTextSlots = {
@@ -2963,35 +3934,49 @@ var defaultMarkdownSlots = {
2963
3934
  };
2964
3935
 
2965
3936
  // packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
2966
- import { At, CaretRight, ChatText, Code, Image, Link, ListBullets, ListChecks, ListNumbers, TextStrikethrough, Table as Table2, TextB, TextItalic } from "@phosphor-icons/react";
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";
2967
3938
  import { createContext } from "@radix-ui/react-context";
2968
3939
  import React2 from "react";
2969
3940
  import { Button, DensityProvider, Select } from "@dxos/react-ui";
2970
- 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
+ };
2971
3951
  var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
2972
- var ToolbarRoot = ({ children, onAction }) => {
3952
+ var ToolbarRoot = ({ children, onAction, state }) => {
2973
3953
  return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
2974
- onAction
3954
+ onAction,
3955
+ state
2975
3956
  }, /* @__PURE__ */ React2.createElement(DensityProvider, {
2976
3957
  density: "fine"
2977
3958
  }, /* @__PURE__ */ React2.createElement("div", {
2978
3959
  role: "toolbar",
2979
- className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
3960
+ className: "flex w-full shrink-0 p-1 gap-4 items-center whitespace-nowrap overflow-hidden"
2980
3961
  }, children)));
2981
3962
  };
2982
- var ToolbarButton = ({ Icon, onClick, title }) => {
2983
- const { onAction } = useToolbarContext("ToolbarButton");
2984
- const handleClick = () => {
2985
- const action = onClick();
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);
2986
3969
  if (action) {
2987
3970
  onAction?.(action);
3971
+ event.preventDefault();
2988
3972
  }
2989
3973
  };
2990
3974
  return /* @__PURE__ */ React2.createElement(Button, {
2991
3975
  variant: "ghost",
2992
- classNames: "p-2",
2993
- onClick: handleClick,
2994
- title
3976
+ classNames: mx4("p-2", active && "ring-[1px]"),
3977
+ onMouseDown: handleClick,
3978
+ title,
3979
+ disabled
2995
3980
  }, /* @__PURE__ */ React2.createElement(Icon, {
2996
3981
  className: getSize(5)
2997
3982
  }));
@@ -3000,16 +3985,21 @@ var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
3000
3985
  className: "grow"
3001
3986
  });
3002
3987
  var MarkdownHeading = () => {
3003
- 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"];
3004
3993
  return /* @__PURE__ */ React2.createElement(Select.Root, {
3005
- defaultValue: "0",
3006
- onValueChange: (value) => onAction?.({
3994
+ disabled: value === null,
3995
+ value: value ?? "0",
3996
+ onValueChange: (value2) => onAction?.({
3007
3997
  type: "heading",
3008
- data: parseInt(value)
3998
+ data: parseInt(value2)
3009
3999
  })
3010
- }, /* @__PURE__ */ React2.createElement(Select.TriggerButton, {
3011
- classNames: "w-[8rem]"
3012
- }), /* @__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, {
3013
4003
  value: "0"
3014
4004
  }, "Paragraph"), [
3015
4005
  1,
@@ -3027,21 +4017,39 @@ var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
3027
4017
  role: "none"
3028
4018
  }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3029
4019
  Icon: TextB,
3030
- title: "Bold",
3031
- onClick: () => ({
3032
- type: "bold"
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
3033
4026
  })
3034
4027
  }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3035
4028
  Icon: TextItalic,
3036
- title: "Italic",
3037
- onClick: () => ({
3038
- type: "italic"
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
3039
4035
  })
3040
4036
  }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3041
4037
  Icon: TextStrikethrough,
3042
4038
  title: "Strike-through",
3043
- onClick: () => ({
3044
- type: "strikethrough"
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
3045
4053
  })
3046
4054
  }));
3047
4055
  var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
@@ -3049,39 +4057,51 @@ var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
3049
4057
  }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3050
4058
  Icon: ListBullets,
3051
4059
  title: "Bullet list",
3052
- onClick: () => ({
3053
- type: "list"
4060
+ getState: (s) => s.listStyle === "bullet",
4061
+ onClick: (s) => ({
4062
+ type: "list-bullet",
4063
+ data: s ? s.listStyle !== "bullet" : null
3054
4064
  })
3055
4065
  }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3056
4066
  Icon: ListNumbers,
3057
4067
  title: "Numbered list",
3058
- onClick: () => ({
3059
- type: "list-ordered"
4068
+ getState: (s) => s.listStyle === "ordered",
4069
+ onClick: (s) => ({
4070
+ type: "list-ordered",
4071
+ data: s ? s.listStyle !== "ordered" : null
3060
4072
  })
3061
4073
  }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3062
4074
  Icon: ListChecks,
3063
4075
  title: "Task list",
3064
- onClick: () => ({
3065
- type: "list-tasks"
4076
+ getState: (s) => s.listStyle === "task",
4077
+ onClick: (s) => ({
4078
+ type: "list-tasks",
4079
+ data: s ? s.listStyle !== "task" : null
3066
4080
  })
3067
4081
  }));
3068
4082
  var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
3069
4083
  role: "none"
3070
4084
  }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3071
4085
  Icon: CaretRight,
3072
- title: "Block quite",
3073
- onClick: () => ({
3074
- type: "blockquote"
4086
+ title: "Block quote",
4087
+ getState: (s) => s.blockQuote,
4088
+ onClick: (s) => ({
4089
+ type: "blockquote",
4090
+ data: s ? !s.blockQuote : null
3075
4091
  })
3076
4092
  }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3077
- Icon: Code,
4093
+ Icon: CodeBlock,
3078
4094
  title: "Code block",
3079
- onClick: () => ({
3080
- type: "codeblock"
4095
+ disable: (s) => !s.blankLine,
4096
+ getState: (s) => s.blockType === "codeblock",
4097
+ onClick: (s) => ({
4098
+ type: "codeblock",
4099
+ data: s ? s.blockType !== "codeblock" : null
3081
4100
  })
3082
4101
  }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3083
4102
  Icon: Table2,
3084
4103
  title: "Table",
4104
+ disable: (s) => !s.blankLine,
3085
4105
  onClick: () => ({
3086
4106
  type: "table"
3087
4107
  })
@@ -3091,20 +4111,10 @@ var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
3091
4111
  }, /* @__PURE__ */ React2.createElement(ToolbarButton, {
3092
4112
  Icon: Link,
3093
4113
  title: "Link",
3094
- onClick: () => ({
3095
- type: "link"
3096
- })
3097
- }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3098
- Icon: At,
3099
- title: "Mention",
3100
- onClick: () => ({
3101
- type: "mention"
3102
- })
3103
- }), /* @__PURE__ */ React2.createElement(ToolbarButton, {
3104
- Icon: Image,
3105
- title: "Image",
3106
- onClick: () => ({
3107
- type: "image"
4114
+ getState: (s) => s.link,
4115
+ onClick: (s) => ({
4116
+ type: "link",
4117
+ data: s ? !s.link : null
3108
4118
  })
3109
4119
  }));
3110
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));
@@ -3240,45 +4250,52 @@ var useActionHandler = (view) => {
3240
4250
  if (!view) {
3241
4251
  return;
3242
4252
  }
4253
+ let inlineType, listType;
3243
4254
  switch (action.type) {
3244
4255
  case "heading":
3245
4256
  setHeading(parseInt(action.data))(view);
3246
4257
  break;
3247
- case "bold":
3248
- toggleBold(view);
3249
- break;
3250
- case "italic":
3251
- toggleItalic(view);
3252
- break;
4258
+ case "strong":
4259
+ case "emphasis":
3253
4260
  case "strikethrough":
3254
- toggleStrikethrough(view);
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);
3255
4264
  break;
3256
- case "list":
3257
- toggleList(view);
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);
3258
4273
  break;
3259
4274
  case "codeblock":
3260
- insertCodeblock(view);
4275
+ (action.data === false ? removeCodeblock : addCodeblock)(view);
3261
4276
  break;
3262
4277
  case "table":
3263
4278
  insertTable(view);
3264
4279
  break;
4280
+ case "link":
4281
+ (action.data === false ? removeLink : addLink)(view);
4282
+ break;
3265
4283
  case "comment":
3266
4284
  createComment(view);
3267
4285
  break;
3268
4286
  }
3269
4287
  setTimeout(() => {
3270
- view.focus();
3271
- view.dispatch({
3272
- selection: view.state.selection
3273
- });
4288
+ if (!view.hasFocus) {
4289
+ view.focus();
4290
+ }
3274
4291
  });
3275
4292
  };
3276
4293
  };
3277
4294
 
3278
4295
  // packages/ui/react-ui-editor/src/hooks/useEditorView.ts
3279
- import { useState as useState2 } from "react";
4296
+ import { useState as useState3 } from "react";
3280
4297
  var useEditorView = () => {
3281
- const [view, setView] = useState2(null);
4298
+ const [view, setView] = useState3(null);
3282
4299
  return [
3283
4300
  (ref) => {
3284
4301
  setView(ref);
@@ -3288,15 +4305,15 @@ var useEditorView = () => {
3288
4305
  };
3289
4306
 
3290
4307
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
3291
- import { EditorSelection, EditorState as EditorState3 } from "@codemirror/state";
3292
- import { EditorView as EditorView14 } from "@codemirror/view";
3293
- import { useEffect as useEffect3, useRef as useRef2, useState as useState3 } from "react";
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";
3294
4311
  import { log as log4 } from "@dxos/log";
3295
4312
  import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
3296
4313
  var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
3297
4314
  var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
3298
4315
  const onUpdate = useRef2();
3299
- const [view, setView] = useState3();
4316
+ const [view, setView] = useState4();
3300
4317
  const parentRef = useRef2(null);
3301
4318
  useEffect3(() => {
3302
4319
  if (parentRef.current) {
@@ -3305,7 +4322,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
3305
4322
  selection,
3306
4323
  extensions: [
3307
4324
  // TODO(burdon): Doesn't catch errors in keymap functions.
3308
- EditorView14.exceptionSink.of((err) => {
4325
+ EditorView15.exceptionSink.of((err) => {
3309
4326
  log4.catch(err, void 0, {
3310
4327
  F: __dxlog_file5,
3311
4328
  L: 57,
@@ -3314,12 +4331,12 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
3314
4331
  });
3315
4332
  }),
3316
4333
  extensions,
3317
- EditorView14.updateListener.of(() => {
4334
+ EditorView15.updateListener.of(() => {
3318
4335
  onUpdate.current?.();
3319
4336
  })
3320
4337
  ].filter(isNotFalsy4)
3321
4338
  });
3322
- const view2 = new EditorView14({
4339
+ const view2 = new EditorView15({
3323
4340
  parent: parentRef.current,
3324
4341
  scrollTo,
3325
4342
  state,
@@ -3343,7 +4360,7 @@ var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions }
3343
4360
  useEffect3(() => {
3344
4361
  if (view) {
3345
4362
  if (!selection && !view.state.selection.main.anchor) {
3346
- selection = EditorSelection.single(view.state.doc.line(1).to);
4363
+ selection = EditorSelection2.single(view.state.doc.line(1).to);
3347
4364
  }
3348
4365
  if (selection || scrollTo) {
3349
4366
  onUpdate.current = () => {
@@ -3376,19 +4393,19 @@ var createDataExtensions = ({ readonly = false } = {}) => {
3376
4393
  return [
3377
4394
  //
3378
4395
  EditorState3.readOnly.of(readonly),
3379
- EditorView14.editable.of(!readonly)
4396
+ EditorView15.editable.of(!readonly)
3380
4397
  ];
3381
4398
  };
3382
4399
  var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3383
4400
  return [
3384
4401
  //
3385
- EditorView14.baseTheme(defaultTheme),
3386
- theme && EditorView14.theme(theme),
3387
- EditorView14.darkTheme.of(themeMode === "dark"),
3388
- EditorView14.editorAttributes.of({
4402
+ EditorView15.baseTheme(defaultTheme),
4403
+ theme && EditorView15.theme(theme),
4404
+ EditorView15.darkTheme.of(themeMode === "dark"),
4405
+ EditorView15.editorAttributes.of({
3389
4406
  class: slots?.editor?.className ?? ""
3390
4407
  }),
3391
- EditorView14.contentAttributes.of({
4408
+ EditorView15.contentAttributes.of({
3392
4409
  class: slots?.content?.className ?? ""
3393
4410
  })
3394
4411
  ].filter(isNotFalsy4);
@@ -3396,7 +4413,7 @@ var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
3396
4413
 
3397
4414
  // packages/ui/react-ui-editor/src/hooks/useTextModel.ts
3398
4415
  import get4 from "lodash.get";
3399
- import { useEffect as useEffect4, useState as useState4 } from "react";
4416
+ import { useEffect as useEffect4, useState as useState5 } from "react";
3400
4417
  import { generateName } from "@dxos/display-name";
3401
4418
  import { getRawDoc } from "@dxos/echo-schema";
3402
4419
  import { invariant as invariant3 } from "@dxos/invariant";
@@ -3404,7 +4421,7 @@ import { isAutomergeObject } from "@dxos/react-client/echo";
3404
4421
  var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
3405
4422
  var useTextModel = (props) => {
3406
4423
  const { identity, space, text } = props;
3407
- const [model, setModel] = useState4();
4424
+ const [model, setModel] = useState5();
3408
4425
  useEffect4(() => setModel(createModel(props)), [
3409
4426
  identity,
3410
4427
  space,
@@ -3412,10 +4429,19 @@ var useTextModel = (props) => {
3412
4429
  ]);
3413
4430
  return model;
3414
4431
  };
4432
+ var useInMemoryTextModel = ({ id, defaultContent }) => {
4433
+ const [content, setContent] = useState5(defaultContent ?? "");
4434
+ return {
4435
+ id,
4436
+ content,
4437
+ setContent,
4438
+ text: () => content
4439
+ };
4440
+ };
3415
4441
  var createModel = ({ space, identity, text }) => {
3416
4442
  invariant3(isAutomergeObject(text), void 0, {
3417
4443
  F: __dxlog_file6,
3418
- L: 34,
4444
+ L: 50,
3419
4445
  S: void 0,
3420
4446
  A: [
3421
4447
  "isAutomergeObject(text)",
@@ -3464,6 +4490,8 @@ export {
3464
4490
  Cursor,
3465
4491
  Doc,
3466
4492
  EditorModes,
4493
+ Inline,
4494
+ List,
3467
4495
  MarkdownEditor,
3468
4496
  RemoteSelectionsDecorator,
3469
4497
  SpaceAwarenessProvider,
@@ -3472,6 +4500,11 @@ export {
3472
4500
  Toolbar,
3473
4501
  YText,
3474
4502
  YXmlFragment,
4503
+ addBlockquote,
4504
+ addCodeblock,
4505
+ addLink,
4506
+ addList,
4507
+ addStyle,
3475
4508
  autocomplete,
3476
4509
  automerge,
3477
4510
  awareness,
@@ -3481,6 +4514,7 @@ export {
3481
4514
  code2 as code,
3482
4515
  codeTheme,
3483
4516
  comments,
4517
+ compareFormatting,
3484
4518
  createBasicBundle,
3485
4519
  createComment,
3486
4520
  createDataExtensions,
@@ -3493,12 +4527,13 @@ export {
3493
4527
  defaultTheme,
3494
4528
  focusComment,
3495
4529
  formatting,
4530
+ getFormatting,
3496
4531
  getToken,
3497
4532
  heading2 as heading,
3498
4533
  hr,
3499
4534
  image,
3500
- insertCodeblock,
3501
4535
  insertTable,
4536
+ keymap7 as keymap,
3502
4537
  link,
3503
4538
  listener,
3504
4539
  logChanges,
@@ -3509,22 +4544,34 @@ export {
3509
4544
  mention,
3510
4545
  modelState,
3511
4546
  outliner,
4547
+ removeBlockquote,
4548
+ removeCodeblock,
4549
+ removeLink,
4550
+ removeList,
4551
+ removeStyle,
4552
+ setBlockquote,
3512
4553
  setComments,
3513
4554
  setHeading,
3514
4555
  setSelection,
4556
+ setStyle,
3515
4557
  table,
3516
4558
  tags2 as tags,
3517
4559
  tasklist,
3518
4560
  textTheme,
3519
- toggleBold,
3520
- toggleItalic,
4561
+ toggleBlockquote,
4562
+ toggleCodeblock,
4563
+ toggleEmphasis,
4564
+ toggleInlineCode,
3521
4565
  toggleList,
3522
4566
  toggleStrikethrough,
4567
+ toggleStrong,
3523
4568
  toggleStyle,
3524
4569
  typewriter,
3525
4570
  useActionHandler,
3526
4571
  useComments,
3527
4572
  useEditorView,
4573
+ useFormattingState,
4574
+ useInMemoryTextModel,
3528
4575
  useTextEditor,
3529
4576
  useTextModel,
3530
4577
  useToolbarContext