@dxos/react-ui-editor 0.4.8-main.c67d72c → 0.4.8-main.dcf422f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/browser/index.mjs +454 -274
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +2 -2
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +2 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +11 -5
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +4 -2
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts +1 -0
- package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +1 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.d.ts +7 -4
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/image.d.ts +6 -0
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
- package/dist/types/src/extensions/modes.d.ts +1 -0
- package/dist/types/src/extensions/modes.d.ts.map +1 -1
- package/dist/types/src/extensions/state.d.ts +20 -0
- package/dist/types/src/extensions/state.d.ts.map +1 -0
- package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.stories.d.ts +1 -0
- package/dist/types/src/hooks/useTextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/translations.d.ts +1 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/package.json +25 -24
- package/src/components/TextEditor/TextEditor.stories.tsx +16 -4
- package/src/components/TextEditor/TextEditor.tsx +13 -12
- package/src/components/Toolbar/Toolbar.stories.tsx +23 -24
- package/src/components/Toolbar/Toolbar.tsx +101 -35
- package/src/extensions/automerge/automerge.ts +4 -4
- package/src/extensions/comments.ts +2 -2
- package/src/extensions/index.ts +1 -0
- package/src/extensions/markdown/formatting.test.ts +13 -13
- package/src/extensions/markdown/formatting.ts +91 -83
- package/src/extensions/markdown/image.ts +6 -0
- package/src/extensions/modes.ts +3 -1
- package/src/extensions/state.ts +90 -0
- package/src/hooks/useActionHandler.ts +5 -1
- package/src/hooks/useTextEditor.stories.tsx +3 -3
- package/src/hooks/useTextEditor.ts +3 -0
- package/src/translations.ts +1 -0
|
@@ -15,6 +15,7 @@ var translations_default = [
|
|
|
15
15
|
"blockquote label": "Block quote",
|
|
16
16
|
"codeblock label": "Code block",
|
|
17
17
|
"comment label": "Create comment",
|
|
18
|
+
"image label": "Insert image",
|
|
18
19
|
"heading label": "Heading level",
|
|
19
20
|
"table label": "Create table"
|
|
20
21
|
}
|
|
@@ -23,18 +24,18 @@ var translations_default = [
|
|
|
23
24
|
];
|
|
24
25
|
|
|
25
26
|
// packages/ui/react-ui-editor/src/index.ts
|
|
26
|
-
import { keymap as
|
|
27
|
+
import { keymap as keymap10 } from "@codemirror/view";
|
|
27
28
|
import { tags as tags2 } from "@lezer/highlight";
|
|
28
29
|
import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
|
|
29
30
|
import { Doc, YText, YXmlFragment } from "@dxos/text-model";
|
|
30
31
|
|
|
31
32
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
32
33
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
33
|
-
import { EditorView as
|
|
34
|
+
import { EditorView as EditorView17 } from "@codemirror/view";
|
|
34
35
|
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
35
|
-
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState
|
|
36
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
|
|
36
37
|
import { log as log9 } from "@dxos/log";
|
|
37
|
-
import { isNotFalsy as
|
|
38
|
+
import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
|
|
38
39
|
|
|
39
40
|
// packages/ui/react-ui-editor/src/extensions/annotations.ts
|
|
40
41
|
import { StateField } from "@codemirror/state";
|
|
@@ -56,8 +57,8 @@ var Cursor = class _Cursor {
|
|
|
56
57
|
});
|
|
57
58
|
}
|
|
58
59
|
static {
|
|
59
|
-
this.getCursorFromRange = (
|
|
60
|
-
const cursorConverter2 =
|
|
60
|
+
this.getCursorFromRange = (state2, range) => {
|
|
61
|
+
const cursorConverter2 = state2.facet(_Cursor.converter);
|
|
61
62
|
const from = cursorConverter2.toCursor(range.from);
|
|
62
63
|
const to = cursorConverter2.toCursor(range.to, -1);
|
|
63
64
|
return [
|
|
@@ -67,8 +68,8 @@ var Cursor = class _Cursor {
|
|
|
67
68
|
};
|
|
68
69
|
}
|
|
69
70
|
static {
|
|
70
|
-
this.getRangeFromCursor = (
|
|
71
|
-
const cursorConverter2 =
|
|
71
|
+
this.getRangeFromCursor = (state2, cursor) => {
|
|
72
|
+
const cursorConverter2 = state2.facet(_Cursor.converter);
|
|
72
73
|
const parts = cursor.split(":");
|
|
73
74
|
const from = cursorConverter2.fromCursor(parts[0]);
|
|
74
75
|
const to = cursorConverter2.fromCursor(parts[1]);
|
|
@@ -85,15 +86,15 @@ var annotationMark = Decoration.mark({
|
|
|
85
86
|
class: "cm-annotation"
|
|
86
87
|
});
|
|
87
88
|
var annotations = (options = {}) => {
|
|
88
|
-
const match = (
|
|
89
|
+
const match = (state2) => {
|
|
89
90
|
const annotations2 = [];
|
|
90
|
-
const text =
|
|
91
|
+
const text = state2.doc.toString();
|
|
91
92
|
if (options.match) {
|
|
92
93
|
const matches = text.matchAll(options.match);
|
|
93
94
|
for (const match2 of matches) {
|
|
94
95
|
const from = match2.index;
|
|
95
96
|
const to = from + match2[0].length;
|
|
96
|
-
const cursor = Cursor.getCursorFromRange(
|
|
97
|
+
const cursor = Cursor.getCursorFromRange(state2, {
|
|
97
98
|
from,
|
|
98
99
|
to
|
|
99
100
|
});
|
|
@@ -105,8 +106,8 @@ var annotations = (options = {}) => {
|
|
|
105
106
|
return annotations2;
|
|
106
107
|
};
|
|
107
108
|
const annotationsState = StateField.define({
|
|
108
|
-
create: (
|
|
109
|
-
return match(
|
|
109
|
+
create: (state2) => {
|
|
110
|
+
return match(state2);
|
|
110
111
|
},
|
|
111
112
|
update: (value, tr) => {
|
|
112
113
|
if (!tr.changes.empty) {
|
|
@@ -119,10 +120,10 @@ var annotations = (options = {}) => {
|
|
|
119
120
|
annotationsState,
|
|
120
121
|
EditorView.decorations.compute([
|
|
121
122
|
annotationsState
|
|
122
|
-
], (
|
|
123
|
-
const annotations2 =
|
|
123
|
+
], (state2) => {
|
|
124
|
+
const annotations2 = state2.field(annotationsState);
|
|
124
125
|
const decorations = annotations2.map((annotation2) => {
|
|
125
|
-
const range = Cursor.getRangeFromCursor(
|
|
126
|
+
const range = Cursor.getRangeFromCursor(state2, annotation2.cursor);
|
|
126
127
|
return range && annotationMark.range(range.from, range.to);
|
|
127
128
|
}).filter(isNotFalsy);
|
|
128
129
|
return Decoration.set(decorations);
|
|
@@ -239,8 +240,8 @@ var cursorConverter = ({ handle, path }) => ({
|
|
|
239
240
|
|
|
240
241
|
// packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
|
|
241
242
|
import { Annotation, StateEffect } from "@codemirror/state";
|
|
242
|
-
var getPath = (
|
|
243
|
-
var getLastHeads = (
|
|
243
|
+
var getPath = (state2, field) => state2.field(field).path;
|
|
244
|
+
var getLastHeads = (state2, field) => state2.field(field).lastHeads;
|
|
244
245
|
var updateHeadsEffect = StateEffect.define({});
|
|
245
246
|
var updateHeads = (newHeads) => updateHeadsEffect.of({
|
|
246
247
|
newHeads
|
|
@@ -255,8 +256,8 @@ import { next as A3 } from "@dxos/automerge/automerge";
|
|
|
255
256
|
|
|
256
257
|
// packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
|
|
257
258
|
import { next as A2 } from "@dxos/automerge/automerge";
|
|
258
|
-
var updateAutomerge = (field, handle, transactions,
|
|
259
|
-
const { lastHeads, path } =
|
|
259
|
+
var updateAutomerge = (field, handle, transactions, state2) => {
|
|
260
|
+
const { lastHeads, path } = state2.field(field);
|
|
260
261
|
let hasChanges = false;
|
|
261
262
|
for (const tr of transactions) {
|
|
262
263
|
tr.changes.iterChanges(() => {
|
|
@@ -303,7 +304,7 @@ var updateCodeMirror = (view, selection, target, patches) => {
|
|
|
303
304
|
annotations: reconcileAnnotation.of(false)
|
|
304
305
|
});
|
|
305
306
|
};
|
|
306
|
-
var handlePatch = (patch, target,
|
|
307
|
+
var handlePatch = (patch, target, state2) => {
|
|
307
308
|
if (patch.action === "insert") {
|
|
308
309
|
return handleInsert(target, patch);
|
|
309
310
|
} else if (patch.action === "splice") {
|
|
@@ -311,7 +312,7 @@ var handlePatch = (patch, target, state) => {
|
|
|
311
312
|
} else if (patch.action === "del") {
|
|
312
313
|
return handleDel(target, patch);
|
|
313
314
|
} else if (patch.action === "put") {
|
|
314
|
-
return handlePut(target, patch,
|
|
315
|
+
return handlePut(target, patch, state2);
|
|
315
316
|
} else {
|
|
316
317
|
return null;
|
|
317
318
|
}
|
|
@@ -355,7 +356,7 @@ var handleDel = (target, patch) => {
|
|
|
355
356
|
}
|
|
356
357
|
];
|
|
357
358
|
};
|
|
358
|
-
var handlePut = (target, patch,
|
|
359
|
+
var handlePut = (target, patch, state2) => {
|
|
359
360
|
const index = charPath(target, [
|
|
360
361
|
...patch.path,
|
|
361
362
|
0
|
|
@@ -363,7 +364,7 @@ var handlePut = (target, patch, state) => {
|
|
|
363
364
|
if (index == null) {
|
|
364
365
|
return [];
|
|
365
366
|
}
|
|
366
|
-
const length =
|
|
367
|
+
const length = state2.doc.length;
|
|
367
368
|
if (typeof patch.value !== "string") {
|
|
368
369
|
return [];
|
|
369
370
|
}
|
|
@@ -474,13 +475,13 @@ var automerge = (accessor) => {
|
|
|
474
475
|
ViewPlugin.fromClass(class {
|
|
475
476
|
constructor(_view) {
|
|
476
477
|
this._view = _view;
|
|
477
|
-
|
|
478
|
+
this._handleChange = () => {
|
|
479
|
+
syncer.reconcile(this._view, false);
|
|
480
|
+
};
|
|
481
|
+
accessor.handle.addListener("change", this._handleChange);
|
|
478
482
|
}
|
|
479
483
|
destroy() {
|
|
480
|
-
accessor.handle.removeListener("change", this._handleChange
|
|
481
|
-
}
|
|
482
|
-
_handleChange() {
|
|
483
|
-
syncer.reconcile(this._view, false);
|
|
484
|
+
accessor.handle.removeListener("change", this._handleChange);
|
|
484
485
|
}
|
|
485
486
|
}),
|
|
486
487
|
// Reconcile local updates.
|
|
@@ -561,9 +562,9 @@ var RemoteSelectionsDecorator = class {
|
|
|
561
562
|
_updateRemoteSelections(update2) {
|
|
562
563
|
const decorations = [];
|
|
563
564
|
const awarenessStates = this._provider.getRemoteStates();
|
|
564
|
-
for (const
|
|
565
|
-
const anchor =
|
|
566
|
-
const head =
|
|
565
|
+
for (const state2 of awarenessStates) {
|
|
566
|
+
const anchor = state2.position?.anchor ? this._cursorConverter.fromCursor(state2.position.anchor) : null;
|
|
567
|
+
const head = state2.position?.head ? this._cursorConverter.fromCursor(state2.position.head) : null;
|
|
567
568
|
if (anchor == null || head == null) {
|
|
568
569
|
continue;
|
|
569
570
|
}
|
|
@@ -571,8 +572,8 @@ var RemoteSelectionsDecorator = class {
|
|
|
571
572
|
const end = Math.min(Math.max(anchor, head), update2.view.state.doc.length);
|
|
572
573
|
const startLine = update2.view.state.doc.lineAt(start);
|
|
573
574
|
const endLine = update2.view.state.doc.lineAt(end);
|
|
574
|
-
const color =
|
|
575
|
-
const lightColor =
|
|
575
|
+
const color = state2.info.color ?? "#30bced";
|
|
576
|
+
const lightColor = state2.info.lightColor ?? color + "33";
|
|
576
577
|
if (startLine.number === endLine.number) {
|
|
577
578
|
decorations.push({
|
|
578
579
|
from: start,
|
|
@@ -625,7 +626,7 @@ var RemoteSelectionsDecorator = class {
|
|
|
625
626
|
value: Decoration2.widget({
|
|
626
627
|
side: head - anchor > 0 ? -1 : 1,
|
|
627
628
|
block: false,
|
|
628
|
-
widget: new RemoteCaretWidget(
|
|
629
|
+
widget: new RemoteCaretWidget(state2.info.displayName ?? "Anonymous", color)
|
|
629
630
|
})
|
|
630
631
|
});
|
|
631
632
|
}
|
|
@@ -1156,7 +1157,7 @@ var commandConfig = Facet3.define({
|
|
|
1156
1157
|
});
|
|
1157
1158
|
var commandState = StateField3.define({
|
|
1158
1159
|
create: () => ({}),
|
|
1159
|
-
update: (
|
|
1160
|
+
update: (state2, tr) => {
|
|
1160
1161
|
for (const effect of tr.effects) {
|
|
1161
1162
|
if (effect.is(closeEffect)) {
|
|
1162
1163
|
return {};
|
|
@@ -1207,7 +1208,7 @@ var commandState = StateField3.define({
|
|
|
1207
1208
|
};
|
|
1208
1209
|
}
|
|
1209
1210
|
}
|
|
1210
|
-
return
|
|
1211
|
+
return state2;
|
|
1211
1212
|
},
|
|
1212
1213
|
provide: (field) => [
|
|
1213
1214
|
showTooltip.from(field, (value) => value.tooltip ?? null)
|
|
@@ -1453,8 +1454,8 @@ var setComments = StateEffect3.define();
|
|
|
1453
1454
|
var setSelection = StateEffect3.define();
|
|
1454
1455
|
var setCommentState = StateEffect3.define();
|
|
1455
1456
|
var commentsState = StateField4.define({
|
|
1456
|
-
create: (
|
|
1457
|
-
id:
|
|
1457
|
+
create: (state2) => ({
|
|
1458
|
+
id: state2.facet(documentId),
|
|
1458
1459
|
comments: [],
|
|
1459
1460
|
selection: {}
|
|
1460
1461
|
}),
|
|
@@ -1526,8 +1527,8 @@ var commentCurrentMark = Decoration4.mark({
|
|
|
1526
1527
|
});
|
|
1527
1528
|
var commentsDecorations = EditorView7.decorations.compute([
|
|
1528
1529
|
commentsState
|
|
1529
|
-
], (
|
|
1530
|
-
const { selection: { current }, comments: comments2 } =
|
|
1530
|
+
], (state2) => {
|
|
1531
|
+
const { selection: { current }, comments: comments2 } = state2.field(commentsState);
|
|
1531
1532
|
const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
|
|
1532
1533
|
const range = comment.range;
|
|
1533
1534
|
if (!range || range.from === range.to) {
|
|
@@ -1691,7 +1692,7 @@ var optionsFacet = Facet4.define({
|
|
|
1691
1692
|
});
|
|
1692
1693
|
var comments = (options = {}) => {
|
|
1693
1694
|
const { key: shortcut = "meta-'" } = options;
|
|
1694
|
-
const handleSelect = debounce((
|
|
1695
|
+
const handleSelect = debounce((state2) => options.onSelect?.(state2), 200);
|
|
1695
1696
|
return [
|
|
1696
1697
|
optionsFacet.of(options),
|
|
1697
1698
|
documentId.of(options.id),
|
|
@@ -1740,9 +1741,9 @@ var comments = (options = {}) => {
|
|
|
1740
1741
|
//
|
|
1741
1742
|
// Track deleted ranges and update ranges for decorations.
|
|
1742
1743
|
//
|
|
1743
|
-
EditorView7.updateListener.of(({ view, state, changes }) => {
|
|
1744
|
+
EditorView7.updateListener.of(({ view, state: state2, changes }) => {
|
|
1744
1745
|
let mod = false;
|
|
1745
|
-
const { comments: comments2, ...value } =
|
|
1746
|
+
const { comments: comments2, ...value } = state2.field(commentsState);
|
|
1746
1747
|
changes.iterChanges((from, to, from2, to2) => {
|
|
1747
1748
|
comments2.forEach(({ comment, range }) => {
|
|
1748
1749
|
if (from2 === to2) {
|
|
@@ -1772,10 +1773,10 @@ var comments = (options = {}) => {
|
|
|
1772
1773
|
//
|
|
1773
1774
|
// Track selection/proximity.
|
|
1774
1775
|
//
|
|
1775
|
-
EditorView7.updateListener.of(({ view, state }) => {
|
|
1776
|
+
EditorView7.updateListener.of(({ view, state: state2 }) => {
|
|
1776
1777
|
let min = Infinity;
|
|
1777
|
-
const { selection: { current, closest }, comments: comments2 } =
|
|
1778
|
-
const { head } =
|
|
1778
|
+
const { selection: { current, closest }, comments: comments2 } = state2.field(commentsState);
|
|
1779
|
+
const { head } = state2.selection.main;
|
|
1779
1780
|
const selection = {};
|
|
1780
1781
|
comments2.forEach(({ comment, range }) => {
|
|
1781
1782
|
if (head >= range.from && head <= range.to) {
|
|
@@ -1796,7 +1797,7 @@ var comments = (options = {}) => {
|
|
|
1796
1797
|
});
|
|
1797
1798
|
handleSelect({
|
|
1798
1799
|
selection,
|
|
1799
|
-
id:
|
|
1800
|
+
id: state2.facet(documentId),
|
|
1800
1801
|
comments: comments2.map(({ comment, range }) => ({
|
|
1801
1802
|
comment,
|
|
1802
1803
|
range,
|
|
@@ -1831,14 +1832,14 @@ var focusComment = (view, id, center = true) => {
|
|
|
1831
1832
|
});
|
|
1832
1833
|
}
|
|
1833
1834
|
};
|
|
1834
|
-
var useComments = (view, id, comments2
|
|
1835
|
+
var useComments = (view, id, comments2) => {
|
|
1835
1836
|
useEffect(() => {
|
|
1836
1837
|
if (view) {
|
|
1837
1838
|
if (id === view.state.facet(documentId)) {
|
|
1838
1839
|
view.dispatch({
|
|
1839
1840
|
effects: setComments.of({
|
|
1840
1841
|
id,
|
|
1841
|
-
comments: comments2
|
|
1842
|
+
comments: comments2 ?? []
|
|
1842
1843
|
})
|
|
1843
1844
|
});
|
|
1844
1845
|
}
|
|
@@ -1864,11 +1865,11 @@ var cursorLineMargin = (options = {
|
|
|
1864
1865
|
if (!lines || lines <= 0) {
|
|
1865
1866
|
return [];
|
|
1866
1867
|
}
|
|
1867
|
-
return EditorView8.updateListener.of(({ transactions, state, selectionSet, startState, view }) => {
|
|
1868
|
+
return EditorView8.updateListener.of(({ transactions, state: state2, selectionSet, startState, view }) => {
|
|
1868
1869
|
if (transactions.length < 1 || transactions.some((tr) => tr.isUserEvent(annotation))) {
|
|
1869
1870
|
return;
|
|
1870
1871
|
}
|
|
1871
|
-
const s =
|
|
1872
|
+
const s = state2;
|
|
1872
1873
|
const { main } = s.selection;
|
|
1873
1874
|
const rect = view.dom.getBoundingClientRect();
|
|
1874
1875
|
const viewportTop = rect.top - view.documentTop;
|
|
@@ -2606,8 +2607,8 @@ var checkedTask = Decoration5.replace({
|
|
|
2606
2607
|
var uncheckedTask = Decoration5.replace({
|
|
2607
2608
|
widget: new CheckboxWidget(false)
|
|
2608
2609
|
});
|
|
2609
|
-
var editingRange = (
|
|
2610
|
-
const { readOnly, selection: { main: { head } } } =
|
|
2610
|
+
var editingRange = (state2, range, focus) => {
|
|
2611
|
+
const { readOnly, selection: { main: { head } } } = state2;
|
|
2611
2612
|
return focus && !readOnly && head >= range.from && head <= range.to;
|
|
2612
2613
|
};
|
|
2613
2614
|
var MarksByParent = /* @__PURE__ */ new Set([
|
|
@@ -2620,14 +2621,14 @@ var MarksByParent = /* @__PURE__ */ new Set([
|
|
|
2620
2621
|
var buildDecorations = (view, options, focus) => {
|
|
2621
2622
|
const deco = new RangeSetBuilder2();
|
|
2622
2623
|
const atomicDeco = new RangeSetBuilder2();
|
|
2623
|
-
const { state } = view;
|
|
2624
|
+
const { state: state2 } = view;
|
|
2624
2625
|
for (const { from, to } of view.visibleRanges) {
|
|
2625
|
-
syntaxTree(
|
|
2626
|
+
syntaxTree(state2).iterate({
|
|
2626
2627
|
from,
|
|
2627
2628
|
to,
|
|
2628
2629
|
enter: (node) => {
|
|
2629
2630
|
if (node.name === "FencedCode") {
|
|
2630
|
-
const editing = editingRange(
|
|
2631
|
+
const editing = editingRange(state2, node, focus);
|
|
2631
2632
|
for (const block of view.viewportLineBlocks) {
|
|
2632
2633
|
if (block.to < node.from) {
|
|
2633
2634
|
continue;
|
|
@@ -2636,7 +2637,7 @@ var buildDecorations = (view, options, focus) => {
|
|
|
2636
2637
|
break;
|
|
2637
2638
|
}
|
|
2638
2639
|
const first = block.from <= node.from;
|
|
2639
|
-
const last = block.to >= node.to && /^(\s>)*```$/.test(
|
|
2640
|
+
const last = block.to >= node.to && /^(\s>)*```$/.test(state2.doc.sliceString(block.from, block.to));
|
|
2640
2641
|
deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
|
|
2641
2642
|
if (!editing && (first || last)) {
|
|
2642
2643
|
atomicDeco.add(block.from, block.to, hide);
|
|
@@ -2646,9 +2647,9 @@ var buildDecorations = (view, options, focus) => {
|
|
|
2646
2647
|
} else if (node.name === "Link") {
|
|
2647
2648
|
const marks = node.node.getChildren("LinkMark");
|
|
2648
2649
|
const urlNode = node.node.getChild("URL");
|
|
2649
|
-
const editing = editingRange(
|
|
2650
|
+
const editing = editingRange(state2, node, focus);
|
|
2650
2651
|
if (urlNode && marks.length >= 2) {
|
|
2651
|
-
const url =
|
|
2652
|
+
const url = state2.sliceDoc(urlNode.from, urlNode.to);
|
|
2652
2653
|
if (!editing) {
|
|
2653
2654
|
atomicDeco.add(node.from, marks[0].to, hide);
|
|
2654
2655
|
}
|
|
@@ -2669,24 +2670,24 @@ var buildDecorations = (view, options, focus) => {
|
|
|
2669
2670
|
}
|
|
2670
2671
|
} else if (node.name === "HeaderMark") {
|
|
2671
2672
|
const parent = node.node.parent;
|
|
2672
|
-
if (/^ATX/.test(parent.name) && !editingRange(
|
|
2673
|
-
const next =
|
|
2673
|
+
if (/^ATX/.test(parent.name) && !editingRange(state2, state2.doc.lineAt(node.from), focus)) {
|
|
2674
|
+
const next = state2.doc.sliceString(node.to, node.to + 1);
|
|
2674
2675
|
atomicDeco.add(node.from, node.to + (next === " " ? 1 : 0), hide);
|
|
2675
2676
|
}
|
|
2676
2677
|
} else if (node.name === "HorizontalRule") {
|
|
2677
|
-
if (!editingRange(
|
|
2678
|
+
if (!editingRange(state2, node, focus)) {
|
|
2678
2679
|
deco.add(node.from, node.to, horizontalRule);
|
|
2679
2680
|
}
|
|
2680
2681
|
} else if (node.name === "TaskMarker") {
|
|
2681
|
-
if (!editingRange(
|
|
2682
|
-
const checked =
|
|
2682
|
+
if (!editingRange(state2, node, focus)) {
|
|
2683
|
+
const checked = state2.doc.sliceString(node.from + 1, node.to - 1) === "x";
|
|
2683
2684
|
atomicDeco.add(node.from - 2, node.from - 1, Decoration5.mark({
|
|
2684
2685
|
class: "cm-task"
|
|
2685
2686
|
}));
|
|
2686
2687
|
atomicDeco.add(node.from, node.to, checked ? checkedTask : uncheckedTask);
|
|
2687
2688
|
}
|
|
2688
2689
|
} else if (MarksByParent.has(node.name)) {
|
|
2689
|
-
if (!editingRange(
|
|
2690
|
+
if (!editingRange(state2, node.node.parent, focus)) {
|
|
2690
2691
|
atomicDeco.add(node.from, node.to, hide);
|
|
2691
2692
|
}
|
|
2692
2693
|
}
|
|
@@ -2796,8 +2797,9 @@ import { snippet } from "@codemirror/autocomplete";
|
|
|
2796
2797
|
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
2797
2798
|
import { EditorSelection } from "@codemirror/state";
|
|
2798
2799
|
import { EditorView as EditorView13, keymap as keymap6 } from "@codemirror/view";
|
|
2799
|
-
import {
|
|
2800
|
-
|
|
2800
|
+
import { useMemo } from "react";
|
|
2801
|
+
import { useStateRef } from "@dxos/react-async";
|
|
2802
|
+
var formattingEquals = (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;
|
|
2801
2803
|
var Inline;
|
|
2802
2804
|
(function(Inline2) {
|
|
2803
2805
|
Inline2[Inline2["Strong"] = 0] = "Strong";
|
|
@@ -2811,13 +2813,13 @@ var List;
|
|
|
2811
2813
|
List2[List2["Bullet"] = 1] = "Bullet";
|
|
2812
2814
|
List2[List2["Task"] = 2] = "Task";
|
|
2813
2815
|
})(List || (List = {}));
|
|
2814
|
-
var setHeading = (level) => ({ state, dispatch }) => {
|
|
2815
|
-
const { selection: { ranges }, doc } =
|
|
2816
|
+
var setHeading = (level) => ({ state: state2, dispatch }) => {
|
|
2817
|
+
const { selection: { ranges }, doc } = state2;
|
|
2816
2818
|
const changes = [];
|
|
2817
2819
|
let prevBlock = -1;
|
|
2818
2820
|
for (const range of ranges) {
|
|
2819
2821
|
let sawBlock = false;
|
|
2820
|
-
syntaxTree2(
|
|
2822
|
+
syntaxTree2(state2).iterate({
|
|
2821
2823
|
from: range.from,
|
|
2822
2824
|
to: range.to,
|
|
2823
2825
|
enter: (node) => {
|
|
@@ -2870,7 +2872,7 @@ var setHeading = (level) => ({ state, dispatch }) => {
|
|
|
2870
2872
|
}
|
|
2871
2873
|
});
|
|
2872
2874
|
let line;
|
|
2873
|
-
if (!sawBlock && range.empty && level > 0 && !/\S/.test((line =
|
|
2875
|
+
if (!sawBlock && range.empty && level > 0 && !/\S/.test((line = state2.doc.lineAt(range.from)).text)) {
|
|
2874
2876
|
changes.push({
|
|
2875
2877
|
from: line.from,
|
|
2876
2878
|
to: line.to,
|
|
@@ -2881,23 +2883,23 @@ var setHeading = (level) => ({ state, dispatch }) => {
|
|
|
2881
2883
|
if (!changes.length) {
|
|
2882
2884
|
return false;
|
|
2883
2885
|
}
|
|
2884
|
-
const changeSet =
|
|
2885
|
-
dispatch(
|
|
2886
|
+
const changeSet = state2.changes(changes);
|
|
2887
|
+
dispatch(state2.update({
|
|
2886
2888
|
changes: changeSet,
|
|
2887
|
-
selection:
|
|
2889
|
+
selection: state2.selection.map(changeSet, 1),
|
|
2888
2890
|
userEvent: "format.setHeading",
|
|
2889
2891
|
scrollIntoView: true
|
|
2890
2892
|
}));
|
|
2891
2893
|
return true;
|
|
2892
2894
|
};
|
|
2893
|
-
var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
2895
|
+
var setStyle = (type, enable) => ({ state: state2, dispatch }) => {
|
|
2894
2896
|
const marker = inlineMarkerText(type);
|
|
2895
|
-
const changes =
|
|
2897
|
+
const changes = state2.changeByRange((range) => {
|
|
2896
2898
|
if (!enable && range.empty) {
|
|
2897
|
-
const after =
|
|
2899
|
+
const after = state2.doc.sliceString(range.head, range.head + 6);
|
|
2898
2900
|
const found = after.indexOf(marker);
|
|
2899
2901
|
if (found >= 0 && /^[*~`]*$/.test(after.slice(0, found))) {
|
|
2900
|
-
const before =
|
|
2902
|
+
const before = state2.doc.sliceString(range.head - 6, range.head);
|
|
2901
2903
|
if (before.slice(before.length - found - marker.length, before.length - found) === marker && [
|
|
2902
2904
|
...before.slice(before.length - found)
|
|
2903
2905
|
].reverse().join("") === after.slice(0, found)) {
|
|
@@ -2924,14 +2926,14 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
|
2924
2926
|
let startCovered = false;
|
|
2925
2927
|
let endCovered = false;
|
|
2926
2928
|
let { from, to } = range;
|
|
2927
|
-
syntaxTree2(
|
|
2929
|
+
syntaxTree2(state2).iterate({
|
|
2928
2930
|
from,
|
|
2929
2931
|
to,
|
|
2930
2932
|
enter: (node) => {
|
|
2931
2933
|
const { name } = node;
|
|
2932
2934
|
if (Object.hasOwn(Textblocks, name) && Textblocks[name] !== "codeblock") {
|
|
2933
2935
|
blockStart = blockContentStart(node);
|
|
2934
|
-
blockEnd = blockContentEnd(node,
|
|
2936
|
+
blockEnd = blockContentEnd(node, state2.doc);
|
|
2935
2937
|
startCovered = endCovered = false;
|
|
2936
2938
|
} else if (name === "Link" || name === "Image" && enable) {
|
|
2937
2939
|
if (from < node.from && to > node.from && to <= node.to) {
|
|
@@ -2976,7 +2978,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
|
2976
2978
|
});
|
|
2977
2979
|
if (markType !== type && closeStart >= to) {
|
|
2978
2980
|
changesAtEnd.push({
|
|
2979
|
-
from: skipSpaces(Math.min(to, blockEnd),
|
|
2981
|
+
from: skipSpaces(Math.min(to, blockEnd), state2.doc, 1, blockEnd),
|
|
2980
2982
|
insert: inlineMarkerText(markType)
|
|
2981
2983
|
});
|
|
2982
2984
|
}
|
|
@@ -2988,7 +2990,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
|
2988
2990
|
});
|
|
2989
2991
|
if (markType !== type && openEnd <= from) {
|
|
2990
2992
|
changes2.push({
|
|
2991
|
-
from: skipSpaces(Math.max(from, blockStart),
|
|
2993
|
+
from: skipSpaces(Math.max(from, blockStart), state2.doc, -1, blockStart),
|
|
2992
2994
|
insert: inlineMarkerText(markType)
|
|
2993
2995
|
});
|
|
2994
2996
|
}
|
|
@@ -3018,7 +3020,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
|
3018
3020
|
changes2.push(startCovered);
|
|
3019
3021
|
} else if (startCovered) {
|
|
3020
3022
|
changes2.push({
|
|
3021
|
-
from: skipSpaces(rangeStart,
|
|
3023
|
+
from: skipSpaces(rangeStart, state2.doc, -1, blockStart),
|
|
3022
3024
|
insert: marker
|
|
3023
3025
|
});
|
|
3024
3026
|
}
|
|
@@ -3026,7 +3028,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
|
3026
3028
|
changes2.push(endCovered);
|
|
3027
3029
|
} else if (endCovered) {
|
|
3028
3030
|
changes2.push({
|
|
3029
|
-
from: skipSpaces(rangeEnd,
|
|
3031
|
+
from: skipSpaces(rangeEnd, state2.doc, 1, blockEnd),
|
|
3030
3032
|
insert: marker
|
|
3031
3033
|
});
|
|
3032
3034
|
}
|
|
@@ -3034,7 +3036,7 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
|
3034
3036
|
}
|
|
3035
3037
|
}
|
|
3036
3038
|
});
|
|
3037
|
-
if (blockStart < 0 && range.empty && enable && !/\S/.test(
|
|
3039
|
+
if (blockStart < 0 && range.empty && enable && !/\S/.test(state2.doc.lineAt(range.from).text)) {
|
|
3038
3040
|
return {
|
|
3039
3041
|
changes: {
|
|
3040
3042
|
from: range.head,
|
|
@@ -3043,13 +3045,13 @@ var setStyle = (type, enable) => ({ state, dispatch }) => {
|
|
|
3043
3045
|
range: EditorSelection.cursor(range.head + marker.length)
|
|
3044
3046
|
};
|
|
3045
3047
|
}
|
|
3046
|
-
const changeSet =
|
|
3048
|
+
const changeSet = state2.changes(changes2.concat(changesAtEnd));
|
|
3047
3049
|
return {
|
|
3048
3050
|
changes: changeSet,
|
|
3049
3051
|
range: range.empty && !changeSet.empty ? EditorSelection.cursor(range.head + marker.length) : EditorSelection.range(changeSet.mapPos(range.from, 1), changeSet.mapPos(range.to, -1))
|
|
3050
3052
|
};
|
|
3051
3053
|
});
|
|
3052
|
-
dispatch(
|
|
3054
|
+
dispatch(state2.update(changes, {
|
|
3053
3055
|
userEvent: enable ? "format.style.add" : "format.style.remove",
|
|
3054
3056
|
scrollIntoView: true
|
|
3055
3057
|
}));
|
|
@@ -3123,8 +3125,8 @@ var insertTable = (view) => {
|
|
|
3123
3125
|
const { from } = doc.line(number);
|
|
3124
3126
|
snippets.table(view, null, from, from);
|
|
3125
3127
|
};
|
|
3126
|
-
var removeLinkInner = (from, to, changes,
|
|
3127
|
-
syntaxTree2(
|
|
3128
|
+
var removeLinkInner = (from, to, changes, state2) => {
|
|
3129
|
+
syntaxTree2(state2).iterate({
|
|
3128
3130
|
from,
|
|
3129
3131
|
to,
|
|
3130
3132
|
enter: (node) => {
|
|
@@ -3138,8 +3140,8 @@ var removeLinkInner = (from, to, changes, state) => {
|
|
|
3138
3140
|
});
|
|
3139
3141
|
} else if (name === "LinkTitle" || name === "URL") {
|
|
3140
3142
|
changes.push({
|
|
3141
|
-
from: skipSpaces(node2.from,
|
|
3142
|
-
to: skipSpaces(node2.to,
|
|
3143
|
+
from: skipSpaces(node2.from, state2.doc, -1),
|
|
3144
|
+
to: skipSpaces(node2.to, state2.doc, 1)
|
|
3143
3145
|
});
|
|
3144
3146
|
}
|
|
3145
3147
|
});
|
|
@@ -3148,32 +3150,32 @@ var removeLinkInner = (from, to, changes, state) => {
|
|
|
3148
3150
|
}
|
|
3149
3151
|
});
|
|
3150
3152
|
};
|
|
3151
|
-
var removeLink = ({ state, dispatch }) => {
|
|
3153
|
+
var removeLink = ({ state: state2, dispatch }) => {
|
|
3152
3154
|
const changes = [];
|
|
3153
|
-
for (const { from, to } of
|
|
3154
|
-
removeLinkInner(from, to, changes,
|
|
3155
|
+
for (const { from, to } of state2.selection.ranges) {
|
|
3156
|
+
removeLinkInner(from, to, changes, state2);
|
|
3155
3157
|
}
|
|
3156
3158
|
if (!changes) {
|
|
3157
3159
|
return false;
|
|
3158
3160
|
}
|
|
3159
|
-
dispatch(
|
|
3161
|
+
dispatch(state2.update({
|
|
3160
3162
|
changes,
|
|
3161
3163
|
userEvent: "format.link.remove",
|
|
3162
3164
|
scrollIntoView: true
|
|
3163
3165
|
}));
|
|
3164
3166
|
return true;
|
|
3165
3167
|
};
|
|
3166
|
-
var addLink = ({ state, dispatch }) => {
|
|
3167
|
-
const changes =
|
|
3168
|
+
var addLink = ({ url, image: image2 } = {}) => ({ state: state2, dispatch }) => {
|
|
3169
|
+
const changes = state2.changeByRange((range) => {
|
|
3168
3170
|
let { from, to } = range;
|
|
3169
3171
|
const cutStyles = [];
|
|
3170
3172
|
let okay = null;
|
|
3171
|
-
syntaxTree2(
|
|
3173
|
+
syntaxTree2(state2).iterate({
|
|
3172
3174
|
from,
|
|
3173
3175
|
to,
|
|
3174
3176
|
enter: (node) => {
|
|
3175
3177
|
if (Object.hasOwn(Textblocks, node.name)) {
|
|
3176
|
-
okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node,
|
|
3178
|
+
okay = Textblocks[node.name] !== "codeblock" && from >= blockContentStart(node) && to <= blockContentEnd(node, state2.doc);
|
|
3177
3179
|
} else if (Object.hasOwn(InlineMarker, node.name)) {
|
|
3178
3180
|
const sNode = node.node;
|
|
3179
3181
|
if (node.from < from && node.to <= to) {
|
|
@@ -3193,7 +3195,7 @@ var addLink = ({ state, dispatch }) => {
|
|
|
3193
3195
|
}
|
|
3194
3196
|
});
|
|
3195
3197
|
if (okay === null) {
|
|
3196
|
-
const line =
|
|
3198
|
+
const line = state2.doc.lineAt(from);
|
|
3197
3199
|
okay = to <= line.to && !/\S/.test(line.text.slice(from - line.from));
|
|
3198
3200
|
}
|
|
3199
3201
|
if (!okay) {
|
|
@@ -3203,26 +3205,26 @@ var addLink = ({ state, dispatch }) => {
|
|
|
3203
3205
|
}
|
|
3204
3206
|
const changes2 = [];
|
|
3205
3207
|
const changesAfter = [];
|
|
3206
|
-
removeLinkInner(from, to, changesAfter,
|
|
3208
|
+
removeLinkInner(from, to, changesAfter, state2);
|
|
3207
3209
|
let cursorOffset = 1;
|
|
3208
3210
|
for (const style of cutStyles) {
|
|
3209
3211
|
const type = InlineMarker[style.name];
|
|
3210
3212
|
const mark2 = inlineMarkerText(type);
|
|
3211
3213
|
if (style.from < from) {
|
|
3212
3214
|
changes2.push({
|
|
3213
|
-
from: skipSpaces(from,
|
|
3215
|
+
from: skipSpaces(from, state2.doc, -1),
|
|
3214
3216
|
insert: mark2
|
|
3215
3217
|
});
|
|
3216
3218
|
changesAfter.push({
|
|
3217
|
-
from: skipSpaces(from,
|
|
3219
|
+
from: skipSpaces(from, state2.doc, 1, to),
|
|
3218
3220
|
insert: mark2
|
|
3219
3221
|
});
|
|
3220
3222
|
} else {
|
|
3221
3223
|
changes2.push({
|
|
3222
|
-
from: skipSpaces(to,
|
|
3224
|
+
from: skipSpaces(to, state2.doc, -1, from),
|
|
3223
3225
|
insert: mark2
|
|
3224
3226
|
});
|
|
3225
|
-
const after = skipSpaces(to,
|
|
3227
|
+
const after = skipSpaces(to, state2.doc, 1);
|
|
3226
3228
|
if (after === to) {
|
|
3227
3229
|
cursorOffset += mark2.length;
|
|
3228
3230
|
}
|
|
@@ -3234,34 +3236,34 @@ var addLink = ({ state, dispatch }) => {
|
|
|
3234
3236
|
}
|
|
3235
3237
|
changes2.push({
|
|
3236
3238
|
from,
|
|
3237
|
-
insert: "["
|
|
3239
|
+
insert: image2 ? "`
|
|
3241
3243
|
});
|
|
3242
|
-
const changeSet =
|
|
3244
|
+
const changeSet = state2.changes(changes2.concat(changesAfter));
|
|
3243
3245
|
return {
|
|
3244
3246
|
changes: changeSet,
|
|
3245
|
-
range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset)
|
|
3247
|
+
range: EditorSelection.cursor(changeSet.mapPos(to, 1) - cursorOffset - (url ? url.length + 2 : 0))
|
|
3246
3248
|
};
|
|
3247
3249
|
});
|
|
3248
3250
|
if (changes.changes.empty) {
|
|
3249
3251
|
return false;
|
|
3250
3252
|
}
|
|
3251
|
-
dispatch(
|
|
3253
|
+
dispatch(state2.update(changes, {
|
|
3252
3254
|
userEvent: "format.link.add",
|
|
3253
3255
|
scrollIntoView: true
|
|
3254
3256
|
}));
|
|
3255
3257
|
return true;
|
|
3256
3258
|
};
|
|
3257
|
-
var addList = (type) => ({ state, dispatch }) => {
|
|
3259
|
+
var addList = (type) => ({ state: state2, dispatch }) => {
|
|
3258
3260
|
let lastBlock = -1;
|
|
3259
3261
|
let counter = 1;
|
|
3260
3262
|
let first = true;
|
|
3261
3263
|
let parentColumn = null;
|
|
3262
3264
|
const blocks = [];
|
|
3263
|
-
for (const { from, to } of
|
|
3264
|
-
syntaxTree2(
|
|
3265
|
+
for (const { from, to } of state2.selection.ranges) {
|
|
3266
|
+
syntaxTree2(state2).iterate({
|
|
3265
3267
|
from,
|
|
3266
3268
|
to,
|
|
3267
3269
|
enter: (node) => {
|
|
@@ -3273,7 +3275,7 @@ var addList = (type) => ({ state, dispatch }) => {
|
|
|
3273
3275
|
}
|
|
3274
3276
|
if (before?.name === (type === 0 ? "OrderedList" : "BulletList")) {
|
|
3275
3277
|
const item = before.lastChild;
|
|
3276
|
-
const itemLine =
|
|
3278
|
+
const itemLine = state2.doc.lineAt(item.from);
|
|
3277
3279
|
const itemText = itemLine.text.slice(item.from - itemLine.from);
|
|
3278
3280
|
parentColumn = item.from - itemLine.from + /^\s*/.exec(itemText)[0].length;
|
|
3279
3281
|
if (type === 0) {
|
|
@@ -3308,10 +3310,10 @@ var addList = (type) => ({ state, dispatch }) => {
|
|
|
3308
3310
|
});
|
|
3309
3311
|
}
|
|
3310
3312
|
if (!blocks.length) {
|
|
3311
|
-
const { from, to } =
|
|
3313
|
+
const { from, to } = state2.doc.lineAt(state2.selection.main.anchor);
|
|
3312
3314
|
if (from === to) {
|
|
3313
3315
|
const insert = type === 1 ? "- " : type === 0 ? "1. " : "- [ ] ";
|
|
3314
|
-
dispatch(
|
|
3316
|
+
dispatch(state2.update({
|
|
3315
3317
|
changes: [
|
|
3316
3318
|
{
|
|
3317
3319
|
from,
|
|
@@ -3332,11 +3334,11 @@ var addList = (type) => ({ state, dispatch }) => {
|
|
|
3332
3334
|
for (let i = 0; i < blocks.length; i++) {
|
|
3333
3335
|
const { node, counter: counter2, parentColumn: parentColumn2 } = blocks[i];
|
|
3334
3336
|
const nodeFrom = node.name === "CodeBlock" ? node.from - 4 : node.from;
|
|
3335
|
-
let padding = nodeFrom > 0 && !/\s/.test(
|
|
3337
|
+
let padding = nodeFrom > 0 && !/\s/.test(state2.doc.sliceString(nodeFrom - 1, nodeFrom)) ? 1 : 0;
|
|
3336
3338
|
if (type === 0) {
|
|
3337
3339
|
padding += String(counter2).length;
|
|
3338
3340
|
}
|
|
3339
|
-
let line =
|
|
3341
|
+
let line = state2.doc.lineAt(nodeFrom);
|
|
3340
3342
|
const column = nodeFrom - line.from;
|
|
3341
3343
|
if (parentColumn2 !== null && parentColumn2 > column) {
|
|
3342
3344
|
padding = Math.max(padding, parentColumn2 - column);
|
|
@@ -3361,7 +3363,7 @@ var addList = (type) => ({ state, dispatch }) => {
|
|
|
3361
3363
|
insert: mark2
|
|
3362
3364
|
});
|
|
3363
3365
|
while (line.to < node.to) {
|
|
3364
|
-
line =
|
|
3366
|
+
line = state2.doc.lineAt(line.to + 1);
|
|
3365
3367
|
const open = /^[\s>]*/.exec(line.text)[0].length;
|
|
3366
3368
|
changes.push({
|
|
3367
3369
|
from: line.from + Math.min(open, column),
|
|
@@ -3376,25 +3378,25 @@ var addList = (type) => ({ state, dispatch }) => {
|
|
|
3376
3378
|
next = next.nextSibling;
|
|
3377
3379
|
}
|
|
3378
3380
|
if (next?.name === "OrderedList") {
|
|
3379
|
-
renumberListItems(next.firstChild, last.counter + 1, changes,
|
|
3381
|
+
renumberListItems(next.firstChild, last.counter + 1, changes, state2.doc);
|
|
3380
3382
|
}
|
|
3381
3383
|
}
|
|
3382
|
-
const changeSet =
|
|
3383
|
-
dispatch(
|
|
3384
|
+
const changeSet = state2.changes(changes);
|
|
3385
|
+
dispatch(state2.update({
|
|
3384
3386
|
changes: changeSet,
|
|
3385
|
-
selection:
|
|
3387
|
+
selection: state2.selection.map(changeSet, 1),
|
|
3386
3388
|
userEvent: "format.list.add",
|
|
3387
3389
|
scrollIntoView: true
|
|
3388
3390
|
}));
|
|
3389
3391
|
return true;
|
|
3390
3392
|
};
|
|
3391
|
-
var removeList = (type) => ({ state, dispatch }) => {
|
|
3393
|
+
var removeList = (type) => ({ state: state2, dispatch }) => {
|
|
3392
3394
|
let lastBlock = -1;
|
|
3393
3395
|
const changes = [];
|
|
3394
3396
|
const stack = [];
|
|
3395
3397
|
const targetNodeType = type === 0 ? "OrderedList" : type === 1 ? "BulletList" : "TaskList";
|
|
3396
|
-
for (const { from, to } of
|
|
3397
|
-
syntaxTree2(
|
|
3398
|
+
for (const { from, to } of state2.selection.ranges) {
|
|
3399
|
+
syntaxTree2(state2).iterate({
|
|
3398
3400
|
from,
|
|
3399
3401
|
to,
|
|
3400
3402
|
enter: (node) => {
|
|
@@ -3411,7 +3413,7 @@ var removeList = (type) => ({ state, dispatch }) => {
|
|
|
3411
3413
|
stack.pop();
|
|
3412
3414
|
} else if (name === "ListItem" && stack[stack.length - 1] === targetNodeType && node.from !== lastBlock) {
|
|
3413
3415
|
lastBlock = node.from;
|
|
3414
|
-
let line =
|
|
3416
|
+
let line = state2.doc.lineAt(node.from);
|
|
3415
3417
|
const mark2 = /^\s*(\d+[.)] |[-*+] (\[[ x]\] )?)/.exec(line.text.slice(node.from - line.from));
|
|
3416
3418
|
if (!mark2) {
|
|
3417
3419
|
return false;
|
|
@@ -3422,7 +3424,7 @@ var removeList = (type) => ({ state, dispatch }) => {
|
|
|
3422
3424
|
to: node.from + mark2[0].length
|
|
3423
3425
|
});
|
|
3424
3426
|
while (line.to < node.to) {
|
|
3425
|
-
line =
|
|
3427
|
+
line = state2.doc.lineAt(line.to + 1);
|
|
3426
3428
|
const open = /^[\s>]*/.exec(line.text)[0].length;
|
|
3427
3429
|
if (open > column) {
|
|
3428
3430
|
changes.push({
|
|
@@ -3432,7 +3434,7 @@ var removeList = (type) => ({ state, dispatch }) => {
|
|
|
3432
3434
|
}
|
|
3433
3435
|
}
|
|
3434
3436
|
if (node.to >= to) {
|
|
3435
|
-
renumberListItems(node.node.nextSibling, 1, changes,
|
|
3437
|
+
renumberListItems(node.node.nextSibling, 1, changes, state2.doc);
|
|
3436
3438
|
}
|
|
3437
3439
|
return false;
|
|
3438
3440
|
}
|
|
@@ -3442,7 +3444,7 @@ var removeList = (type) => ({ state, dispatch }) => {
|
|
|
3442
3444
|
if (!changes.length) {
|
|
3443
3445
|
return false;
|
|
3444
3446
|
}
|
|
3445
|
-
dispatch(
|
|
3447
|
+
dispatch(state2.update({
|
|
3446
3448
|
changes,
|
|
3447
3449
|
userEvent: "format.list.remove",
|
|
3448
3450
|
scrollIntoView: true
|
|
@@ -3472,12 +3474,12 @@ var renumberListItems = (item, counter, changes, doc) => {
|
|
|
3472
3474
|
}
|
|
3473
3475
|
}
|
|
3474
3476
|
};
|
|
3475
|
-
var setBlockquote = (enable) => ({ state, dispatch }) => {
|
|
3477
|
+
var setBlockquote = (enable) => ({ state: state2, dispatch }) => {
|
|
3476
3478
|
const lines = [];
|
|
3477
3479
|
let lastBlock = -1;
|
|
3478
|
-
for (const { from, to } of
|
|
3480
|
+
for (const { from, to } of state2.selection.ranges) {
|
|
3479
3481
|
const sawBlock = false;
|
|
3480
|
-
syntaxTree2(
|
|
3482
|
+
syntaxTree2(state2).iterate({
|
|
3481
3483
|
from,
|
|
3482
3484
|
to,
|
|
3483
3485
|
enter: (node) => {
|
|
@@ -3486,9 +3488,9 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
|
|
|
3486
3488
|
return false;
|
|
3487
3489
|
}
|
|
3488
3490
|
lastBlock = node.from;
|
|
3489
|
-
let line2 =
|
|
3491
|
+
let line2 = state2.doc.lineAt(node.from);
|
|
3490
3492
|
if (line2.number > 1) {
|
|
3491
|
-
const prevLine =
|
|
3493
|
+
const prevLine = state2.doc.line(line2.number - 1);
|
|
3492
3494
|
if (/^[>\s]*$/.test(prevLine.text)) {
|
|
3493
3495
|
if (!enable || lines.length && lines[lines.length - 1].number === prevLine.number - 1) {
|
|
3494
3496
|
lines.push(prevLine);
|
|
@@ -3500,10 +3502,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
|
|
|
3500
3502
|
if (line2.to >= node.to) {
|
|
3501
3503
|
break;
|
|
3502
3504
|
}
|
|
3503
|
-
line2 =
|
|
3505
|
+
line2 = state2.doc.line(line2.number + 1);
|
|
3504
3506
|
}
|
|
3505
|
-
if (!enable && line2.number <
|
|
3506
|
-
const nextLine =
|
|
3507
|
+
if (!enable && line2.number < state2.doc.lines) {
|
|
3508
|
+
const nextLine = state2.doc.line(line2.number + 1);
|
|
3507
3509
|
if (/^[>\s]*$/.test(nextLine.text)) {
|
|
3508
3510
|
lines.push(nextLine);
|
|
3509
3511
|
}
|
|
@@ -3513,7 +3515,7 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
|
|
|
3513
3515
|
}
|
|
3514
3516
|
});
|
|
3515
3517
|
let line;
|
|
3516
|
-
if (!sawBlock && enable && from === to && !/\S/.test((line =
|
|
3518
|
+
if (!sawBlock && enable && from === to && !/\S/.test((line = state2.doc.lineAt(from)).text)) {
|
|
3517
3519
|
lines.push(line);
|
|
3518
3520
|
}
|
|
3519
3521
|
}
|
|
@@ -3537,10 +3539,10 @@ var setBlockquote = (enable) => ({ state, dispatch }) => {
|
|
|
3537
3539
|
if (!changes.length) {
|
|
3538
3540
|
return false;
|
|
3539
3541
|
}
|
|
3540
|
-
const changeSet =
|
|
3541
|
-
dispatch(
|
|
3542
|
+
const changeSet = state2.changes(changes);
|
|
3543
|
+
dispatch(state2.update({
|
|
3542
3544
|
changes: changeSet,
|
|
3543
|
-
selection:
|
|
3545
|
+
selection: state2.selection.map(changeSet, 1),
|
|
3544
3546
|
userEvent: enable ? "format.blockquote.add" : "format.blockquote.remove",
|
|
3545
3547
|
scrollIntoView: true
|
|
3546
3548
|
}));
|
|
@@ -3552,11 +3554,11 @@ var toggleBlockquote = (target) => {
|
|
|
3552
3554
|
return (getFormatting(target.state).blockQuote ? removeBlockquote : addBlockquote)(target);
|
|
3553
3555
|
};
|
|
3554
3556
|
var addCodeblock = (target) => {
|
|
3555
|
-
const { state, dispatch } = target;
|
|
3556
|
-
const { selection } =
|
|
3557
|
+
const { state: state2, dispatch } = target;
|
|
3558
|
+
const { selection } = state2;
|
|
3557
3559
|
if (selection.ranges.length === 1 && selection.main.empty) {
|
|
3558
3560
|
const { head } = selection.main;
|
|
3559
|
-
const line =
|
|
3561
|
+
const line = state2.doc.lineAt(head);
|
|
3560
3562
|
if (!/\S/.test(line.text) && head === line.from) {
|
|
3561
3563
|
snippets.codeblock(target, null, line.from, line.to);
|
|
3562
3564
|
return true;
|
|
@@ -3566,7 +3568,7 @@ var addCodeblock = (target) => {
|
|
|
3566
3568
|
for (const { from, to } of selection.ranges) {
|
|
3567
3569
|
let blockFrom = from;
|
|
3568
3570
|
let blockTo = to;
|
|
3569
|
-
syntaxTree2(
|
|
3571
|
+
syntaxTree2(state2).iterate({
|
|
3570
3572
|
from,
|
|
3571
3573
|
to,
|
|
3572
3574
|
enter: (node) => {
|
|
@@ -3575,8 +3577,8 @@ var addCodeblock = (target) => {
|
|
|
3575
3577
|
blockFrom = node.from;
|
|
3576
3578
|
blockTo = node.to;
|
|
3577
3579
|
} else {
|
|
3578
|
-
blockFrom = Math.min(blockFrom,
|
|
3579
|
-
blockTo = Math.max(blockTo,
|
|
3580
|
+
blockFrom = Math.min(blockFrom, state2.doc.lineAt(node.from).from);
|
|
3581
|
+
blockTo = Math.max(blockTo, state2.doc.lineAt(node.to).to);
|
|
3580
3582
|
}
|
|
3581
3583
|
}
|
|
3582
3584
|
}
|
|
@@ -3594,7 +3596,7 @@ var addCodeblock = (target) => {
|
|
|
3594
3596
|
return false;
|
|
3595
3597
|
}
|
|
3596
3598
|
const changes = ranges.map(({ from, to }) => {
|
|
3597
|
-
const column = from -
|
|
3599
|
+
const column = from - state2.doc.lineAt(from).from;
|
|
3598
3600
|
return [
|
|
3599
3601
|
{
|
|
3600
3602
|
from,
|
|
@@ -3606,30 +3608,30 @@ var addCodeblock = (target) => {
|
|
|
3606
3608
|
}
|
|
3607
3609
|
];
|
|
3608
3610
|
});
|
|
3609
|
-
dispatch(
|
|
3611
|
+
dispatch(state2.update({
|
|
3610
3612
|
changes,
|
|
3611
3613
|
userEvent: "format.codeblock.add",
|
|
3612
3614
|
scrollIntoView: true
|
|
3613
3615
|
}));
|
|
3614
3616
|
return true;
|
|
3615
3617
|
};
|
|
3616
|
-
var removeCodeblock = ({ state, dispatch }) => {
|
|
3618
|
+
var removeCodeblock = ({ state: state2, dispatch }) => {
|
|
3617
3619
|
const changes = [];
|
|
3618
3620
|
let lastBlock = -1;
|
|
3619
|
-
for (const { from, to } of
|
|
3620
|
-
syntaxTree2(
|
|
3621
|
+
for (const { from, to } of state2.selection.ranges) {
|
|
3622
|
+
syntaxTree2(state2).iterate({
|
|
3621
3623
|
from,
|
|
3622
3624
|
to,
|
|
3623
3625
|
enter: (node) => {
|
|
3624
3626
|
if (Textblocks[node.name] === "codeblock" && lastBlock !== node.from) {
|
|
3625
3627
|
lastBlock = node.from;
|
|
3626
|
-
const firstLine =
|
|
3628
|
+
const firstLine = state2.doc.lineAt(node.from);
|
|
3627
3629
|
if (node.name === "FencedCode") {
|
|
3628
3630
|
changes.push({
|
|
3629
3631
|
from: node.from,
|
|
3630
3632
|
to: firstLine.to + 1 + node.from - firstLine.from
|
|
3631
3633
|
});
|
|
3632
|
-
const lastLine =
|
|
3634
|
+
const lastLine = state2.doc.lineAt(node.to);
|
|
3633
3635
|
if (/^([\s>]|[-*+] |\d+[).])*`+$/.test(lastLine.text)) {
|
|
3634
3636
|
changes.push({
|
|
3635
3637
|
from: lastLine.from - (lastLine.number === firstLine.number + 1 ? 0 : 1),
|
|
@@ -3638,7 +3640,7 @@ var removeCodeblock = ({ state, dispatch }) => {
|
|
|
3638
3640
|
}
|
|
3639
3641
|
} else {
|
|
3640
3642
|
const column = node.from - firstLine.from;
|
|
3641
|
-
for (let line = firstLine; ; line =
|
|
3643
|
+
for (let line = firstLine; ; line = state2.doc.line(line.number + 1)) {
|
|
3642
3644
|
changes.push({
|
|
3643
3645
|
from: line.from + column - 4,
|
|
3644
3646
|
to: line.from + column
|
|
@@ -3655,7 +3657,7 @@ var removeCodeblock = ({ state, dispatch }) => {
|
|
|
3655
3657
|
if (!changes.length) {
|
|
3656
3658
|
return false;
|
|
3657
3659
|
}
|
|
3658
|
-
dispatch(
|
|
3660
|
+
dispatch(state2.update({
|
|
3659
3661
|
changes,
|
|
3660
3662
|
userEvent: "format.codeblock.remove",
|
|
3661
3663
|
scrollIntoView: true
|
|
@@ -3714,7 +3716,7 @@ var Textblocks = {
|
|
|
3714
3716
|
TableCell: "tablecell",
|
|
3715
3717
|
Task: "paragraph"
|
|
3716
3718
|
};
|
|
3717
|
-
var getFormatting = (
|
|
3719
|
+
var getFormatting = (state2) => {
|
|
3718
3720
|
let blockType = null;
|
|
3719
3721
|
const inline = [
|
|
3720
3722
|
null,
|
|
@@ -3740,7 +3742,7 @@ var getFormatting = (state) => {
|
|
|
3740
3742
|
continue;
|
|
3741
3743
|
} else if (currentBlock.active[i]) {
|
|
3742
3744
|
inline[i] = true;
|
|
3743
|
-
} else if (/\S/.test(
|
|
3745
|
+
} else if (/\S/.test(state2.doc.sliceString(currentBlock.pos, upto))) {
|
|
3744
3746
|
inline[i] = false;
|
|
3745
3747
|
}
|
|
3746
3748
|
}
|
|
@@ -3751,12 +3753,12 @@ var getFormatting = (state) => {
|
|
|
3751
3753
|
currentBlock.pos = Math.min(upto, currentBlock.end);
|
|
3752
3754
|
}
|
|
3753
3755
|
};
|
|
3754
|
-
const { selection } =
|
|
3756
|
+
const { selection } = state2;
|
|
3755
3757
|
for (const range of selection.ranges) {
|
|
3756
3758
|
if (range.empty && inline.some((v) => v === null)) {
|
|
3757
3759
|
const contextSize = Math.min(range.head, 6);
|
|
3758
|
-
const contextBefore =
|
|
3759
|
-
let contextAfter =
|
|
3760
|
+
const contextBefore = state2.doc.sliceString(range.head - contextSize, range.head);
|
|
3761
|
+
let contextAfter = state2.doc.sliceString(range.head, range.head + contextSize);
|
|
3760
3762
|
for (let i = 0; i < contextSize; i++) {
|
|
3761
3763
|
const ch = contextAfter[i];
|
|
3762
3764
|
if (ch !== contextBefore[contextBefore.length - 1 - i] || !/[~`*]/.test(ch)) {
|
|
@@ -3775,7 +3777,7 @@ var getFormatting = (state) => {
|
|
|
3775
3777
|
}
|
|
3776
3778
|
}
|
|
3777
3779
|
}
|
|
3778
|
-
syntaxTree2(
|
|
3780
|
+
syntaxTree2(state2).iterate({
|
|
3779
3781
|
from: range.from,
|
|
3780
3782
|
to: range.to,
|
|
3781
3783
|
enter: (node) => {
|
|
@@ -3849,7 +3851,7 @@ var getFormatting = (state) => {
|
|
|
3849
3851
|
}
|
|
3850
3852
|
});
|
|
3851
3853
|
}
|
|
3852
|
-
const { from, to } =
|
|
3854
|
+
const { from, to } = state2.doc.lineAt(selection.main.anchor);
|
|
3853
3855
|
const blankLine = from === to;
|
|
3854
3856
|
return {
|
|
3855
3857
|
blankLine,
|
|
@@ -3864,17 +3866,17 @@ var getFormatting = (state) => {
|
|
|
3864
3866
|
};
|
|
3865
3867
|
};
|
|
3866
3868
|
var useFormattingState = () => {
|
|
3867
|
-
const [
|
|
3869
|
+
const [state2, setState, stateRef] = useStateRef();
|
|
3868
3870
|
const observer = useMemo(() => EditorView13.updateListener.of((update2) => {
|
|
3869
3871
|
if (update2.docChanged || update2.selectionSet) {
|
|
3870
3872
|
const newState = getFormatting(update2.state);
|
|
3871
|
-
if (!
|
|
3873
|
+
if (!stateRef.current || !formattingEquals(stateRef.current, newState)) {
|
|
3872
3874
|
setState(newState);
|
|
3873
3875
|
}
|
|
3874
3876
|
}
|
|
3875
3877
|
}), []);
|
|
3876
3878
|
return [
|
|
3877
|
-
|
|
3879
|
+
state2,
|
|
3878
3880
|
observer
|
|
3879
3881
|
];
|
|
3880
3882
|
};
|
|
@@ -3885,8 +3887,8 @@ import { StateField as StateField5 } from "@codemirror/state";
|
|
|
3885
3887
|
import { Decoration as Decoration6, EditorView as EditorView14, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
3886
3888
|
var image = (options = {}) => {
|
|
3887
3889
|
return StateField5.define({
|
|
3888
|
-
create: (
|
|
3889
|
-
return Decoration6.set(buildDecorations2(0,
|
|
3890
|
+
create: (state2) => {
|
|
3891
|
+
return Decoration6.set(buildDecorations2(0, state2.doc.length, state2));
|
|
3890
3892
|
},
|
|
3891
3893
|
update: (value, tr) => {
|
|
3892
3894
|
if (!tr.docChanged && !tr.selection) {
|
|
@@ -3920,16 +3922,16 @@ var preloadImage = (url) => {
|
|
|
3920
3922
|
preloaded.add(url);
|
|
3921
3923
|
}
|
|
3922
3924
|
};
|
|
3923
|
-
var buildDecorations2 = (from, to,
|
|
3925
|
+
var buildDecorations2 = (from, to, state2) => {
|
|
3924
3926
|
const decorations = [];
|
|
3925
|
-
const cursor =
|
|
3926
|
-
syntaxTree3(
|
|
3927
|
+
const cursor = state2.selection.main.head;
|
|
3928
|
+
syntaxTree3(state2).iterate({
|
|
3927
3929
|
enter: (node) => {
|
|
3928
3930
|
if (node.name === "Image") {
|
|
3929
3931
|
const urlNode = node.node.getChild("URL");
|
|
3930
3932
|
if (urlNode) {
|
|
3931
|
-
const hide2 =
|
|
3932
|
-
const url =
|
|
3933
|
+
const hide2 = state2.readOnly || cursor < node.from || cursor > node.to;
|
|
3934
|
+
const url = state2.sliceDoc(urlNode.from, urlNode.to);
|
|
3933
3935
|
preloadImage(url);
|
|
3934
3936
|
decorations.push(Decoration6.replace({
|
|
3935
3937
|
block: true,
|
|
@@ -3959,6 +3961,8 @@ var ImageWidget = class extends WidgetType4 {
|
|
|
3959
3961
|
return img;
|
|
3960
3962
|
}
|
|
3961
3963
|
};
|
|
3964
|
+
var imageUpload = (options = {}) => {
|
|
3965
|
+
};
|
|
3962
3966
|
|
|
3963
3967
|
// packages/ui/react-ui-editor/src/extensions/markdown/link.ts
|
|
3964
3968
|
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
@@ -4000,21 +4004,21 @@ import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "
|
|
|
4000
4004
|
import { Decoration as Decoration7, EditorView as EditorView15, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
4001
4005
|
var table = (options = {}) => {
|
|
4002
4006
|
return StateField6.define({
|
|
4003
|
-
create: (
|
|
4007
|
+
create: (state2) => update(state2, options),
|
|
4004
4008
|
update: (_, tr) => update(tr.state, options),
|
|
4005
4009
|
provide: (field) => EditorView15.decorations.from(field)
|
|
4006
4010
|
});
|
|
4007
4011
|
};
|
|
4008
|
-
var update = (
|
|
4012
|
+
var update = (state2, options) => {
|
|
4009
4013
|
const builder = new RangeSetBuilder3();
|
|
4010
|
-
const cursor =
|
|
4014
|
+
const cursor = state2.selection.main.head;
|
|
4011
4015
|
const tables = [];
|
|
4012
4016
|
const getTable = () => tables[tables.length - 1];
|
|
4013
4017
|
const getRow = () => {
|
|
4014
4018
|
const table2 = getTable();
|
|
4015
4019
|
return table2.rows?.[table2.rows.length - 1];
|
|
4016
4020
|
};
|
|
4017
|
-
syntaxTree5(
|
|
4021
|
+
syntaxTree5(state2).iterate({
|
|
4018
4022
|
enter: (node) => {
|
|
4019
4023
|
switch (node.name) {
|
|
4020
4024
|
case "Table": {
|
|
@@ -4035,9 +4039,9 @@ var update = (state, options) => {
|
|
|
4035
4039
|
case "TableCell": {
|
|
4036
4040
|
const row = getRow();
|
|
4037
4041
|
if (row) {
|
|
4038
|
-
row.push(
|
|
4042
|
+
row.push(state2.sliceDoc(node.from, node.to));
|
|
4039
4043
|
} else {
|
|
4040
|
-
getTable().header?.push(
|
|
4044
|
+
getTable().header?.push(state2.sliceDoc(node.from, node.to));
|
|
4041
4045
|
}
|
|
4042
4046
|
break;
|
|
4043
4047
|
}
|
|
@@ -4045,7 +4049,7 @@ var update = (state, options) => {
|
|
|
4045
4049
|
}
|
|
4046
4050
|
});
|
|
4047
4051
|
tables.forEach((table2) => {
|
|
4048
|
-
const hide2 =
|
|
4052
|
+
const hide2 = state2.readOnly || cursor < table2.from || cursor > table2.to;
|
|
4049
4053
|
if (hide2) {
|
|
4050
4054
|
builder.add(table2.from, table2.to, Decoration7.replace({
|
|
4051
4055
|
widget: new TableWidget(table2)
|
|
@@ -4136,6 +4140,7 @@ var mention = ({ onSearch }) => {
|
|
|
4136
4140
|
import { Facet as Facet6 } from "@codemirror/state";
|
|
4137
4141
|
import { keymap as keymap7 } from "@codemirror/view";
|
|
4138
4142
|
import { vim } from "@replit/codemirror-vim";
|
|
4143
|
+
var focusEvent = "focus.container";
|
|
4139
4144
|
var editorMode = Facet6.define({
|
|
4140
4145
|
combine: (modes) => modes[0] ?? {}
|
|
4141
4146
|
});
|
|
@@ -4152,7 +4157,7 @@ var EditorModes = {
|
|
|
4152
4157
|
key: "Alt-Escape",
|
|
4153
4158
|
run: (view) => {
|
|
4154
4159
|
view.dispatch({
|
|
4155
|
-
userEvent:
|
|
4160
|
+
userEvent: focusEvent
|
|
4156
4161
|
});
|
|
4157
4162
|
return true;
|
|
4158
4163
|
}
|
|
@@ -4161,8 +4166,95 @@ var EditorModes = {
|
|
|
4161
4166
|
]
|
|
4162
4167
|
};
|
|
4163
4168
|
|
|
4169
|
+
// packages/ui/react-ui-editor/src/extensions/state.ts
|
|
4170
|
+
import { Transaction as Transaction2 } from "@codemirror/state";
|
|
4171
|
+
import { EditorView as EditorView16, keymap as keymap8 } from "@codemirror/view";
|
|
4172
|
+
import { debounce as debounce2 } from "@dxos/async";
|
|
4173
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
4174
|
+
import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
|
|
4175
|
+
var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/state.ts";
|
|
4176
|
+
var scrollAnnotation = "dxos.org/cm/scrolling";
|
|
4177
|
+
var keyPrefix = "dxos.org/react-ui-editor/state";
|
|
4178
|
+
var localStorageStateStoreAdapter = {
|
|
4179
|
+
setState: (id, state2) => {
|
|
4180
|
+
invariant4(id, void 0, {
|
|
4181
|
+
F: __dxlog_file11,
|
|
4182
|
+
L: 35,
|
|
4183
|
+
S: void 0,
|
|
4184
|
+
A: [
|
|
4185
|
+
"id",
|
|
4186
|
+
""
|
|
4187
|
+
]
|
|
4188
|
+
});
|
|
4189
|
+
localStorage.setItem(`${keyPrefix}/${id}`, JSON.stringify(state2));
|
|
4190
|
+
},
|
|
4191
|
+
getState: (id) => {
|
|
4192
|
+
invariant4(id, void 0, {
|
|
4193
|
+
F: __dxlog_file11,
|
|
4194
|
+
L: 39,
|
|
4195
|
+
S: void 0,
|
|
4196
|
+
A: [
|
|
4197
|
+
"id",
|
|
4198
|
+
""
|
|
4199
|
+
]
|
|
4200
|
+
});
|
|
4201
|
+
const state2 = localStorage.getItem(`${keyPrefix}/${id}`);
|
|
4202
|
+
return state2 ? JSON.parse(state2) : void 0;
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
4205
|
+
var state = ({ getState, setState } = {}) => {
|
|
4206
|
+
const setStateDebounced = debounce2(setState, 1e3);
|
|
4207
|
+
return [
|
|
4208
|
+
// TODO(burdon): Track scrolling (currently only updates when cursor moves).
|
|
4209
|
+
EditorView16.updateListener.of(({ view, changes, transactions }) => {
|
|
4210
|
+
const id = view.state.facet(documentId2);
|
|
4211
|
+
if (!id || transactions.some((tr) => tr.isUserEvent(scrollAnnotation))) {
|
|
4212
|
+
return;
|
|
4213
|
+
}
|
|
4214
|
+
if (setState) {
|
|
4215
|
+
const { top } = view.dom.getBoundingClientRect();
|
|
4216
|
+
const pos = view.posAtCoords({
|
|
4217
|
+
x: 0,
|
|
4218
|
+
y: top
|
|
4219
|
+
});
|
|
4220
|
+
if (pos !== null) {
|
|
4221
|
+
const { anchor, head } = view.state.selection.main;
|
|
4222
|
+
setStateDebounced(id, {
|
|
4223
|
+
scrollTo: {
|
|
4224
|
+
from: pos,
|
|
4225
|
+
yMargin: 0
|
|
4226
|
+
},
|
|
4227
|
+
selection: {
|
|
4228
|
+
anchor,
|
|
4229
|
+
head
|
|
4230
|
+
}
|
|
4231
|
+
});
|
|
4232
|
+
}
|
|
4233
|
+
}
|
|
4234
|
+
}),
|
|
4235
|
+
getState && keymap8.of([
|
|
4236
|
+
{
|
|
4237
|
+
key: "ctrl-r",
|
|
4238
|
+
run: (view) => {
|
|
4239
|
+
const state2 = getState(view.state.facet(documentId2));
|
|
4240
|
+
if (state2) {
|
|
4241
|
+
view.dispatch({
|
|
4242
|
+
effects: EditorView16.scrollIntoView(state2.scrollTo.from, {
|
|
4243
|
+
yMargin: 0
|
|
4244
|
+
}),
|
|
4245
|
+
selection: state2.selection,
|
|
4246
|
+
annotations: Transaction2.userEvent.of(scrollAnnotation)
|
|
4247
|
+
});
|
|
4248
|
+
}
|
|
4249
|
+
return true;
|
|
4250
|
+
}
|
|
4251
|
+
}
|
|
4252
|
+
])
|
|
4253
|
+
].filter(isNotFalsy3);
|
|
4254
|
+
};
|
|
4255
|
+
|
|
4164
4256
|
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
4165
|
-
import { keymap as
|
|
4257
|
+
import { keymap as keymap9 } from "@codemirror/view";
|
|
4166
4258
|
var defaultItems = [
|
|
4167
4259
|
"hello world!",
|
|
4168
4260
|
"this is a test.",
|
|
@@ -4172,7 +4264,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
|
4172
4264
|
let t;
|
|
4173
4265
|
let idx = 0;
|
|
4174
4266
|
return [
|
|
4175
|
-
|
|
4267
|
+
keymap9.of([
|
|
4176
4268
|
{
|
|
4177
4269
|
// Reset.
|
|
4178
4270
|
key: "alt-meta-'",
|
|
@@ -4219,15 +4311,17 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
|
4219
4311
|
};
|
|
4220
4312
|
|
|
4221
4313
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
4222
|
-
var
|
|
4314
|
+
var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
|
|
4223
4315
|
var instanceCount = 0;
|
|
4224
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo =
|
|
4225
|
-
|
|
4316
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, className, autoFocus, scrollTo = EditorView17.scrollIntoView(0, {
|
|
4317
|
+
yMargin: 0
|
|
4318
|
+
}), moveToEndOfLine, debug, dataTestId }, forwardedRef) => {
|
|
4319
|
+
const [instanceId] = useState(() => `text-editor-${++instanceCount}`);
|
|
4226
4320
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
4227
4321
|
tabBehavior: "limited"
|
|
4228
4322
|
});
|
|
4229
4323
|
const rootRef = useRef(null);
|
|
4230
|
-
const [view, setView] =
|
|
4324
|
+
const [view, setView] = useState(null);
|
|
4231
4325
|
useImperativeHandle(forwardedRef, () => view, [
|
|
4232
4326
|
view
|
|
4233
4327
|
]);
|
|
@@ -4240,42 +4334,42 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
|
|
|
4240
4334
|
autoFocus
|
|
4241
4335
|
]);
|
|
4242
4336
|
useEffect2(() => {
|
|
4243
|
-
log9("
|
|
4337
|
+
log9("create", {
|
|
4244
4338
|
id,
|
|
4245
4339
|
instanceId
|
|
4246
4340
|
}, {
|
|
4247
|
-
F:
|
|
4341
|
+
F: __dxlog_file12,
|
|
4248
4342
|
L: 88,
|
|
4249
4343
|
S: void 0,
|
|
4250
4344
|
C: (f, a) => f(...a)
|
|
4251
4345
|
});
|
|
4252
|
-
const
|
|
4346
|
+
const state2 = EditorState2.create({
|
|
4253
4347
|
doc,
|
|
4254
4348
|
selection,
|
|
4255
4349
|
extensions: [
|
|
4256
4350
|
id && documentId2.of(id),
|
|
4257
4351
|
// TODO(burdon): NOTE: Doesn't catch errors in keymap functions.
|
|
4258
|
-
|
|
4352
|
+
EditorView17.exceptionSink.of((err) => {
|
|
4259
4353
|
log9.catch(err, void 0, {
|
|
4260
|
-
F:
|
|
4354
|
+
F: __dxlog_file12,
|
|
4261
4355
|
L: 101,
|
|
4262
4356
|
S: void 0,
|
|
4263
4357
|
C: (f, a) => f(...a)
|
|
4264
4358
|
});
|
|
4265
4359
|
}),
|
|
4266
4360
|
// Focus.
|
|
4267
|
-
|
|
4361
|
+
EditorView17.updateListener.of((update2) => {
|
|
4268
4362
|
update2.transactions.forEach((transaction) => {
|
|
4269
|
-
if (transaction.isUserEvent(
|
|
4363
|
+
if (transaction.isUserEvent(focusEvent)) {
|
|
4270
4364
|
rootRef.current?.focus();
|
|
4271
4365
|
}
|
|
4272
4366
|
});
|
|
4273
4367
|
}),
|
|
4274
4368
|
extensions
|
|
4275
|
-
].filter(
|
|
4369
|
+
].filter(isNotFalsy4)
|
|
4276
4370
|
});
|
|
4277
|
-
const view2 = new
|
|
4278
|
-
state,
|
|
4371
|
+
const view2 = new EditorView17({
|
|
4372
|
+
state: state2,
|
|
4279
4373
|
parent: rootRef.current,
|
|
4280
4374
|
scrollTo,
|
|
4281
4375
|
// NOTE: Uncomment to debug/monitor all transactions.
|
|
@@ -4287,21 +4381,29 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
|
|
|
4287
4381
|
view3.update(trs);
|
|
4288
4382
|
}
|
|
4289
4383
|
});
|
|
4290
|
-
|
|
4291
|
-
if (moveToEndOfLine) {
|
|
4384
|
+
if (moveToEndOfLine && !(scrollTo || selection)) {
|
|
4292
4385
|
const { to } = view2.state.doc.lineAt(0);
|
|
4293
|
-
view2
|
|
4386
|
+
view2.dispatch({
|
|
4294
4387
|
selection: {
|
|
4295
4388
|
anchor: to
|
|
4296
4389
|
}
|
|
4297
4390
|
});
|
|
4298
4391
|
}
|
|
4299
|
-
if (
|
|
4392
|
+
if (state2.facet(editorMode).noTabster) {
|
|
4300
4393
|
rootRef.current?.removeAttribute("data-tabster");
|
|
4301
4394
|
}
|
|
4395
|
+
setView(view2);
|
|
4302
4396
|
return () => {
|
|
4397
|
+
log9("destroy", {
|
|
4398
|
+
id,
|
|
4399
|
+
instanceId
|
|
4400
|
+
}, {
|
|
4401
|
+
F: __dxlog_file12,
|
|
4402
|
+
L: 150,
|
|
4403
|
+
S: void 0,
|
|
4404
|
+
C: (f, a) => f(...a)
|
|
4405
|
+
});
|
|
4303
4406
|
view2?.destroy();
|
|
4304
|
-
setView(null);
|
|
4305
4407
|
};
|
|
4306
4408
|
}, [
|
|
4307
4409
|
doc,
|
|
@@ -4331,9 +4433,10 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ id, doc, selection, extensions, c
|
|
|
4331
4433
|
});
|
|
4332
4434
|
|
|
4333
4435
|
// packages/ui/react-ui-editor/src/components/Toolbar/Toolbar.tsx
|
|
4334
|
-
import { ChatText, Code, CodeBlock, Link, ListBullets, ListChecks, ListNumbers, Paragraph, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic
|
|
4436
|
+
import { ChatText, Code, CodeBlock, Image, Link, ListBullets, ListChecks, ListNumbers, Paragraph, Quotes, TextStrikethrough, Table as Table2, TextB, TextHOne, TextHTwo, TextHThree, TextHFour, TextHFive, TextHSix, TextItalic } from "@phosphor-icons/react";
|
|
4335
4437
|
import { createContext } from "@radix-ui/react-context";
|
|
4336
|
-
import React2, { useRef as useRef2, useState as
|
|
4438
|
+
import React2, { useEffect as useEffect3, useRef as useRef2, useState as useState2 } from "react";
|
|
4439
|
+
import { useDropzone } from "react-dropzone";
|
|
4337
4440
|
import { DensityProvider, ElevationProvider, Select, Toolbar as NaturalToolbar, Tooltip, useTranslation } from "@dxos/react-ui";
|
|
4338
4441
|
import { getSize } from "@dxos/react-ui-theme";
|
|
4339
4442
|
var tooltipProps = {
|
|
@@ -4350,10 +4453,10 @@ var HeadingIcons = {
|
|
|
4350
4453
|
"6": TextHSix
|
|
4351
4454
|
};
|
|
4352
4455
|
var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
|
|
4353
|
-
var ToolbarRoot = ({ children, onAction, classNames, state }) => {
|
|
4456
|
+
var ToolbarRoot = ({ children, onAction, classNames, state: state2 }) => {
|
|
4354
4457
|
return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
|
|
4355
4458
|
onAction,
|
|
4356
|
-
state
|
|
4459
|
+
state: state2
|
|
4357
4460
|
}, /* @__PURE__ */ React2.createElement(DensityProvider, {
|
|
4358
4461
|
density: "fine"
|
|
4359
4462
|
}, /* @__PURE__ */ React2.createElement(ElevationProvider, {
|
|
@@ -4367,7 +4470,7 @@ var ToolbarRoot = ({ children, onAction, classNames, state }) => {
|
|
|
4367
4470
|
};
|
|
4368
4471
|
var buttonStyles = "min-bs-0 p-2";
|
|
4369
4472
|
var iconStyles = getSize(5);
|
|
4370
|
-
var
|
|
4473
|
+
var ToolbarToggleButton = ({ Icon, children, ...props }) => {
|
|
4371
4474
|
return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
|
|
4372
4475
|
asChild: true
|
|
4373
4476
|
}, /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroupItem, {
|
|
@@ -4380,20 +4483,33 @@ var ToolbarButton = ({ Icon, children, ...props }) => {
|
|
|
4380
4483
|
className: "sr-only"
|
|
4381
4484
|
}, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
|
|
4382
4485
|
};
|
|
4486
|
+
var ToolbarButton = ({ Icon, children, ...props }) => {
|
|
4487
|
+
return /* @__PURE__ */ React2.createElement(Tooltip.Root, null, /* @__PURE__ */ React2.createElement(Tooltip.Trigger, {
|
|
4488
|
+
asChild: true
|
|
4489
|
+
}, /* @__PURE__ */ React2.createElement(NaturalToolbar.Button, {
|
|
4490
|
+
variant: "ghost",
|
|
4491
|
+
...props,
|
|
4492
|
+
classNames: buttonStyles
|
|
4493
|
+
}, /* @__PURE__ */ React2.createElement(Icon, {
|
|
4494
|
+
className: iconStyles
|
|
4495
|
+
}), /* @__PURE__ */ React2.createElement("span", {
|
|
4496
|
+
className: "sr-only"
|
|
4497
|
+
}, children))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, children, /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
|
|
4498
|
+
};
|
|
4383
4499
|
var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
|
|
4384
4500
|
role: "separator",
|
|
4385
4501
|
className: "grow"
|
|
4386
4502
|
});
|
|
4387
4503
|
var MarkdownHeading = () => {
|
|
4388
4504
|
const { t } = useTranslation(translationKey);
|
|
4389
|
-
const { onAction, state } = useToolbarContext("MarkdownFormatting");
|
|
4390
|
-
const blockType =
|
|
4505
|
+
const { onAction, state: state2 } = useToolbarContext("MarkdownFormatting");
|
|
4506
|
+
const blockType = state2 ? state2.blockType : "paragraph";
|
|
4391
4507
|
const header = blockType && /heading(\d)/.exec(blockType);
|
|
4392
4508
|
const value = header ? header[1] : blockType === "paragraph" || !blockType ? "0" : void 0;
|
|
4393
4509
|
const HeadingIcon = HeadingIcons[value ?? "0"];
|
|
4394
4510
|
const suppressNextTooltip = useRef2(false);
|
|
4395
|
-
const [tooltipOpen, setTooltipOpen] =
|
|
4396
|
-
const [selectOpen, setSelectOpen] =
|
|
4511
|
+
const [tooltipOpen, setTooltipOpen] = useState2(false);
|
|
4512
|
+
const [selectOpen, setSelectOpen] = useState2(false);
|
|
4397
4513
|
return /* @__PURE__ */ React2.createElement(Tooltip.Root, {
|
|
4398
4514
|
open: tooltipOpen,
|
|
4399
4515
|
onOpenChange: (nextOpen) => {
|
|
@@ -4461,43 +4577,43 @@ var markdownStyles = [
|
|
|
4461
4577
|
{
|
|
4462
4578
|
type: "strong",
|
|
4463
4579
|
Icon: TextB,
|
|
4464
|
-
getState: (
|
|
4580
|
+
getState: (state2) => state2.strong
|
|
4465
4581
|
},
|
|
4466
4582
|
{
|
|
4467
4583
|
type: "emphasis",
|
|
4468
4584
|
Icon: TextItalic,
|
|
4469
|
-
getState: (
|
|
4585
|
+
getState: (state2) => state2.emphasis
|
|
4470
4586
|
},
|
|
4471
4587
|
{
|
|
4472
4588
|
type: "strikethrough",
|
|
4473
4589
|
Icon: TextStrikethrough,
|
|
4474
|
-
getState: (
|
|
4590
|
+
getState: (state2) => state2.strikethrough
|
|
4475
4591
|
},
|
|
4476
4592
|
{
|
|
4477
4593
|
type: "code",
|
|
4478
4594
|
Icon: Code,
|
|
4479
|
-
getState: (
|
|
4595
|
+
getState: (state2) => state2.code
|
|
4480
4596
|
},
|
|
4481
4597
|
{
|
|
4482
4598
|
type: "link",
|
|
4483
4599
|
Icon: Link,
|
|
4484
|
-
getState: (
|
|
4600
|
+
getState: (state2) => state2.link
|
|
4485
4601
|
}
|
|
4486
4602
|
];
|
|
4487
4603
|
var MarkdownStyles = () => {
|
|
4488
|
-
const { onAction, state } = useToolbarContext("MarkdownStyles");
|
|
4604
|
+
const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
|
|
4489
4605
|
const { t } = useTranslation(translationKey);
|
|
4490
4606
|
return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
|
|
4491
4607
|
type: "multiple",
|
|
4492
|
-
value: markdownStyles.filter(({ getState }) =>
|
|
4493
|
-
}, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(
|
|
4608
|
+
value: markdownStyles.filter(({ getState }) => state2 && getState(state2)).map(({ type }) => type)
|
|
4609
|
+
}, markdownStyles.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
|
|
4494
4610
|
key: type,
|
|
4495
4611
|
value: type,
|
|
4496
4612
|
Icon,
|
|
4497
|
-
disabled:
|
|
4498
|
-
onClick:
|
|
4613
|
+
disabled: state2?.blockType === "codeblock",
|
|
4614
|
+
onClick: state2 ? () => onAction?.({
|
|
4499
4615
|
type,
|
|
4500
|
-
data: !getState(
|
|
4616
|
+
data: !getState(state2)
|
|
4501
4617
|
}) : void 0
|
|
4502
4618
|
}, t(`${type} label`))));
|
|
4503
4619
|
};
|
|
@@ -4505,32 +4621,32 @@ var markdownLists = [
|
|
|
4505
4621
|
{
|
|
4506
4622
|
type: "list-bullet",
|
|
4507
4623
|
Icon: ListBullets,
|
|
4508
|
-
getState: (
|
|
4624
|
+
getState: (state2) => state2.listStyle === "bullet"
|
|
4509
4625
|
},
|
|
4510
4626
|
{
|
|
4511
4627
|
type: "list-ordered",
|
|
4512
4628
|
Icon: ListNumbers,
|
|
4513
|
-
getState: (
|
|
4629
|
+
getState: (state2) => state2.listStyle === "ordered"
|
|
4514
4630
|
},
|
|
4515
4631
|
{
|
|
4516
4632
|
type: "list-task",
|
|
4517
4633
|
Icon: ListChecks,
|
|
4518
|
-
getState: (
|
|
4634
|
+
getState: (state2) => state2.listStyle === "task"
|
|
4519
4635
|
}
|
|
4520
4636
|
];
|
|
4521
4637
|
var MarkdownLists = () => {
|
|
4522
|
-
const { onAction, state } = useToolbarContext("MarkdownStyles");
|
|
4638
|
+
const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
|
|
4523
4639
|
const { t } = useTranslation(translationKey);
|
|
4524
4640
|
return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
|
|
4525
4641
|
type: "single",
|
|
4526
|
-
value:
|
|
4527
|
-
}, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(
|
|
4642
|
+
value: state2?.listStyle ? `list-${state2.listStyle}` : ""
|
|
4643
|
+
}, markdownLists.map(({ type, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
|
|
4528
4644
|
key: type,
|
|
4529
4645
|
value: type,
|
|
4530
4646
|
Icon,
|
|
4531
|
-
onClick:
|
|
4647
|
+
onClick: state2 ? () => onAction?.({
|
|
4532
4648
|
type,
|
|
4533
|
-
data: !getState(
|
|
4649
|
+
data: !getState(state2)
|
|
4534
4650
|
}) : void 0
|
|
4535
4651
|
}, t(`${type} label`))));
|
|
4536
4652
|
};
|
|
@@ -4538,63 +4654,101 @@ var markdownBlocks = [
|
|
|
4538
4654
|
{
|
|
4539
4655
|
type: "blockquote",
|
|
4540
4656
|
Icon: Quotes,
|
|
4541
|
-
getState: (
|
|
4657
|
+
getState: (state2) => state2.blockQuote
|
|
4542
4658
|
},
|
|
4543
4659
|
{
|
|
4544
4660
|
type: "codeblock",
|
|
4545
4661
|
Icon: CodeBlock,
|
|
4546
|
-
getState: (
|
|
4662
|
+
getState: (state2) => state2.blockType === "codeblock"
|
|
4547
4663
|
},
|
|
4548
4664
|
{
|
|
4549
4665
|
type: "table",
|
|
4550
4666
|
Icon: Table2,
|
|
4551
|
-
getState: (
|
|
4552
|
-
disabled: (
|
|
4667
|
+
getState: (state2) => state2.blockType === "tablecell",
|
|
4668
|
+
disabled: (state2) => !state2.blankLine
|
|
4553
4669
|
}
|
|
4554
4670
|
];
|
|
4555
4671
|
var MarkdownBlocks = () => {
|
|
4556
|
-
const { onAction, state } = useToolbarContext("MarkdownStyles");
|
|
4672
|
+
const { onAction, state: state2 } = useToolbarContext("MarkdownStyles");
|
|
4557
4673
|
const { t } = useTranslation(translationKey);
|
|
4558
|
-
const value = markdownBlocks.find(({ getState }) =>
|
|
4674
|
+
const value = markdownBlocks.find(({ getState }) => state2 && getState(state2));
|
|
4559
4675
|
return /* @__PURE__ */ React2.createElement(NaturalToolbar.ToggleGroup, {
|
|
4560
4676
|
type: "single",
|
|
4561
4677
|
value: value?.type ?? ""
|
|
4562
|
-
}, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(
|
|
4678
|
+
}, markdownBlocks.map(({ type, disabled, getState, Icon }) => /* @__PURE__ */ React2.createElement(ToolbarToggleButton, {
|
|
4563
4679
|
key: type,
|
|
4564
4680
|
value: type,
|
|
4565
4681
|
Icon,
|
|
4566
|
-
disabled: !
|
|
4567
|
-
onClick:
|
|
4682
|
+
disabled: !state2 || disabled?.(state2),
|
|
4683
|
+
onClick: state2 ? () => onAction?.({
|
|
4568
4684
|
type,
|
|
4569
|
-
data: !getState(
|
|
4685
|
+
data: !getState(state2)
|
|
4570
4686
|
}) : void 0
|
|
4571
4687
|
}, t(`${type} label`))));
|
|
4572
4688
|
};
|
|
4573
4689
|
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));
|
|
4574
|
-
var
|
|
4690
|
+
var MarkdownCustom = ({ onUpload } = {}) => {
|
|
4575
4691
|
const { onAction } = useToolbarContext("MarkdownStyles");
|
|
4576
4692
|
const { t } = useTranslation(translationKey);
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4693
|
+
const { acceptedFiles, getInputProps, open } = useDropzone({
|
|
4694
|
+
multiple: false,
|
|
4695
|
+
noDrag: true,
|
|
4696
|
+
accept: {
|
|
4697
|
+
"image/*": [
|
|
4698
|
+
".jpg",
|
|
4699
|
+
".jpeg",
|
|
4700
|
+
".png",
|
|
4701
|
+
".gif"
|
|
4702
|
+
]
|
|
4703
|
+
}
|
|
4704
|
+
});
|
|
4705
|
+
useEffect3(() => {
|
|
4706
|
+
if (onUpload && acceptedFiles.length) {
|
|
4707
|
+
setTimeout(async () => {
|
|
4708
|
+
const f = acceptedFiles[0];
|
|
4709
|
+
const file = new File([
|
|
4710
|
+
f
|
|
4711
|
+
], f.name, {
|
|
4712
|
+
type: f.type,
|
|
4713
|
+
lastModified: f.lastModified
|
|
4714
|
+
});
|
|
4715
|
+
const { url } = await onUpload(file);
|
|
4716
|
+
if (url) {
|
|
4717
|
+
onAction?.({
|
|
4718
|
+
type: "image",
|
|
4719
|
+
data: url
|
|
4720
|
+
});
|
|
4721
|
+
}
|
|
4722
|
+
});
|
|
4723
|
+
}
|
|
4724
|
+
}, [
|
|
4725
|
+
acceptedFiles
|
|
4726
|
+
]);
|
|
4727
|
+
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement("input", getInputProps()), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
4728
|
+
value: "image",
|
|
4729
|
+
Icon: Image,
|
|
4730
|
+
onClick: () => open()
|
|
4731
|
+
}, t("image label")));
|
|
4732
|
+
};
|
|
4733
|
+
var MarkdownActions = () => {
|
|
4734
|
+
const { onAction } = useToolbarContext("MarkdownStyles");
|
|
4735
|
+
const { t } = useTranslation(translationKey);
|
|
4736
|
+
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
4737
|
+
value: "comment",
|
|
4738
|
+
Icon: ChatText,
|
|
4581
4739
|
"data-testid": "editor.toolbar.comment",
|
|
4582
4740
|
onClick: () => onAction?.({
|
|
4583
4741
|
type: "comment"
|
|
4584
|
-
})
|
|
4585
|
-
|
|
4586
|
-
}, /* @__PURE__ */ React2.createElement(ChatText, {
|
|
4587
|
-
className: iconStyles
|
|
4588
|
-
}), /* @__PURE__ */ React2.createElement("span", {
|
|
4589
|
-
className: "sr-only"
|
|
4590
|
-
}, t("comment label")))), /* @__PURE__ */ React2.createElement(Tooltip.Portal, null, /* @__PURE__ */ React2.createElement(Tooltip.Content, tooltipProps, t("comment label"), /* @__PURE__ */ React2.createElement(Tooltip.Arrow, null))));
|
|
4742
|
+
})
|
|
4743
|
+
}, t("comment label")));
|
|
4591
4744
|
};
|
|
4592
4745
|
var Toolbar = {
|
|
4593
4746
|
Root: ToolbarRoot,
|
|
4594
|
-
Button:
|
|
4747
|
+
Button: ToolbarToggleButton,
|
|
4595
4748
|
Separator: ToolbarSeparator,
|
|
4596
4749
|
Markdown: MarkdownStandard,
|
|
4597
|
-
|
|
4750
|
+
Custom: MarkdownCustom,
|
|
4751
|
+
Actions: MarkdownActions
|
|
4598
4752
|
};
|
|
4599
4753
|
|
|
4600
4754
|
// packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
|
|
@@ -4631,7 +4785,13 @@ var useActionHandler = (view) => {
|
|
|
4631
4785
|
insertTable(view);
|
|
4632
4786
|
break;
|
|
4633
4787
|
case "link":
|
|
4634
|
-
(action.data === false ? removeLink : addLink)(view);
|
|
4788
|
+
(action.data === false ? removeLink : addLink())(view);
|
|
4789
|
+
break;
|
|
4790
|
+
case "image":
|
|
4791
|
+
addLink({
|
|
4792
|
+
url: action.data,
|
|
4793
|
+
image: true
|
|
4794
|
+
})(view);
|
|
4635
4795
|
break;
|
|
4636
4796
|
case "comment":
|
|
4637
4797
|
createComment(view);
|
|
@@ -4660,43 +4820,51 @@ var useDocAccessor = (text) => {
|
|
|
4660
4820
|
|
|
4661
4821
|
// packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
|
|
4662
4822
|
import { EditorSelection as EditorSelection2, EditorState as EditorState3 } from "@codemirror/state";
|
|
4663
|
-
import { EditorView as
|
|
4664
|
-
import { useEffect as
|
|
4823
|
+
import { EditorView as EditorView18 } from "@codemirror/view";
|
|
4824
|
+
import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState3 } from "react";
|
|
4665
4825
|
import { log as log10 } from "@dxos/log";
|
|
4666
|
-
import { isNotFalsy as
|
|
4667
|
-
var
|
|
4826
|
+
import { isNotFalsy as isNotFalsy5 } from "@dxos/util";
|
|
4827
|
+
var __dxlog_file13 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
|
|
4668
4828
|
var useTextEditor = (cb = () => ({}), deps = []) => {
|
|
4669
4829
|
let { id, doc, selection, extensions, autoFocus, scrollTo, debug } = useMemo3(cb, deps ?? []);
|
|
4670
4830
|
const onUpdate = useRef3();
|
|
4671
|
-
const [view, setView] =
|
|
4831
|
+
const [view, setView] = useState3();
|
|
4672
4832
|
const parentRef = useRef3(null);
|
|
4673
|
-
|
|
4833
|
+
useEffect4(() => {
|
|
4674
4834
|
let view2;
|
|
4675
4835
|
if (parentRef.current) {
|
|
4676
|
-
|
|
4836
|
+
log10("create", {
|
|
4837
|
+
id
|
|
4838
|
+
}, {
|
|
4839
|
+
F: __dxlog_file13,
|
|
4840
|
+
L: 36,
|
|
4841
|
+
S: void 0,
|
|
4842
|
+
C: (f, a) => f(...a)
|
|
4843
|
+
});
|
|
4844
|
+
const state2 = EditorState3.create({
|
|
4677
4845
|
doc,
|
|
4678
4846
|
selection,
|
|
4679
4847
|
extensions: [
|
|
4680
4848
|
id && documentId2.of(id),
|
|
4681
4849
|
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
4682
|
-
|
|
4850
|
+
EditorView18.exceptionSink.of((err) => {
|
|
4683
4851
|
log10.catch(err, void 0, {
|
|
4684
|
-
F:
|
|
4685
|
-
L:
|
|
4852
|
+
F: __dxlog_file13,
|
|
4853
|
+
L: 46,
|
|
4686
4854
|
S: void 0,
|
|
4687
4855
|
C: (f, a) => f(...a)
|
|
4688
4856
|
});
|
|
4689
4857
|
}),
|
|
4690
4858
|
extensions,
|
|
4691
|
-
|
|
4859
|
+
EditorView18.updateListener.of(() => {
|
|
4692
4860
|
onUpdate.current?.();
|
|
4693
4861
|
})
|
|
4694
|
-
].filter(
|
|
4862
|
+
].filter(isNotFalsy5)
|
|
4695
4863
|
});
|
|
4696
|
-
view2 = new
|
|
4864
|
+
view2 = new EditorView18({
|
|
4697
4865
|
parent: parentRef.current,
|
|
4698
4866
|
scrollTo,
|
|
4699
|
-
state,
|
|
4867
|
+
state: state2,
|
|
4700
4868
|
// NOTE: Uncomment to debug/monitor all transactions.
|
|
4701
4869
|
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
4702
4870
|
dispatchTransactions: (trs, view3) => {
|
|
@@ -4709,10 +4877,18 @@ var useTextEditor = (cb = () => ({}), deps = []) => {
|
|
|
4709
4877
|
setView(view2);
|
|
4710
4878
|
}
|
|
4711
4879
|
return () => {
|
|
4880
|
+
log10("destroy", {
|
|
4881
|
+
id
|
|
4882
|
+
}, {
|
|
4883
|
+
F: __dxlog_file13,
|
|
4884
|
+
L: 74,
|
|
4885
|
+
S: void 0,
|
|
4886
|
+
C: (f, a) => f(...a)
|
|
4887
|
+
});
|
|
4712
4888
|
view2?.destroy();
|
|
4713
4889
|
};
|
|
4714
4890
|
}, deps);
|
|
4715
|
-
|
|
4891
|
+
useEffect4(() => {
|
|
4716
4892
|
if (view) {
|
|
4717
4893
|
if (!selection && !view.state.selection.main.anchor) {
|
|
4718
4894
|
selection = EditorSelection2.single(view.state.doc.line(1).to);
|
|
@@ -4771,7 +4947,6 @@ export {
|
|
|
4771
4947
|
callbackWrapper,
|
|
4772
4948
|
command,
|
|
4773
4949
|
comments,
|
|
4774
|
-
compareFormatting,
|
|
4775
4950
|
createBasicExtensions,
|
|
4776
4951
|
createComment,
|
|
4777
4952
|
createDataExtensions,
|
|
@@ -4788,14 +4963,18 @@ export {
|
|
|
4788
4963
|
editorMode,
|
|
4789
4964
|
editorWithToolbarLayout,
|
|
4790
4965
|
focusComment,
|
|
4966
|
+
focusEvent,
|
|
4967
|
+
formattingEquals,
|
|
4791
4968
|
formattingKeymap,
|
|
4792
4969
|
getFormatting,
|
|
4793
4970
|
getToken,
|
|
4794
4971
|
image,
|
|
4972
|
+
imageUpload,
|
|
4795
4973
|
insertTable,
|
|
4796
|
-
|
|
4974
|
+
keymap10 as keymap,
|
|
4797
4975
|
linkTooltip,
|
|
4798
4976
|
listener,
|
|
4977
|
+
localStorageStateStoreAdapter,
|
|
4799
4978
|
logChanges,
|
|
4800
4979
|
markdownHighlightStyle,
|
|
4801
4980
|
markdownTags,
|
|
@@ -4811,6 +4990,7 @@ export {
|
|
|
4811
4990
|
setHeading,
|
|
4812
4991
|
setSelection,
|
|
4813
4992
|
setStyle,
|
|
4993
|
+
state,
|
|
4814
4994
|
table,
|
|
4815
4995
|
tags2 as tags,
|
|
4816
4996
|
toggleBlockquote,
|