@dxos/react-ui-editor 0.3.11-main.4feb9c9 → 0.3.11-main.5052d1a
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 +1662 -1088
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +13 -15
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +14 -6
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/{yjs.stories.d.ts → hooks.stories.d.ts} +13 -2
- package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +25 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +23 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/index.d.ts +2 -0
- package/dist/types/src/components/Toolbar/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +1 -0
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
- package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/defs.d.ts +9 -11
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/index.d.ts +0 -1
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/semaphore.d.ts +8 -12
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness.d.ts +2 -2
- package/dist/types/src/extensions/awareness.d.ts.map +1 -1
- package/dist/types/src/extensions/basic.d.ts +4 -5
- package/dist/types/src/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +35 -18
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/cursor.d.ts +3 -3
- package/dist/types/src/extensions/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +1 -7
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts +13 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
- package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts +8 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner.d.ts +4 -0
- package/dist/types/src/extensions/outliner.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
- package/dist/types/src/hooks/defs.d.ts +25 -0
- package/dist/types/src/hooks/defs.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +4 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
- package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
- package/dist/types/src/hooks/useEditorView.d.ts +17 -0
- package/dist/types/src/hooks/useEditorView.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +39 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextModel.d.ts +2 -27
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +2 -2
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +2 -0
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +48 -38
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +73 -82
- package/src/components/TextEditor/TextEditor.tsx +112 -75
- package/src/components/TextEditor/comments.stories.tsx +113 -71
- package/src/components/TextEditor/hooks.stories.tsx +106 -0
- package/src/components/Toolbar/Toolbar.stories.tsx +90 -0
- package/src/components/Toolbar/Toolbar.tsx +166 -0
- package/src/components/Toolbar/index.ts +5 -0
- package/src/components/index.ts +1 -0
- package/src/extensions/autocomplete.ts +6 -3
- package/src/extensions/automerge/automerge.spec.tsx +76 -0
- package/src/extensions/automerge/automerge.test.ts +13 -0
- package/src/extensions/automerge/automerge.ts +54 -45
- package/src/extensions/automerge/cursor.ts +3 -3
- package/src/extensions/automerge/defs.ts +13 -34
- package/src/extensions/automerge/index.ts +0 -1
- package/src/extensions/automerge/semaphore.ts +33 -32
- package/src/extensions/automerge/update-automerge.ts +14 -8
- package/src/extensions/automerge/update-codemirror.ts +5 -5
- package/src/extensions/awareness.ts +18 -17
- package/src/extensions/basic.ts +17 -10
- package/src/extensions/comments.ts +316 -165
- package/src/extensions/cursor.ts +7 -3
- package/src/extensions/index.ts +1 -7
- package/src/extensions/markdown/bundle.ts +23 -28
- package/src/extensions/markdown/code.ts +150 -0
- package/src/extensions/markdown/formatting.ts +188 -0
- package/src/extensions/markdown/heading.ts +59 -0
- package/src/extensions/markdown/highlight.ts +9 -15
- package/src/extensions/markdown/hr.ts +71 -0
- package/src/extensions/markdown/index.ts +8 -0
- package/src/extensions/{link.ts → markdown/link.ts} +8 -5
- package/src/extensions/markdown/tasklist.ts +110 -0
- package/src/extensions/mention.ts +9 -2
- package/src/extensions/outliner.ts +12 -0
- package/src/hooks/awareness-provider.ts +24 -20
- package/src/hooks/defs.ts +42 -0
- package/src/hooks/index.ts +5 -2
- package/src/hooks/useActionHandler.ts +62 -0
- package/src/hooks/useEditorView.ts +29 -0
- package/src/hooks/useTextEditor.ts +153 -0
- package/src/hooks/useTextModel.ts +49 -55
- package/src/styles/markdown.ts +9 -10
- package/src/themes/default.ts +4 -4
- package/src/util.ts +26 -3
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
- package/dist/types/src/extensions/code.d.ts.map +0 -1
- package/dist/types/src/extensions/hr.d.ts.map +0 -1
- package/dist/types/src/extensions/image.d.ts.map +0 -1
- package/dist/types/src/extensions/link.d.ts.map +0 -1
- package/dist/types/src/extensions/table.d.ts.map +0 -1
- package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/index.d.ts +0 -2
- package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
- package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
- package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge.d.ts +0 -3
- package/dist/types/src/hooks/automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs.d.ts +0 -27
- package/dist/types/src/hooks/yjs.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -3
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
- package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
- package/dist/types/src/testing/proto/index.d.ts +0 -2
- package/dist/types/src/testing/proto/index.d.ts.map +0 -1
- package/dist/types/src/testing/replicator.d.ts +0 -45
- package/dist/types/src/testing/replicator.d.ts.map +0 -1
- package/src/components/TextEditor/codemirror.stories.ts +0 -115
- package/src/components/TextEditor/yjs.stories.tsx +0 -56
- package/src/extensions/code.ts +0 -106
- package/src/extensions/hr.ts +0 -73
- package/src/extensions/tasklist.ts +0 -119
- package/src/extensions/yjs/cursor.ts +0 -22
- package/src/extensions/yjs/index.ts +0 -5
- package/src/extensions/yjs/yjs.test.ts +0 -35
- package/src/extensions/yjs/yjs.ts +0 -16
- package/src/hooks/automerge.ts +0 -52
- package/src/hooks/yjs.ts +0 -156
- package/src/testing/index.ts +0 -6
- package/src/testing/proto/gen/schema.ts +0 -49
- package/src/testing/proto/index.ts +0 -5
- package/src/testing/proto/schema.proto +0 -15
- package/src/testing/replicator.ts +0 -184
- /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
- /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
- /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
- /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
- /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
- /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
- /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
- /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
|
@@ -7,14 +7,14 @@ import { tags as tags2 } from "@lezer/highlight";
|
|
|
7
7
|
|
|
8
8
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
9
9
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
10
|
-
import { EditorView as
|
|
11
|
-
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
10
|
+
import { EditorView as EditorView13 } from "@codemirror/view";
|
|
12
11
|
import { vim } from "@replit/codemirror-vim";
|
|
13
12
|
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
14
|
-
import React, { forwardRef, useCallback, useEffect, useImperativeHandle,
|
|
15
|
-
import {
|
|
13
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
|
|
14
|
+
import { log as log2 } from "@dxos/log";
|
|
16
15
|
import { useThemeContext } from "@dxos/react-ui";
|
|
17
|
-
import {
|
|
16
|
+
import { attentionSurface, mx as mx3 } from "@dxos/react-ui-theme";
|
|
17
|
+
import { isNotFalsy as isNotFalsy3 } from "@dxos/util";
|
|
18
18
|
|
|
19
19
|
// packages/ui/react-ui-editor/src/extensions/autocomplete.ts
|
|
20
20
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
@@ -29,9 +29,11 @@ var autocomplete = ({ onSearch }) => {
|
|
|
29
29
|
// https://codemirror.net/examples/autocompletion
|
|
30
30
|
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
31
31
|
autocompletion({
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
|
|
32
|
+
activateOnTyping: true,
|
|
33
|
+
// closeOnBlur: false,
|
|
34
|
+
// defaultKeymap: false,
|
|
35
|
+
// TODO(burdon): Styles/fragments.
|
|
36
|
+
tooltipClass: () => "shadow rounded"
|
|
35
37
|
}),
|
|
36
38
|
// TODO(burdon): Option to create new page?
|
|
37
39
|
// TODO(burdon): Optional decoration via addToOptions
|
|
@@ -51,13 +53,14 @@ var autocomplete = ({ onSearch }) => {
|
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
56
|
-
import {
|
|
56
|
+
import { invertedEffects } from "@codemirror/commands";
|
|
57
|
+
import { StateField } from "@codemirror/state";
|
|
58
|
+
import { EditorView, ViewPlugin } from "@codemirror/view";
|
|
59
|
+
import { next as A4 } from "@dxos/automerge/automerge";
|
|
57
60
|
|
|
58
61
|
// packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
|
|
59
62
|
import get from "lodash.get";
|
|
60
|
-
import { next as
|
|
63
|
+
import { next as A } from "@dxos/automerge/automerge";
|
|
61
64
|
var cursorConverter = (handle, path) => ({
|
|
62
65
|
// TODO(burdon): Handle assoc to associate with a previous character.
|
|
63
66
|
toCursor: (pos) => {
|
|
@@ -69,7 +72,7 @@ var cursorConverter = (handle, path) => ({
|
|
|
69
72
|
if (typeof value === "string" && value.length <= pos) {
|
|
70
73
|
return "end";
|
|
71
74
|
}
|
|
72
|
-
return
|
|
75
|
+
return A.getCursor(doc, path.slice(), pos);
|
|
73
76
|
},
|
|
74
77
|
fromCursor: (cursor) => {
|
|
75
78
|
if (cursor === "") {
|
|
@@ -87,26 +90,28 @@ var cursorConverter = (handle, path) => ({
|
|
|
87
90
|
return 0;
|
|
88
91
|
}
|
|
89
92
|
}
|
|
90
|
-
return
|
|
93
|
+
return A.getCursorPosition(doc, path.slice(), cursor);
|
|
91
94
|
}
|
|
92
95
|
});
|
|
93
96
|
|
|
94
97
|
// packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
|
|
95
98
|
import { Annotation, StateEffect } from "@codemirror/state";
|
|
96
|
-
var
|
|
97
|
-
var
|
|
99
|
+
var getPath = (state, field) => state.field(field).path;
|
|
100
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
101
|
+
var updateHeadsEffect = StateEffect.define({});
|
|
102
|
+
var updateHeads = (newHeads) => updateHeadsEffect.of({
|
|
98
103
|
newHeads
|
|
99
104
|
});
|
|
100
|
-
var
|
|
101
|
-
var
|
|
102
|
-
|
|
103
|
-
|
|
105
|
+
var reconcileAnnotation = Annotation.define();
|
|
106
|
+
var isReconcile = (tr) => {
|
|
107
|
+
return !!tr.annotation(reconcileAnnotation);
|
|
108
|
+
};
|
|
104
109
|
|
|
105
110
|
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
106
|
-
import { next as
|
|
111
|
+
import { next as A3 } from "@dxos/automerge/automerge";
|
|
107
112
|
|
|
108
113
|
// packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
|
|
109
|
-
import { next as
|
|
114
|
+
import { next as A2 } from "@dxos/automerge/automerge";
|
|
110
115
|
var updateAutomerge = (field, handle, transactions, state) => {
|
|
111
116
|
const { lastHeads, path } = state.field(field);
|
|
112
117
|
let hasChanges = false;
|
|
@@ -119,11 +124,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
|
|
|
119
124
|
return void 0;
|
|
120
125
|
}
|
|
121
126
|
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
127
|
+
const invertedTransactions = [];
|
|
122
128
|
for (const tr of transactions) {
|
|
123
|
-
tr.changes.iterChanges((fromA, toA, _fromB, _toB,
|
|
124
|
-
|
|
129
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
|
|
130
|
+
invertedTransactions.push({
|
|
131
|
+
from: fromA,
|
|
132
|
+
del: toA - fromA,
|
|
133
|
+
insert
|
|
134
|
+
});
|
|
125
135
|
});
|
|
126
136
|
}
|
|
137
|
+
invertedTransactions.reverse().forEach(({ from, del, insert }) => {
|
|
138
|
+
A2.splice(doc, path.slice(), from, del, insert.toString());
|
|
139
|
+
});
|
|
127
140
|
});
|
|
128
141
|
return newHeads ?? void 0;
|
|
129
142
|
};
|
|
@@ -138,13 +151,13 @@ var updateCodeMirror = (view, selection, target, patches) => {
|
|
|
138
151
|
selection = selection.map(changeSet, 1);
|
|
139
152
|
view.dispatch({
|
|
140
153
|
changes: changeSet,
|
|
141
|
-
annotations:
|
|
154
|
+
annotations: reconcileAnnotation.of(false)
|
|
142
155
|
});
|
|
143
156
|
}
|
|
144
157
|
}
|
|
145
158
|
view.dispatch({
|
|
146
159
|
selection,
|
|
147
|
-
annotations:
|
|
160
|
+
annotations: reconcileAnnotation.of(false)
|
|
148
161
|
});
|
|
149
162
|
};
|
|
150
163
|
var handlePatch = (patch, target, state) => {
|
|
@@ -165,7 +178,7 @@ var handleInsert = (target, patch) => {
|
|
|
165
178
|
if (index == null) {
|
|
166
179
|
return [];
|
|
167
180
|
}
|
|
168
|
-
const text = patch.values.map((
|
|
181
|
+
const text = patch.values.map((value) => value ? value.toString() : "").join("");
|
|
169
182
|
return [
|
|
170
183
|
{
|
|
171
184
|
from: index,
|
|
@@ -237,40 +250,43 @@ var charPath = (textPath, candidatePath) => {
|
|
|
237
250
|
|
|
238
251
|
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
239
252
|
var PatchSemaphore = class {
|
|
240
|
-
|
|
241
|
-
|
|
253
|
+
// prettier-ignore
|
|
254
|
+
constructor(_handle, _state) {
|
|
255
|
+
this._handle = _handle;
|
|
256
|
+
this._state = _state;
|
|
242
257
|
this._inReconcile = false;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
258
|
+
}
|
|
259
|
+
reconcile(view) {
|
|
260
|
+
if (!this._inReconcile) {
|
|
248
261
|
this._inReconcile = true;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
|
|
263
|
-
if (newHeads === null || newHeads === void 0) {
|
|
264
|
-
newHeads = automerge2.getHeads(handle.docSync());
|
|
265
|
-
}
|
|
266
|
-
const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(handle.docSync(), oldHeads, newHeads);
|
|
267
|
-
updateCodeMirror(view, selection, path, diff);
|
|
262
|
+
this.doReconcile(view);
|
|
263
|
+
this._inReconcile = false;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
doReconcile(view) {
|
|
267
|
+
const path = getPath(view.state, this._state);
|
|
268
|
+
const oldHeads = getLastHeads(view.state, this._state);
|
|
269
|
+
let selection = view.state.selection;
|
|
270
|
+
const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
|
|
271
|
+
const toInvert = transactions.slice().reverse();
|
|
272
|
+
for (const tr of toInvert) {
|
|
273
|
+
const inverted = tr.changes.invert(tr.startState.doc);
|
|
274
|
+
selection = selection.map(inverted);
|
|
268
275
|
view.dispatch({
|
|
269
|
-
|
|
270
|
-
annotations:
|
|
276
|
+
changes: inverted,
|
|
277
|
+
annotations: reconcileAnnotation.of(true)
|
|
271
278
|
});
|
|
272
|
-
|
|
273
|
-
|
|
279
|
+
}
|
|
280
|
+
let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
|
|
281
|
+
if (newHeads === null || newHeads === void 0) {
|
|
282
|
+
newHeads = A3.getHeads(this._handle.docSync());
|
|
283
|
+
}
|
|
284
|
+
const diff = A3.equals(oldHeads, newHeads) ? [] : A3.diff(this._handle.docSync(), oldHeads, newHeads);
|
|
285
|
+
updateCodeMirror(view, selection, path, diff);
|
|
286
|
+
view.dispatch({
|
|
287
|
+
effects: updateHeads(newHeads),
|
|
288
|
+
annotations: reconcileAnnotation.of(false)
|
|
289
|
+
});
|
|
274
290
|
}
|
|
275
291
|
};
|
|
276
292
|
|
|
@@ -280,16 +296,17 @@ var defaultCursorConverter = {
|
|
|
280
296
|
toCursor: (position) => position.toString(),
|
|
281
297
|
fromCursor: (cursor) => parseInt(cursor)
|
|
282
298
|
};
|
|
283
|
-
var Cursor = class {
|
|
299
|
+
var Cursor = class _Cursor {
|
|
284
300
|
static {
|
|
285
301
|
this.converter = Facet.define({
|
|
286
302
|
combine: (providers) => providers[0] ?? defaultCursorConverter
|
|
287
303
|
});
|
|
288
304
|
}
|
|
289
305
|
static {
|
|
290
|
-
this.getCursorFromRange = (
|
|
291
|
-
const
|
|
292
|
-
const
|
|
306
|
+
this.getCursorFromRange = (state, range) => {
|
|
307
|
+
const cursorConverter2 = state.facet(_Cursor.converter);
|
|
308
|
+
const from = cursorConverter2.toCursor(range.from);
|
|
309
|
+
const to = cursorConverter2.toCursor(range.to, -1);
|
|
293
310
|
return [
|
|
294
311
|
from,
|
|
295
312
|
to
|
|
@@ -297,10 +314,11 @@ var Cursor = class {
|
|
|
297
314
|
};
|
|
298
315
|
}
|
|
299
316
|
static {
|
|
300
|
-
this.getRangeFromCursor = (
|
|
317
|
+
this.getRangeFromCursor = (state, cursor) => {
|
|
318
|
+
const cursorConverter2 = state.facet(_Cursor.converter);
|
|
301
319
|
const parts = cursor.split(":");
|
|
302
|
-
const from =
|
|
303
|
-
const to =
|
|
320
|
+
const from = cursorConverter2.fromCursor(parts[0]);
|
|
321
|
+
const to = cursorConverter2.fromCursor(parts[1]);
|
|
304
322
|
return from !== void 0 && to !== void 0 ? {
|
|
305
323
|
from,
|
|
306
324
|
to
|
|
@@ -310,25 +328,22 @@ var Cursor = class {
|
|
|
310
328
|
};
|
|
311
329
|
|
|
312
330
|
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
313
|
-
var
|
|
314
|
-
|
|
315
|
-
});
|
|
316
|
-
var automerge3 = ({ handle, path }) => {
|
|
317
|
-
const stateField = StateField.define({
|
|
331
|
+
var automerge = ({ handle, path }) => {
|
|
332
|
+
const syncState = StateField.define({
|
|
318
333
|
create: () => ({
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
334
|
+
path: path.slice(),
|
|
335
|
+
lastHeads: A4.getHeads(handle.docSync()),
|
|
336
|
+
unreconciledTransactions: []
|
|
322
337
|
}),
|
|
323
338
|
update: (value, tr) => {
|
|
324
339
|
const result = {
|
|
340
|
+
path: path.slice(),
|
|
325
341
|
lastHeads: value.lastHeads,
|
|
326
|
-
unreconciledTransactions: value.unreconciledTransactions.slice()
|
|
327
|
-
path: path.slice()
|
|
342
|
+
unreconciledTransactions: value.unreconciledTransactions.slice()
|
|
328
343
|
};
|
|
329
344
|
let clearUnreconciled = false;
|
|
330
345
|
for (const effect of tr.effects) {
|
|
331
|
-
if (effect.is(
|
|
346
|
+
if (effect.is(updateHeadsEffect)) {
|
|
332
347
|
result.lastHeads = effect.value.newHeads;
|
|
333
348
|
clearUnreconciled = true;
|
|
334
349
|
}
|
|
@@ -336,46 +351,53 @@ var automerge3 = ({ handle, path }) => {
|
|
|
336
351
|
if (clearUnreconciled) {
|
|
337
352
|
result.unreconciledTransactions = [];
|
|
338
353
|
} else {
|
|
339
|
-
if (!
|
|
354
|
+
if (!isReconcile(tr)) {
|
|
340
355
|
result.unreconciledTransactions.push(tr);
|
|
341
356
|
}
|
|
342
357
|
}
|
|
343
358
|
return result;
|
|
344
359
|
}
|
|
345
360
|
});
|
|
346
|
-
const semaphore = new PatchSemaphore(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
const semaphore = new PatchSemaphore(handle, syncState);
|
|
362
|
+
return [
|
|
363
|
+
Cursor.converter.of(cursorConverter(handle, path)),
|
|
364
|
+
syncState,
|
|
365
|
+
// Reconcile external updates.
|
|
366
|
+
ViewPlugin.fromClass(class {
|
|
367
|
+
constructor(_view) {
|
|
368
|
+
this._view = _view;
|
|
369
|
+
handle.addListener("change", this._handleChange.bind(this));
|
|
370
|
+
}
|
|
371
|
+
destroy() {
|
|
372
|
+
handle.removeListener("change", this._handleChange.bind(this));
|
|
373
|
+
}
|
|
374
|
+
_handleChange() {
|
|
375
|
+
semaphore.reconcile(this._view);
|
|
376
|
+
}
|
|
377
|
+
}),
|
|
378
|
+
// Reconcile local updates.
|
|
379
|
+
EditorView.updateListener.of(({ view, changes }) => {
|
|
380
|
+
if (!changes.empty) {
|
|
381
|
+
semaphore.reconcile(view);
|
|
382
|
+
}
|
|
383
|
+
}),
|
|
384
|
+
// TODO(burdon): Record undo transactions.
|
|
385
|
+
// See https://github.com/yjs/y-codemirror.next/tree/main
|
|
386
|
+
// https://codemirror.net/examples/inverted-effect
|
|
387
|
+
invertedEffects.of((tr) => {
|
|
388
|
+
const effects = [];
|
|
389
|
+
if (!tr.changes.empty) {
|
|
390
|
+
tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
|
359
391
|
});
|
|
360
392
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
return {
|
|
367
|
-
extension: [
|
|
368
|
-
Cursor.converter.of(cursorConverter(handle, path)),
|
|
369
|
-
semaphoreFacet.of(semaphore),
|
|
370
|
-
stateField,
|
|
371
|
-
viewPlugin
|
|
372
|
-
]
|
|
373
|
-
};
|
|
393
|
+
return effects;
|
|
394
|
+
})
|
|
395
|
+
];
|
|
374
396
|
};
|
|
375
397
|
|
|
376
398
|
// packages/ui/react-ui-editor/src/extensions/awareness.ts
|
|
377
|
-
import { Annotation as Annotation2, Facet as
|
|
378
|
-
import { Decoration, EditorView, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
399
|
+
import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
|
|
400
|
+
import { Decoration, EditorView as EditorView2, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
379
401
|
import { Event } from "@dxos/async";
|
|
380
402
|
import { Context } from "@dxos/context";
|
|
381
403
|
var dummyProvider = {
|
|
@@ -384,17 +406,17 @@ var dummyProvider = {
|
|
|
384
406
|
},
|
|
385
407
|
close: () => {
|
|
386
408
|
},
|
|
409
|
+
getRemoteStates: () => [],
|
|
387
410
|
update: () => {
|
|
388
|
-
}
|
|
389
|
-
getRemoteStates: () => []
|
|
411
|
+
}
|
|
390
412
|
};
|
|
391
|
-
var
|
|
413
|
+
var awarenessProvider = Facet2.define({
|
|
392
414
|
combine: (providers) => providers[0] ?? dummyProvider
|
|
393
415
|
});
|
|
394
416
|
var RemoteSelectionChangedAnnotation = Annotation2.define();
|
|
395
417
|
var awareness = (provider = dummyProvider) => {
|
|
396
418
|
return [
|
|
397
|
-
|
|
419
|
+
awarenessProvider.of(provider),
|
|
398
420
|
ViewPlugin2.fromClass(RemoteSelectionsDecorator, {
|
|
399
421
|
decorations: (value) => value.decorations
|
|
400
422
|
}),
|
|
@@ -408,7 +430,7 @@ var RemoteSelectionsDecorator = class {
|
|
|
408
430
|
this._lastAnchor = void 0;
|
|
409
431
|
this._lastHead = void 0;
|
|
410
432
|
this._cursorConverter = view.state.facet(Cursor.converter);
|
|
411
|
-
this._provider = view.state.facet(
|
|
433
|
+
this._provider = view.state.facet(awarenessProvider);
|
|
412
434
|
this._provider.open();
|
|
413
435
|
this._provider.remoteStateChange.on(this._ctx, () => {
|
|
414
436
|
view.dispatch({
|
|
@@ -549,7 +571,7 @@ var RemoteCaretWidget = class extends WidgetType {
|
|
|
549
571
|
return true;
|
|
550
572
|
}
|
|
551
573
|
};
|
|
552
|
-
var styles =
|
|
574
|
+
var styles = EditorView2.baseTheme({
|
|
553
575
|
".cm-collab-selection": {},
|
|
554
576
|
".cm-collab-selectionLine": {
|
|
555
577
|
padding: 0,
|
|
@@ -562,40 +584,42 @@ var styles = EditorView.baseTheme({
|
|
|
562
584
|
marginLeft: "-1px",
|
|
563
585
|
marginRight: "-1px",
|
|
564
586
|
boxSizing: "border-box",
|
|
565
|
-
display: "inline"
|
|
587
|
+
display: "inline",
|
|
588
|
+
cursor: "pointer"
|
|
566
589
|
},
|
|
567
590
|
".cm-collab-selectionCaretDot": {
|
|
568
591
|
borderRadius: "50%",
|
|
569
592
|
position: "absolute",
|
|
570
|
-
width: ".
|
|
571
|
-
height: ".
|
|
572
|
-
top: "-.
|
|
573
|
-
left: "-.
|
|
593
|
+
width: ".5em",
|
|
594
|
+
height: ".5em",
|
|
595
|
+
top: "-.25em",
|
|
596
|
+
left: "-.25em",
|
|
574
597
|
backgroundColor: "inherit",
|
|
575
598
|
transition: "transform .3s ease-in-out",
|
|
576
599
|
boxSizing: "border-box"
|
|
577
600
|
},
|
|
578
601
|
".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
|
|
579
|
-
|
|
580
|
-
|
|
602
|
+
transform: "scale(0)",
|
|
603
|
+
transformOrigin: "center"
|
|
581
604
|
},
|
|
582
605
|
".cm-collab-selectionInfo": {
|
|
583
606
|
position: "absolute",
|
|
584
|
-
|
|
585
|
-
|
|
607
|
+
transform: "translate(-50%, 0)",
|
|
608
|
+
top: "-20px",
|
|
609
|
+
left: 0,
|
|
586
610
|
fontSize: ".75em",
|
|
587
|
-
fontFamily: "serif",
|
|
611
|
+
fontFamily: "sans-serif",
|
|
588
612
|
fontStyle: "normal",
|
|
589
613
|
fontWeight: "normal",
|
|
590
614
|
lineHeight: "normal",
|
|
591
615
|
userSelect: "none",
|
|
592
616
|
color: "white",
|
|
593
|
-
|
|
594
|
-
paddingRight: "2px",
|
|
617
|
+
padding: "2px",
|
|
595
618
|
zIndex: 101,
|
|
596
619
|
transition: "opacity .3s ease-in-out",
|
|
597
620
|
backgroundColor: "inherit",
|
|
598
|
-
|
|
621
|
+
borderRadius: "2px",
|
|
622
|
+
// These should be separate.
|
|
599
623
|
opacity: 0,
|
|
600
624
|
transitionDelay: "0s",
|
|
601
625
|
whiteSpace: "nowrap"
|
|
@@ -608,23 +632,26 @@ var styles = EditorView.baseTheme({
|
|
|
608
632
|
|
|
609
633
|
// packages/ui/react-ui-editor/src/extensions/basic.ts
|
|
610
634
|
import { closeBrackets } from "@codemirror/autocomplete";
|
|
635
|
+
import { history } from "@codemirror/commands";
|
|
611
636
|
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
612
637
|
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
613
|
-
import { drawSelection, EditorView as
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
638
|
+
import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
|
|
639
|
+
import { isNotFalsy } from "@dxos/util";
|
|
640
|
+
var createBasicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => [
|
|
641
|
+
lineWrapping && EditorView3.lineWrapping,
|
|
617
642
|
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|
|
618
643
|
bracketMatching(),
|
|
619
644
|
closeBrackets(),
|
|
620
645
|
drawSelection(),
|
|
646
|
+
highlightActiveLine(),
|
|
647
|
+
history(),
|
|
621
648
|
_placeholder && placeholder(_placeholder),
|
|
622
|
-
//
|
|
649
|
+
// https://github.com/codemirror/theme-one-dark
|
|
623
650
|
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
624
|
-
].filter(
|
|
651
|
+
].filter(isNotFalsy);
|
|
625
652
|
|
|
626
653
|
// packages/ui/react-ui-editor/src/extensions/blast.ts
|
|
627
|
-
import { EditorView as
|
|
654
|
+
import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
|
|
628
655
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
629
656
|
import { invariant } from "@dxos/invariant";
|
|
630
657
|
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
|
|
@@ -660,10 +687,10 @@ var blast = (options = defaultOptions) => {
|
|
|
660
687
|
const getPoint = (view, pos) => {
|
|
661
688
|
const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
|
|
662
689
|
const element = document.elementFromPoint(x, y);
|
|
663
|
-
const { offsetLeft: left = 0, offsetTop:
|
|
690
|
+
const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
|
|
664
691
|
const point = {
|
|
665
692
|
x: x - left,
|
|
666
|
-
y: y -
|
|
693
|
+
y: y - top2
|
|
667
694
|
};
|
|
668
695
|
return {
|
|
669
696
|
element,
|
|
@@ -672,7 +699,7 @@ var blast = (options = defaultOptions) => {
|
|
|
672
699
|
};
|
|
673
700
|
return [
|
|
674
701
|
// Cursor moved.
|
|
675
|
-
|
|
702
|
+
EditorView4.updateListener.of((update2) => {
|
|
676
703
|
if (blaster?.node !== update2.view.scrollDOM) {
|
|
677
704
|
if (blaster) {
|
|
678
705
|
blaster.destroy();
|
|
@@ -933,9 +960,14 @@ var random = (min, max) => {
|
|
|
933
960
|
return min + ~~(Math.random() * (max - min + 1));
|
|
934
961
|
};
|
|
935
962
|
|
|
936
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
937
|
-
import {
|
|
938
|
-
import {
|
|
963
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
964
|
+
import { invertedEffects as invertedEffects2 } from "@codemirror/commands";
|
|
965
|
+
import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
966
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
|
|
967
|
+
import sortBy from "lodash.sortby";
|
|
968
|
+
import { useEffect } from "react";
|
|
969
|
+
import { debounce } from "@dxos/async";
|
|
970
|
+
import { nonNullable } from "@dxos/util";
|
|
939
971
|
|
|
940
972
|
// packages/ui/react-ui-editor/src/styles/cursors.ts
|
|
941
973
|
import * as random2 from "lib0/random";
|
|
@@ -978,34 +1010,15 @@ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
|
|
|
978
1010
|
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
979
1011
|
import { mx } from "@dxos/react-ui-theme";
|
|
980
1012
|
var headings = {
|
|
981
|
-
1:
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
|
|
991
|
-
"-ml-[7px]"
|
|
992
|
-
],
|
|
993
|
-
4: [
|
|
994
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
|
|
995
|
-
"-ml-[6px]"
|
|
996
|
-
],
|
|
997
|
-
5: [
|
|
998
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
|
|
999
|
-
"-ml-[5px]"
|
|
1000
|
-
],
|
|
1001
|
-
6: [
|
|
1002
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline",
|
|
1003
|
-
"-ml-[4px]"
|
|
1004
|
-
]
|
|
1005
|
-
};
|
|
1006
|
-
var heading = (level, readonly) => {
|
|
1007
|
-
const [style, offset] = headings[level];
|
|
1008
|
-
return mx(style, readonly && offset);
|
|
1013
|
+
1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
|
|
1014
|
+
2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
|
|
1015
|
+
3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
|
|
1016
|
+
4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
|
|
1017
|
+
5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
|
|
1018
|
+
6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
|
|
1019
|
+
};
|
|
1020
|
+
var heading = (level) => {
|
|
1021
|
+
return headings[level];
|
|
1009
1022
|
};
|
|
1010
1023
|
var light = "text-neutral-200 dark:text-neutral-800";
|
|
1011
1024
|
var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
|
|
@@ -1023,168 +1036,109 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
|
1023
1036
|
var tokens = tailwindConfig({}).theme;
|
|
1024
1037
|
var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
|
|
1025
1038
|
|
|
1026
|
-
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
1027
|
-
var CODE_REGEX = /```[\s\S]*?```/gs;
|
|
1028
|
-
var styles2 = EditorView4.baseTheme({
|
|
1029
|
-
"& .cm-codeblock": {
|
|
1030
|
-
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
1031
|
-
},
|
|
1032
|
-
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
1033
|
-
background: getToken("extend.colors.neutral.50")
|
|
1034
|
-
},
|
|
1035
|
-
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
1036
|
-
background: getToken("extend.colors.neutral.850")
|
|
1037
|
-
},
|
|
1038
|
-
'& [aria-readonly="true"] .cm-codeblock': {
|
|
1039
|
-
display: "block",
|
|
1040
|
-
paddingInline: "4px !important"
|
|
1041
|
-
},
|
|
1042
|
-
'& [aria-readonly="true"] .cm-codeblock-first': {
|
|
1043
|
-
paddingTop: "4px !important",
|
|
1044
|
-
borderTopLeftRadius: "4px",
|
|
1045
|
-
borderTopRightRadius: "4px"
|
|
1046
|
-
},
|
|
1047
|
-
'& [aria-readonly="true"] .cm-codeblock-last': {
|
|
1048
|
-
paddingBottom: "4px !important",
|
|
1049
|
-
borderBottomLeftRadius: "4px",
|
|
1050
|
-
borderBottomRightRadius: "4px"
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
var getLineRange = (lines, from, to) => {
|
|
1054
|
-
const start = lines.findIndex((line) => line.from >= from);
|
|
1055
|
-
const end = lines.findIndex((line) => line.to >= to);
|
|
1056
|
-
return [
|
|
1057
|
-
start,
|
|
1058
|
-
end
|
|
1059
|
-
];
|
|
1060
|
-
};
|
|
1061
|
-
var code2 = () => {
|
|
1062
|
-
const getDecorations = (view) => {
|
|
1063
|
-
const decorations = [];
|
|
1064
|
-
if (!view.hasFocus || view.state.readOnly) {
|
|
1065
|
-
const text = view.state.doc.sliceString(0);
|
|
1066
|
-
const matches = text.matchAll(CODE_REGEX);
|
|
1067
|
-
const blocks = view.viewportLineBlocks;
|
|
1068
|
-
for (const match of matches) {
|
|
1069
|
-
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
1070
|
-
for (let i = range[0]; i <= range[1]; i++) {
|
|
1071
|
-
const block = blocks[i];
|
|
1072
|
-
decorations.push(Decoration2.line({
|
|
1073
|
-
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
1074
|
-
}).range(block.from));
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
return Decoration2.set(decorations);
|
|
1079
|
-
};
|
|
1080
|
-
return [
|
|
1081
|
-
ViewPlugin3.fromClass(class {
|
|
1082
|
-
constructor(view) {
|
|
1083
|
-
this.hasFocus = false;
|
|
1084
|
-
this.decorations = getDecorations(view);
|
|
1085
|
-
}
|
|
1086
|
-
update(update2) {
|
|
1087
|
-
if (
|
|
1088
|
-
// TODO(burdon): Generalize for other extensions.
|
|
1089
|
-
this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
|
|
1090
|
-
) {
|
|
1091
|
-
this.hasFocus = update2.view.hasFocus;
|
|
1092
|
-
this.decorations = getDecorations(update2.view);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}, {
|
|
1096
|
-
decorations: (value) => value.decorations
|
|
1097
|
-
}),
|
|
1098
|
-
styles2
|
|
1099
|
-
];
|
|
1100
|
-
};
|
|
1101
|
-
|
|
1102
|
-
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1103
|
-
import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
1104
|
-
import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
|
|
1105
|
-
import sortBy from "lodash.sortby";
|
|
1106
|
-
import { debounce } from "@dxos/async";
|
|
1107
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1108
|
-
import { log as log2 } from "@dxos/log";
|
|
1109
|
-
import { nonNullable } from "@dxos/util";
|
|
1110
|
-
|
|
1111
1039
|
// packages/ui/react-ui-editor/src/util.ts
|
|
1112
1040
|
import { log } from "@dxos/log";
|
|
1113
1041
|
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
|
|
1114
1042
|
var callbackWrapper = (fn) => (...args) => {
|
|
1115
1043
|
try {
|
|
1116
1044
|
return fn(...args);
|
|
1117
|
-
} catch (
|
|
1118
|
-
log.catch(
|
|
1045
|
+
} catch (err) {
|
|
1046
|
+
log.catch(err, void 0, {
|
|
1119
1047
|
F: __dxlog_file2,
|
|
1120
|
-
L:
|
|
1048
|
+
L: 18,
|
|
1121
1049
|
S: void 0,
|
|
1122
1050
|
C: (f, a) => f(...a)
|
|
1123
1051
|
});
|
|
1124
1052
|
}
|
|
1125
1053
|
};
|
|
1054
|
+
var logChanges = (trs) => {
|
|
1055
|
+
const changes = trs.flatMap((tr) => {
|
|
1056
|
+
if (tr.changes.empty) {
|
|
1057
|
+
return void 0;
|
|
1058
|
+
}
|
|
1059
|
+
const changes2 = [];
|
|
1060
|
+
tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
|
|
1061
|
+
fromA,
|
|
1062
|
+
toA,
|
|
1063
|
+
fromB,
|
|
1064
|
+
toB,
|
|
1065
|
+
inserted: inserted.toString()
|
|
1066
|
+
})));
|
|
1067
|
+
return changes2;
|
|
1068
|
+
}).filter(Boolean);
|
|
1069
|
+
if (changes.length) {
|
|
1070
|
+
console.log("changes", changes);
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1126
1073
|
|
|
1127
1074
|
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1128
|
-
var
|
|
1129
|
-
var styles3 = EditorView5.baseTheme({
|
|
1075
|
+
var styles2 = EditorView5.baseTheme({
|
|
1130
1076
|
"& .cm-comment": {
|
|
1131
1077
|
backgroundColor: getToken("extend.colors.yellow.50")
|
|
1132
1078
|
},
|
|
1133
|
-
"& .cm-comment-
|
|
1079
|
+
"& .cm-comment-current": {
|
|
1134
1080
|
backgroundColor: getToken("extend.colors.yellow.100")
|
|
1135
1081
|
}
|
|
1136
1082
|
});
|
|
1137
|
-
var
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
const range = view.state.field(commentsStateField).ranges.find((range2) => range2.id === thread);
|
|
1147
|
-
if (!range) {
|
|
1083
|
+
var commentMark = Decoration2.mark({
|
|
1084
|
+
class: "cm-comment"
|
|
1085
|
+
});
|
|
1086
|
+
var commentCurrentMark = Decoration2.mark({
|
|
1087
|
+
class: "cm-comment-current"
|
|
1088
|
+
});
|
|
1089
|
+
var focusComment = (view, id, center = true) => {
|
|
1090
|
+
const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
|
|
1091
|
+
if (!comment?.comment.cursor) {
|
|
1148
1092
|
return;
|
|
1149
1093
|
}
|
|
1150
|
-
view.
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1094
|
+
const range = Cursor.getRangeFromCursor(view.state, comment.comment.cursor);
|
|
1095
|
+
if (range) {
|
|
1096
|
+
view.dispatch({
|
|
1097
|
+
selection: {
|
|
1098
|
+
anchor: range.from
|
|
1099
|
+
},
|
|
1100
|
+
effects: [
|
|
1101
|
+
//
|
|
1102
|
+
EditorView5.scrollIntoView(range.from, center ? {
|
|
1103
|
+
y: "center"
|
|
1104
|
+
} : void 0),
|
|
1105
|
+
setSelection.of({
|
|
1106
|
+
current: id
|
|
1107
|
+
})
|
|
1108
|
+
]
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1159
1111
|
};
|
|
1160
|
-
var
|
|
1112
|
+
var setComments = StateEffect2.define();
|
|
1161
1113
|
var setSelection = StateEffect2.define();
|
|
1162
1114
|
var setCommentState = StateEffect2.define();
|
|
1163
|
-
var
|
|
1115
|
+
var commentsState = StateField2.define({
|
|
1164
1116
|
create: () => ({
|
|
1165
|
-
|
|
1117
|
+
comments: [],
|
|
1118
|
+
selection: {}
|
|
1166
1119
|
}),
|
|
1167
1120
|
update: (value, tr) => {
|
|
1168
|
-
const cursorConverter3 = tr.state.facet(Cursor.converter);
|
|
1169
1121
|
for (const effect of tr.effects) {
|
|
1170
1122
|
if (effect.is(setSelection)) {
|
|
1171
1123
|
return {
|
|
1172
1124
|
...value,
|
|
1173
|
-
|
|
1125
|
+
selection: effect.value
|
|
1174
1126
|
};
|
|
1175
1127
|
}
|
|
1176
|
-
if (effect.is(
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1128
|
+
if (effect.is(setComments)) {
|
|
1129
|
+
const comments2 = effect.value.map((comment) => {
|
|
1130
|
+
if (!comment.cursor) {
|
|
1131
|
+
return void 0;
|
|
1132
|
+
}
|
|
1133
|
+
const range = Cursor.getRangeFromCursor(tr.state, comment.cursor);
|
|
1180
1134
|
return range && {
|
|
1181
|
-
|
|
1182
|
-
|
|
1135
|
+
comment,
|
|
1136
|
+
range
|
|
1183
1137
|
};
|
|
1184
1138
|
}).filter(nonNullable);
|
|
1185
1139
|
return {
|
|
1186
1140
|
...value,
|
|
1187
|
-
|
|
1141
|
+
comments: comments2
|
|
1188
1142
|
};
|
|
1189
1143
|
}
|
|
1190
1144
|
if (effect.is(setCommentState)) {
|
|
@@ -1194,87 +1148,174 @@ var commentsStateField = StateField2.define({
|
|
|
1194
1148
|
return value;
|
|
1195
1149
|
}
|
|
1196
1150
|
});
|
|
1197
|
-
var
|
|
1198
|
-
|
|
1151
|
+
var commentsDecorations = EditorView5.decorations.compute([
|
|
1152
|
+
commentsState
|
|
1199
1153
|
], (state) => {
|
|
1200
|
-
const {
|
|
1201
|
-
const decorations = sortBy(
|
|
1202
|
-
|
|
1154
|
+
const { selection: { current }, comments: comments2 } = state.field(commentsState);
|
|
1155
|
+
const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
|
|
1156
|
+
const range = comment.range;
|
|
1157
|
+
if (!range || range.from === range.to) {
|
|
1158
|
+
console.warn("Invalid range:", range);
|
|
1203
1159
|
return void 0;
|
|
1204
1160
|
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
to: selection.to
|
|
1208
|
-
};
|
|
1209
|
-
if (selection.id === active) {
|
|
1210
|
-
return marks.highlightActive.range(range.from, range.to);
|
|
1161
|
+
if (comment.comment.id === current) {
|
|
1162
|
+
return commentCurrentMark.range(range.from, range.to);
|
|
1211
1163
|
} else {
|
|
1212
|
-
return
|
|
1164
|
+
return commentMark.range(range.from, range.to);
|
|
1213
1165
|
}
|
|
1214
|
-
}).filter(nonNullable)
|
|
1215
|
-
return
|
|
1166
|
+
}).filter(nonNullable);
|
|
1167
|
+
return Decoration2.set(decorations);
|
|
1216
1168
|
});
|
|
1217
|
-
var
|
|
1218
|
-
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1169
|
+
var trackPastedComments = (onUpdate) => {
|
|
1170
|
+
let tracked = null;
|
|
1171
|
+
const handleTrack = (event, view) => {
|
|
1172
|
+
const comments2 = view.state.field(commentsState);
|
|
1173
|
+
const { main } = view.state.selection;
|
|
1174
|
+
const selectedRanges = comments2.comments.filter(({ range }) => range.from >= main.from && range.to <= main.to && range.from < range.to);
|
|
1175
|
+
if (!selectedRanges.length) {
|
|
1176
|
+
tracked = null;
|
|
1177
|
+
} else {
|
|
1178
|
+
tracked = {
|
|
1179
|
+
text: view.state.doc.slice(main.from, main.to),
|
|
1180
|
+
comments: selectedRanges.map(({ comment, range }) => ({
|
|
1181
|
+
id: comment.id,
|
|
1182
|
+
from: range.from - main.from,
|
|
1183
|
+
to: range.to - main.from
|
|
1184
|
+
}))
|
|
1185
|
+
};
|
|
1234
1186
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1187
|
+
};
|
|
1188
|
+
return [
|
|
1189
|
+
EditorView5.domEventHandlers({
|
|
1190
|
+
cut: handleTrack,
|
|
1191
|
+
copy: handleTrack
|
|
1192
|
+
}),
|
|
1193
|
+
// Track deleted comments.
|
|
1194
|
+
invertedEffects2.of((tr) => {
|
|
1195
|
+
const { comments: comments2 } = tr.startState.field(commentsState);
|
|
1196
|
+
const effects = [];
|
|
1197
|
+
tr.changes.iterChangedRanges((fromA, toA) => {
|
|
1198
|
+
for (const { comment: { id }, range: { from, to } } of comments2) {
|
|
1199
|
+
if (from < to && from >= fromA && to <= toA) {
|
|
1200
|
+
effects.push(restoreCommentEffect.of({
|
|
1201
|
+
id,
|
|
1202
|
+
from,
|
|
1203
|
+
to
|
|
1204
|
+
}));
|
|
1248
1205
|
}
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1206
|
+
}
|
|
1207
|
+
});
|
|
1208
|
+
return effects;
|
|
1209
|
+
}),
|
|
1210
|
+
// Handle paste or the undo of comment deletion.
|
|
1211
|
+
EditorView5.updateListener.of((update2) => {
|
|
1212
|
+
const restore = [];
|
|
1213
|
+
for (let i = 0; i < update2.transactions.length; i++) {
|
|
1214
|
+
const tr = update2.transactions[i];
|
|
1215
|
+
for (let j = 0; j < restore.length; j++) {
|
|
1216
|
+
restore[j] = mapTrackedComment(restore[j], tr.changes);
|
|
1217
|
+
}
|
|
1218
|
+
for (const effect of tr.effects) {
|
|
1219
|
+
if (effect.is(restoreCommentEffect)) {
|
|
1220
|
+
restore.push(effect.value);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
if (tracked) {
|
|
1225
|
+
const paste = update2.transactions.find((tr) => tr.isUserEvent("input.paste"));
|
|
1226
|
+
if (paste) {
|
|
1227
|
+
let found = -1;
|
|
1228
|
+
paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
1229
|
+
if (text.eq(tracked.text)) {
|
|
1230
|
+
for (let i = update2.transactions.indexOf(paste) + 1; i < update2.transactions.length; i++) {
|
|
1231
|
+
fromB = update2.transactions[i].changes.mapPos(fromB);
|
|
1232
|
+
}
|
|
1233
|
+
found = fromB;
|
|
1259
1234
|
}
|
|
1260
1235
|
});
|
|
1236
|
+
if (found > -1) {
|
|
1237
|
+
for (const moved of tracked.comments) {
|
|
1238
|
+
restore.push({
|
|
1239
|
+
id: moved.id,
|
|
1240
|
+
from: found + moved.from,
|
|
1241
|
+
to: found + moved.to
|
|
1242
|
+
});
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
tracked = null;
|
|
1261
1246
|
}
|
|
1262
|
-
return true;
|
|
1263
1247
|
}
|
|
1264
|
-
|
|
1248
|
+
for (const comment of restore) {
|
|
1249
|
+
const { comments: comments2 } = update2.startState.field(commentsState);
|
|
1250
|
+
const exists = comments2.some((c) => c.comment.id === comment.id && c.range.from < c.range.to);
|
|
1251
|
+
if (!exists) {
|
|
1252
|
+
const cursor = Cursor.getCursorFromRange(update2.state, comment);
|
|
1253
|
+
onUpdate(comment.id, cursor);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
})
|
|
1257
|
+
];
|
|
1258
|
+
};
|
|
1259
|
+
var mapTrackedComment = (comment, changes) => ({
|
|
1260
|
+
id: comment.id,
|
|
1261
|
+
from: changes.mapPos(comment.from, 1),
|
|
1262
|
+
to: changes.mapPos(comment.to, 1)
|
|
1263
|
+
});
|
|
1264
|
+
var restoreCommentEffect = StateEffect2.define({
|
|
1265
|
+
map: mapTrackedComment
|
|
1266
|
+
});
|
|
1267
|
+
var optionsFacet = Facet3.define({
|
|
1268
|
+
combine: (providers) => providers[0]
|
|
1269
|
+
});
|
|
1270
|
+
var createComment = (view) => {
|
|
1271
|
+
const options = view.state.facet(optionsFacet);
|
|
1272
|
+
const { from, to } = view.state.selection.main;
|
|
1273
|
+
if (from === to) {
|
|
1265
1274
|
return false;
|
|
1266
|
-
}
|
|
1275
|
+
}
|
|
1276
|
+
if (to === view.state.doc.length) {
|
|
1277
|
+
view.dispatch({
|
|
1278
|
+
changes: {
|
|
1279
|
+
from: to,
|
|
1280
|
+
insert: "\n"
|
|
1281
|
+
}
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
const cursor = Cursor.getCursorFromRange(view.state, {
|
|
1285
|
+
from,
|
|
1286
|
+
to
|
|
1287
|
+
});
|
|
1288
|
+
if (cursor) {
|
|
1289
|
+
const id = options.onCreate?.(cursor, view.coordsAtPos(from));
|
|
1290
|
+
if (id) {
|
|
1291
|
+
view.dispatch({
|
|
1292
|
+
effects: setSelection.of({
|
|
1293
|
+
current: id
|
|
1294
|
+
}),
|
|
1295
|
+
selection: {
|
|
1296
|
+
anchor: to
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
return true;
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
return false;
|
|
1303
|
+
};
|
|
1304
|
+
var comments = (options = {}) => {
|
|
1305
|
+
const { key: shortcut = "meta-'" } = options;
|
|
1306
|
+
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1267
1307
|
return [
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1308
|
+
optionsFacet.of(options),
|
|
1309
|
+
commentsState,
|
|
1310
|
+
commentsDecorations,
|
|
1311
|
+
styles2,
|
|
1271
1312
|
//
|
|
1272
1313
|
// Keymap.
|
|
1273
1314
|
//
|
|
1274
1315
|
options.onCreate ? keymap3.of([
|
|
1275
1316
|
{
|
|
1276
1317
|
key: shortcut,
|
|
1277
|
-
run: callbackWrapper(
|
|
1318
|
+
run: callbackWrapper(createComment)
|
|
1278
1319
|
}
|
|
1279
1320
|
]) : [],
|
|
1280
1321
|
//
|
|
@@ -1290,7 +1331,7 @@ var comments = (options = {}) => {
|
|
|
1290
1331
|
above: true,
|
|
1291
1332
|
create: () => {
|
|
1292
1333
|
const el = document.createElement("div");
|
|
1293
|
-
options.onHover
|
|
1334
|
+
options.onHover(el, shortcut);
|
|
1294
1335
|
return {
|
|
1295
1336
|
dom: el,
|
|
1296
1337
|
offset: {
|
|
@@ -1303,364 +1344,116 @@ var comments = (options = {}) => {
|
|
|
1303
1344
|
}
|
|
1304
1345
|
return null;
|
|
1305
1346
|
}, {
|
|
1306
|
-
|
|
1347
|
+
// TODO(burdon): Hide on change triggered immediately?
|
|
1348
|
+
// hideOnChange: true,
|
|
1307
1349
|
hoverTime: 1e3
|
|
1308
1350
|
}) : [],
|
|
1309
1351
|
//
|
|
1310
|
-
//
|
|
1311
|
-
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1352
|
+
// Track deleted ranges and update ranges for decorations.
|
|
1312
1353
|
//
|
|
1313
1354
|
EditorView5.updateListener.of(({ view, state, changes }) => {
|
|
1314
|
-
|
|
1315
|
-
{
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
if (
|
|
1321
|
-
|
|
1322
|
-
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1323
|
-
log2.info("deleted comment", {
|
|
1324
|
-
thread: range.id
|
|
1325
|
-
}, {
|
|
1326
|
-
F: __dxlog_file3,
|
|
1327
|
-
L: 274,
|
|
1328
|
-
S: void 0,
|
|
1329
|
-
C: (f, a) => f(...a)
|
|
1330
|
-
});
|
|
1331
|
-
}
|
|
1355
|
+
let mod = false;
|
|
1356
|
+
const { comments: comments2, ...value } = state.field(commentsState);
|
|
1357
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1358
|
+
comments2.forEach(({ comment, range }) => {
|
|
1359
|
+
if (from2 === to2) {
|
|
1360
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1361
|
+
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1362
|
+
options.onDelete?.(comment.id);
|
|
1332
1363
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
}
|
|
1364
|
+
}
|
|
1365
|
+
if (from <= range.to) {
|
|
1366
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1367
|
+
Object.assign(range, newRange);
|
|
1368
|
+
mod = true;
|
|
1369
|
+
}
|
|
1370
|
+
});
|
|
1371
|
+
});
|
|
1372
|
+
if (mod) {
|
|
1373
|
+
view.dispatch({
|
|
1374
|
+
effects: setCommentState.of({
|
|
1375
|
+
comments: comments2,
|
|
1376
|
+
...value
|
|
1377
|
+
})
|
|
1339
1378
|
});
|
|
1340
|
-
if (mod) {
|
|
1341
|
-
view.dispatch({
|
|
1342
|
-
effects: setCommentState.of({
|
|
1343
|
-
active,
|
|
1344
|
-
ranges
|
|
1345
|
-
})
|
|
1346
|
-
});
|
|
1347
|
-
}
|
|
1348
1379
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1380
|
+
}),
|
|
1381
|
+
//
|
|
1382
|
+
// Track selection/proximity.
|
|
1383
|
+
//
|
|
1384
|
+
EditorView5.updateListener.of(({ view, state }) => {
|
|
1385
|
+
let min = Infinity;
|
|
1386
|
+
const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
|
|
1387
|
+
const { head } = state.selection.main;
|
|
1388
|
+
const selection = {};
|
|
1389
|
+
comments2.forEach(({ comment, range }) => {
|
|
1390
|
+
if (head >= range.from && head <= range.to) {
|
|
1391
|
+
selection.current = comment.id;
|
|
1392
|
+
selection.closest = void 0;
|
|
1393
|
+
}
|
|
1394
|
+
if (!selection.current) {
|
|
1395
|
+
const d = Math.min(Math.abs(head - range.from), Math.abs(head - range.to));
|
|
1362
1396
|
if (d < min) {
|
|
1363
|
-
|
|
1397
|
+
selection.closest = comment.id;
|
|
1364
1398
|
min = d;
|
|
1365
1399
|
}
|
|
1366
|
-
});
|
|
1367
|
-
if (selected.active !== active || selected.closest !== closest) {
|
|
1368
|
-
view.dispatch({
|
|
1369
|
-
effects: setSelection.of(selected)
|
|
1370
|
-
});
|
|
1371
|
-
handleSelect({
|
|
1372
|
-
...selected,
|
|
1373
|
-
ranges: ranges.map((range) => ({
|
|
1374
|
-
...range,
|
|
1375
|
-
location: view.coordsAtPos(range.from)
|
|
1376
|
-
}))
|
|
1377
|
-
});
|
|
1378
1400
|
}
|
|
1401
|
+
});
|
|
1402
|
+
if (selection.current !== current || selection.closest !== closest) {
|
|
1403
|
+
view.dispatch({
|
|
1404
|
+
effects: setSelection.of(selection)
|
|
1405
|
+
});
|
|
1406
|
+
handleSelect({
|
|
1407
|
+
selection,
|
|
1408
|
+
comments: comments2.map(({ comment, range }) => ({
|
|
1409
|
+
comment,
|
|
1410
|
+
range,
|
|
1411
|
+
location: view.coordsAtPos(range.from)
|
|
1412
|
+
}))
|
|
1413
|
+
});
|
|
1379
1414
|
}
|
|
1380
|
-
})
|
|
1415
|
+
}),
|
|
1416
|
+
options.onUpdate ? trackPastedComments(options.onUpdate) : []
|
|
1381
1417
|
];
|
|
1382
1418
|
};
|
|
1419
|
+
var useComments = (view, comments2 = []) => {
|
|
1420
|
+
useEffect(() => {
|
|
1421
|
+
if (view) {
|
|
1422
|
+
view.dispatch({
|
|
1423
|
+
effects: setComments.of(comments2)
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
}, [
|
|
1427
|
+
view,
|
|
1428
|
+
comments2
|
|
1429
|
+
]);
|
|
1430
|
+
};
|
|
1383
1431
|
|
|
1384
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
1385
|
-
import {
|
|
1386
|
-
var
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
this._pos = _pos;
|
|
1397
|
-
}
|
|
1398
|
-
eq(other) {
|
|
1399
|
-
return this._pos === other._pos;
|
|
1400
|
-
}
|
|
1401
|
-
toDOM(view) {
|
|
1402
|
-
const el = document.createElement("div");
|
|
1403
|
-
el.className = "cm-hr";
|
|
1404
|
-
return el;
|
|
1405
|
-
}
|
|
1406
|
-
};
|
|
1407
|
-
var placeholderMatcher = new MatchDecorator({
|
|
1408
|
-
regexp: /^---$/g,
|
|
1409
|
-
decoration: (match, view, pos) => Decoration4.replace({
|
|
1410
|
-
widget: new HorizontalRuleWidget(pos)
|
|
1411
|
-
})
|
|
1412
|
-
});
|
|
1413
|
-
var hr = () => [
|
|
1414
|
-
styles4,
|
|
1415
|
-
ViewPlugin4.fromClass(class {
|
|
1416
|
-
constructor(view) {
|
|
1417
|
-
this.rules = placeholderMatcher.createDeco(view);
|
|
1418
|
-
}
|
|
1419
|
-
update(update2) {
|
|
1420
|
-
this.rules = placeholderMatcher.updateDeco(update2, this.rules);
|
|
1421
|
-
}
|
|
1422
|
-
}, {
|
|
1423
|
-
decorations: (instance) => instance.rules,
|
|
1424
|
-
// TODO(burdon): Is this really atomic (cursor can move into ---).
|
|
1425
|
-
provide: (plugin) => EditorView6.atomicRanges.of((view) => {
|
|
1426
|
-
return view.plugin(plugin)?.rules || Decoration4.none;
|
|
1427
|
-
})
|
|
1428
|
-
})
|
|
1429
|
-
];
|
|
1430
|
-
|
|
1431
|
-
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
1432
|
-
import { syntaxTree } from "@codemirror/language";
|
|
1433
|
-
import { StateField as StateField3 } from "@codemirror/state";
|
|
1434
|
-
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
1435
|
-
var image = (options = {}) => {
|
|
1436
|
-
return StateField3.define({
|
|
1437
|
-
create: (state) => {
|
|
1438
|
-
return Decoration5.set(buildDecorations(0, state.doc.length, state));
|
|
1439
|
-
},
|
|
1440
|
-
update: (value, tr) => {
|
|
1441
|
-
if (!tr.docChanged && !tr.selection) {
|
|
1442
|
-
return value;
|
|
1443
|
-
}
|
|
1444
|
-
const cursor = tr.state.selection.main.head;
|
|
1445
|
-
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
1446
|
-
let from = Math.min(cursor, oldCursor);
|
|
1447
|
-
let to = Math.max(cursor, oldCursor);
|
|
1448
|
-
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
1449
|
-
from = Math.min(from, fromB);
|
|
1450
|
-
to = Math.max(to, toB);
|
|
1451
|
-
});
|
|
1452
|
-
from = tr.state.doc.lineAt(from).from;
|
|
1453
|
-
to = tr.state.doc.lineAt(to).to;
|
|
1454
|
-
return value.map(tr.changes).update({
|
|
1455
|
-
filterFrom: from,
|
|
1456
|
-
filterTo: to,
|
|
1457
|
-
filter: () => false,
|
|
1458
|
-
add: buildDecorations(from, to, tr.state)
|
|
1459
|
-
});
|
|
1460
|
-
},
|
|
1461
|
-
provide: (field) => EditorView7.decorations.from(field)
|
|
1462
|
-
});
|
|
1463
|
-
};
|
|
1464
|
-
var buildDecorations = (from, to, state) => {
|
|
1465
|
-
const decorations = [];
|
|
1466
|
-
const cursor = state.selection.main.head;
|
|
1467
|
-
syntaxTree(state).iterate({
|
|
1468
|
-
enter: (node) => {
|
|
1469
|
-
if (node.name === "Image") {
|
|
1470
|
-
const urlNode = node.node.getChild("URL");
|
|
1471
|
-
if (urlNode) {
|
|
1472
|
-
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
1473
|
-
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
1474
|
-
decorations.push(Decoration5.replace({
|
|
1475
|
-
block: true,
|
|
1476
|
-
widget: new ImageWidget(url)
|
|
1477
|
-
}).range(hide ? node.from : node.to, node.to));
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
},
|
|
1481
|
-
from,
|
|
1482
|
-
to
|
|
1483
|
-
});
|
|
1484
|
-
return decorations;
|
|
1485
|
-
};
|
|
1486
|
-
var ImageWidget = class extends WidgetType3 {
|
|
1487
|
-
constructor(_url) {
|
|
1488
|
-
super();
|
|
1489
|
-
this._url = _url;
|
|
1490
|
-
}
|
|
1491
|
-
eq(other) {
|
|
1492
|
-
return this._url === other._url;
|
|
1493
|
-
}
|
|
1494
|
-
toDOM(view) {
|
|
1495
|
-
const img = document.createElement("img");
|
|
1496
|
-
img.setAttribute("src", this._url);
|
|
1497
|
-
img.setAttribute("class", "cm-image");
|
|
1498
|
-
img.onload = () => img.classList.add("cm-loaded-image");
|
|
1499
|
-
return img;
|
|
1500
|
-
}
|
|
1501
|
-
};
|
|
1502
|
-
|
|
1503
|
-
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
1504
|
-
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1505
|
-
import { RangeSetBuilder } from "@codemirror/state";
|
|
1506
|
-
import { Decoration as Decoration6, WidgetType as WidgetType4, hoverTooltip as hoverTooltip2, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
1507
|
-
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1508
|
-
var link = (options = {}) => {
|
|
1509
|
-
const extensions = [
|
|
1510
|
-
ViewPlugin5.fromClass(class {
|
|
1511
|
-
constructor(view) {
|
|
1512
|
-
this.decorations = buildDecorations2(view, options);
|
|
1513
|
-
}
|
|
1514
|
-
update(update2) {
|
|
1515
|
-
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
1516
|
-
this.decorations = buildDecorations2(update2.view, options);
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
}, {
|
|
1520
|
-
decorations: (v) => v.decorations
|
|
1521
|
-
})
|
|
1522
|
-
];
|
|
1523
|
-
if (options.onHover) {
|
|
1524
|
-
extensions.push(
|
|
1525
|
-
// https://codemirror.net/examples/tooltip
|
|
1526
|
-
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1527
|
-
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1528
|
-
hoverTooltip2((view, pos, side) => {
|
|
1529
|
-
const syntax = syntaxTree2(view.state).resolveInner(pos, side);
|
|
1530
|
-
let link2 = null;
|
|
1531
|
-
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
1532
|
-
link2 = node.name === "Link" ? node : null;
|
|
1533
|
-
}
|
|
1534
|
-
const url = link2 && link2.getChild("URL");
|
|
1535
|
-
if (!url || !link2) {
|
|
1536
|
-
return null;
|
|
1537
|
-
}
|
|
1538
|
-
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
1539
|
-
return {
|
|
1540
|
-
pos: link2.from,
|
|
1541
|
-
end: link2.to,
|
|
1542
|
-
above: true,
|
|
1543
|
-
create: () => {
|
|
1544
|
-
const el = document.createElement("div");
|
|
1545
|
-
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1546
|
-
options.onHover(el, urlText);
|
|
1547
|
-
return {
|
|
1548
|
-
dom: el,
|
|
1549
|
-
offset: {
|
|
1550
|
-
x: 0,
|
|
1551
|
-
y: 4
|
|
1552
|
-
}
|
|
1553
|
-
};
|
|
1554
|
-
}
|
|
1555
|
-
};
|
|
1556
|
-
})
|
|
1557
|
-
);
|
|
1558
|
-
}
|
|
1559
|
-
return extensions;
|
|
1560
|
-
};
|
|
1561
|
-
var LinkButton = class extends WidgetType4 {
|
|
1562
|
-
constructor(_url, _onAttach) {
|
|
1563
|
-
super();
|
|
1564
|
-
this._url = _url;
|
|
1565
|
-
this._onAttach = _onAttach;
|
|
1566
|
-
}
|
|
1567
|
-
eq(other) {
|
|
1568
|
-
return this._url === other._url;
|
|
1569
|
-
}
|
|
1570
|
-
toDOM(view) {
|
|
1571
|
-
const el = document.createElement("span");
|
|
1572
|
-
this._onAttach(el, this._url);
|
|
1573
|
-
return el;
|
|
1574
|
-
}
|
|
1575
|
-
};
|
|
1576
|
-
var LinkText = class extends WidgetType4 {
|
|
1577
|
-
constructor(_text, _url) {
|
|
1578
|
-
super();
|
|
1579
|
-
this._text = _text;
|
|
1580
|
-
this._url = _url;
|
|
1581
|
-
}
|
|
1582
|
-
eq(other) {
|
|
1583
|
-
return this._url === other._url;
|
|
1584
|
-
}
|
|
1585
|
-
toDOM(view) {
|
|
1586
|
-
const link2 = document.createElement("a");
|
|
1587
|
-
link2.setAttribute("class", "cm-link");
|
|
1588
|
-
link2.textContent = this._text;
|
|
1589
|
-
if (this._url) {
|
|
1590
|
-
link2.setAttribute("rel", "noreferrer");
|
|
1591
|
-
link2.setAttribute("target", "_blank");
|
|
1592
|
-
link2.href = this._url;
|
|
1593
|
-
} else {
|
|
1594
|
-
link2.onclick = () => {
|
|
1595
|
-
view.dispatch({
|
|
1596
|
-
selection: {
|
|
1597
|
-
anchor: view.posAtDOM(link2)
|
|
1598
|
-
}
|
|
1599
|
-
});
|
|
1600
|
-
};
|
|
1601
|
-
}
|
|
1602
|
-
return link2;
|
|
1603
|
-
}
|
|
1604
|
-
};
|
|
1605
|
-
var buildDecorations2 = (view, options) => {
|
|
1606
|
-
const builder = new RangeSetBuilder();
|
|
1607
|
-
const { state } = view;
|
|
1608
|
-
const cursor = state.selection.main.head;
|
|
1609
|
-
for (const { from, to } of view.visibleRanges) {
|
|
1610
|
-
syntaxTree2(state).iterate({
|
|
1611
|
-
enter: (node) => {
|
|
1612
|
-
if (node.name === "Link") {
|
|
1613
|
-
const marks2 = node.node.getChildren("LinkMark");
|
|
1614
|
-
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1615
|
-
const urlNode = node.node.getChild("URL");
|
|
1616
|
-
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1617
|
-
if (!url) {
|
|
1618
|
-
return false;
|
|
1619
|
-
}
|
|
1620
|
-
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1621
|
-
builder.add(node.from, node.to, Decoration6.replace({
|
|
1622
|
-
widget: new LinkText(text, options.link ? url : void 0)
|
|
1623
|
-
}));
|
|
1624
|
-
}
|
|
1625
|
-
if (options.onRender) {
|
|
1626
|
-
builder.add(node.to, node.to, Decoration6.replace({
|
|
1627
|
-
widget: new LinkButton(url, options.onRender)
|
|
1628
|
-
}));
|
|
1629
|
-
}
|
|
1630
|
-
return false;
|
|
1631
|
-
}
|
|
1632
|
-
},
|
|
1633
|
-
from,
|
|
1634
|
-
to
|
|
1635
|
-
});
|
|
1636
|
-
}
|
|
1637
|
-
return builder.finish();
|
|
1638
|
-
};
|
|
1639
|
-
|
|
1640
|
-
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1641
|
-
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
1642
|
-
var listener = ({ onFocus, onChange }) => {
|
|
1643
|
-
const extensions = [];
|
|
1644
|
-
onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
|
|
1645
|
-
onFocus(focused);
|
|
1646
|
-
return null;
|
|
1647
|
-
}));
|
|
1648
|
-
onChange && extensions.push(EditorView8.updateListener.of((update2) => {
|
|
1649
|
-
onChange(update2.state.doc.toString());
|
|
1650
|
-
}));
|
|
1651
|
-
return extensions;
|
|
1432
|
+
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1433
|
+
import { EditorView as EditorView6 } from "@codemirror/view";
|
|
1434
|
+
var listener = ({ onFocus, onChange }) => {
|
|
1435
|
+
const extensions = [];
|
|
1436
|
+
onFocus && extensions.push(EditorView6.focusChangeEffect.of((_, focused) => {
|
|
1437
|
+
onFocus(focused);
|
|
1438
|
+
return null;
|
|
1439
|
+
}));
|
|
1440
|
+
onChange && extensions.push(EditorView6.updateListener.of((update2) => {
|
|
1441
|
+
onChange(update2.state.doc.toString());
|
|
1442
|
+
}));
|
|
1443
|
+
return extensions;
|
|
1652
1444
|
};
|
|
1653
1445
|
|
|
1654
1446
|
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1655
1447
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
1656
|
-
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1448
|
+
import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1657
1449
|
import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
|
|
1658
1450
|
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
1659
1451
|
import { languages } from "@codemirror/language-data";
|
|
1660
1452
|
import { searchKeymap } from "@codemirror/search";
|
|
1661
1453
|
import { EditorState } from "@codemirror/state";
|
|
1662
1454
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
1663
|
-
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as
|
|
1455
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
1456
|
+
import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
|
|
1664
1457
|
|
|
1665
1458
|
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1666
1459
|
import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
|
|
@@ -1757,7 +1550,7 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1757
1550
|
markdownTags.QuoteMark,
|
|
1758
1551
|
markdownTags.EmphasisMark
|
|
1759
1552
|
],
|
|
1760
|
-
class:
|
|
1553
|
+
class: mark
|
|
1761
1554
|
},
|
|
1762
1555
|
// E.g., code block language (after ```).
|
|
1763
1556
|
{
|
|
@@ -1765,7 +1558,7 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1765
1558
|
tags.function(tags.variableName),
|
|
1766
1559
|
tags.labelName
|
|
1767
1560
|
],
|
|
1768
|
-
class:
|
|
1561
|
+
class: codeMark
|
|
1769
1562
|
},
|
|
1770
1563
|
{
|
|
1771
1564
|
tag: [
|
|
@@ -1776,27 +1569,27 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1776
1569
|
// Headings.
|
|
1777
1570
|
{
|
|
1778
1571
|
tag: tags.heading1,
|
|
1779
|
-
class: heading(1
|
|
1572
|
+
class: heading(1)
|
|
1780
1573
|
},
|
|
1781
1574
|
{
|
|
1782
1575
|
tag: tags.heading2,
|
|
1783
|
-
class: heading(2
|
|
1576
|
+
class: heading(2)
|
|
1784
1577
|
},
|
|
1785
1578
|
{
|
|
1786
1579
|
tag: tags.heading3,
|
|
1787
|
-
class: heading(3
|
|
1580
|
+
class: heading(3)
|
|
1788
1581
|
},
|
|
1789
1582
|
{
|
|
1790
1583
|
tag: tags.heading4,
|
|
1791
|
-
class: heading(4
|
|
1584
|
+
class: heading(4)
|
|
1792
1585
|
},
|
|
1793
1586
|
{
|
|
1794
1587
|
tag: tags.heading5,
|
|
1795
|
-
class: heading(5
|
|
1588
|
+
class: heading(5)
|
|
1796
1589
|
},
|
|
1797
1590
|
{
|
|
1798
1591
|
tag: tags.heading6,
|
|
1799
|
-
class: heading(6
|
|
1592
|
+
class: heading(6)
|
|
1800
1593
|
},
|
|
1801
1594
|
// Emphasis.
|
|
1802
1595
|
{
|
|
@@ -1822,7 +1615,7 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1822
1615
|
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
1823
1616
|
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
1824
1617
|
// the code, but all other CSS properties will be inherited.
|
|
1825
|
-
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
1618
|
+
// IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
|
|
1826
1619
|
{
|
|
1827
1620
|
tag: [
|
|
1828
1621
|
markdownTags.CodeText,
|
|
@@ -1851,19 +1644,19 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1851
1644
|
};
|
|
1852
1645
|
|
|
1853
1646
|
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1854
|
-
var
|
|
1647
|
+
var createMarkdownExtensions = ({ themeMode, placeholder: _placeholder, lineWrapping = true } = {}) => {
|
|
1855
1648
|
return [
|
|
1649
|
+
lineWrapping && EditorView7.lineWrapping,
|
|
1856
1650
|
EditorState.allowMultipleSelections.of(true),
|
|
1857
1651
|
EditorState.tabSize.of(2),
|
|
1858
|
-
|
|
1859
|
-
customKeymap(),
|
|
1652
|
+
// https://github.com/codemirror/basic-setup
|
|
1860
1653
|
bracketMatching2(),
|
|
1861
1654
|
closeBrackets2(),
|
|
1862
1655
|
crosshairCursor(),
|
|
1863
1656
|
dropCursor(),
|
|
1864
1657
|
drawSelection2(),
|
|
1865
|
-
|
|
1866
|
-
|
|
1658
|
+
highlightActiveLine2(),
|
|
1659
|
+
history2(),
|
|
1867
1660
|
indentOnInput(),
|
|
1868
1661
|
_placeholder && placeholder2(_placeholder),
|
|
1869
1662
|
// Main extension.
|
|
@@ -1887,58 +1680,602 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1887
1680
|
// https://github.com/codemirror/theme-one-dark
|
|
1888
1681
|
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
|
|
1889
1682
|
// Custom styles.
|
|
1890
|
-
syntaxHighlighting2(markdownHighlightStyle(
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
]);
|
|
1905
|
-
|
|
1906
|
-
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
1907
|
-
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
1908
|
-
var mention = ({ onSearch }) => {
|
|
1909
|
-
return autocompletion2({
|
|
1910
|
-
closeOnBlur: false,
|
|
1911
|
-
override: [
|
|
1912
|
-
(context) => {
|
|
1913
|
-
const match = context.matchBefore(/@(\w+)?/);
|
|
1914
|
-
if (!match || match.from === match.to && !context.explicit) {
|
|
1915
|
-
return null;
|
|
1916
|
-
}
|
|
1917
|
-
console.log(match);
|
|
1918
|
-
return {
|
|
1919
|
-
from: match.from,
|
|
1920
|
-
options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
|
|
1921
|
-
label: `@${value}`
|
|
1922
|
-
}))
|
|
1923
|
-
};
|
|
1924
|
-
}
|
|
1925
|
-
]
|
|
1926
|
-
});
|
|
1683
|
+
syntaxHighlighting2(markdownHighlightStyle()),
|
|
1684
|
+
keymap4.of([
|
|
1685
|
+
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
1686
|
+
indentWithTab,
|
|
1687
|
+
// https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
|
|
1688
|
+
...closeBracketsKeymap,
|
|
1689
|
+
// https://codemirror.net/docs/ref/#commands.historyKeymap
|
|
1690
|
+
...historyKeymap,
|
|
1691
|
+
// https://codemirror.net/docs/ref/#search.searchKeymap
|
|
1692
|
+
...searchKeymap,
|
|
1693
|
+
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
1694
|
+
...standardKeymap
|
|
1695
|
+
])
|
|
1696
|
+
].filter(isNotFalsy2);
|
|
1927
1697
|
};
|
|
1928
1698
|
|
|
1929
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
1930
|
-
import { syntaxTree
|
|
1931
|
-
import { RangeSetBuilder
|
|
1932
|
-
import {
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1699
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/code.ts
|
|
1700
|
+
import { syntaxTree } from "@codemirror/language";
|
|
1701
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
1702
|
+
import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Decoration3, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1703
|
+
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
1704
|
+
var styles3 = EditorView8.baseTheme({
|
|
1705
|
+
"& .cm-code": {
|
|
1706
|
+
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
1707
|
+
},
|
|
1708
|
+
"& .cm-codeblock": {
|
|
1709
|
+
width: "100%",
|
|
1710
|
+
paddingInline: "1rem !important",
|
|
1711
|
+
position: "relative",
|
|
1712
|
+
"&::after": {
|
|
1713
|
+
content: '""',
|
|
1714
|
+
position: "absolute",
|
|
1715
|
+
inset: 0
|
|
1716
|
+
}
|
|
1717
|
+
},
|
|
1718
|
+
"&light .cm-codeblock": {
|
|
1719
|
+
"&::after": {
|
|
1720
|
+
background: getToken("extend.semanticColors.input.light"),
|
|
1721
|
+
mixBlendMode: "darken"
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
"&dark .cm-codeblock": {
|
|
1725
|
+
"&::after": {
|
|
1726
|
+
background: getToken("extend.semanticColors.input.dark"),
|
|
1727
|
+
mixBlendMode: "lighten"
|
|
1728
|
+
}
|
|
1729
|
+
},
|
|
1730
|
+
"& .cm-codeblock-first": {
|
|
1731
|
+
display: "inline-block",
|
|
1732
|
+
"&::after": {
|
|
1733
|
+
borderTopLeftRadius: ".5rem",
|
|
1734
|
+
borderTopRightRadius: ".5rem"
|
|
1735
|
+
}
|
|
1736
|
+
},
|
|
1737
|
+
"& .cm-codeblock-last": {
|
|
1738
|
+
display: "inline-block",
|
|
1739
|
+
"&::after": {
|
|
1740
|
+
borderBottomLeftRadius: ".5rem",
|
|
1741
|
+
borderBottomRightRadius: ".5rem"
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
var getLineRange = (lines, from, to) => {
|
|
1746
|
+
const start = lines.findIndex((line) => line.from >= from);
|
|
1747
|
+
const end = lines.findIndex((line) => line.to >= to);
|
|
1748
|
+
return [
|
|
1749
|
+
start,
|
|
1750
|
+
end
|
|
1751
|
+
];
|
|
1939
1752
|
};
|
|
1940
|
-
var
|
|
1941
|
-
|
|
1753
|
+
var LineWidget = class extends WidgetType2 {
|
|
1754
|
+
constructor(_className) {
|
|
1755
|
+
super();
|
|
1756
|
+
this._className = _className;
|
|
1757
|
+
}
|
|
1758
|
+
toDOM() {
|
|
1759
|
+
const el = document.createElement("span");
|
|
1760
|
+
el.innerHTML = " ";
|
|
1761
|
+
el.className = mx2("cm-code cm-codeblock", this._className);
|
|
1762
|
+
return el;
|
|
1763
|
+
}
|
|
1764
|
+
};
|
|
1765
|
+
var top = new LineWidget("cm-codeblock-first");
|
|
1766
|
+
var bottom = new LineWidget("cm-codeblock-last");
|
|
1767
|
+
var buildDecorations = (view) => {
|
|
1768
|
+
const builder = new RangeSetBuilder();
|
|
1769
|
+
const { state } = view;
|
|
1770
|
+
const blocks = view.viewportLineBlocks;
|
|
1771
|
+
const cursor = state.selection.main.head;
|
|
1772
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1773
|
+
syntaxTree(state).iterate({
|
|
1774
|
+
enter: (node) => {
|
|
1775
|
+
if (node.name === "FencedCode") {
|
|
1776
|
+
const edit = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
|
|
1777
|
+
const range = getLineRange(blocks, node.from, node.to);
|
|
1778
|
+
for (let i = range[0]; i <= range[1]; i++) {
|
|
1779
|
+
const block = blocks[i];
|
|
1780
|
+
if (!edit && (i === range[0] || i === range[1])) {
|
|
1781
|
+
builder.add(block.from, block.to, Decoration3.replace({
|
|
1782
|
+
widget: i === range[0] ? top : bottom
|
|
1783
|
+
}));
|
|
1784
|
+
} else {
|
|
1785
|
+
builder.add(block.from, block.from, Decoration3.line({
|
|
1786
|
+
class: mx2("cm-code cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
1787
|
+
}));
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
},
|
|
1792
|
+
from,
|
|
1793
|
+
to
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
return builder.finish();
|
|
1797
|
+
};
|
|
1798
|
+
var code2 = () => {
|
|
1799
|
+
return [
|
|
1800
|
+
ViewPlugin3.fromClass(class {
|
|
1801
|
+
constructor(view) {
|
|
1802
|
+
this.decorations = buildDecorations(view);
|
|
1803
|
+
}
|
|
1804
|
+
update(update2) {
|
|
1805
|
+
this.decorations = buildDecorations(update2.view);
|
|
1806
|
+
}
|
|
1807
|
+
}, {
|
|
1808
|
+
decorations: (value) => value.decorations
|
|
1809
|
+
}),
|
|
1810
|
+
styles3
|
|
1811
|
+
];
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
|
|
1815
|
+
import { snippet } from "@codemirror/autocomplete";
|
|
1816
|
+
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;
|
|
1821
|
+
const changes = [];
|
|
1822
|
+
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
|
|
1839
|
+
});
|
|
1840
|
+
}
|
|
1841
|
+
return true;
|
|
1842
|
+
};
|
|
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;
|
|
1848
|
+
}
|
|
1849
|
+
view.dispatch({
|
|
1850
|
+
changes: [
|
|
1851
|
+
{
|
|
1852
|
+
from: range.from,
|
|
1853
|
+
insert: mark2
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
from: range.to,
|
|
1857
|
+
insert: mark2
|
|
1858
|
+
}
|
|
1859
|
+
]
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
return true;
|
|
1863
|
+
};
|
|
1864
|
+
var snippets = {
|
|
1865
|
+
codeblock: snippet([
|
|
1866
|
+
"```#{lang}",
|
|
1867
|
+
" #{}",
|
|
1868
|
+
"```"
|
|
1869
|
+
].join("\n")),
|
|
1870
|
+
table: snippet([
|
|
1871
|
+
"| #{col1} | #{col2} |",
|
|
1872
|
+
"| ---- | ---- |",
|
|
1873
|
+
"| #{val1} | #{val2} |",
|
|
1874
|
+
"| #{val3} | #{val4} |"
|
|
1875
|
+
].join("\n"))
|
|
1876
|
+
};
|
|
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
|
+
var insertTable = (view) => {
|
|
1884
|
+
const { selection: { main }, doc } = view.state;
|
|
1885
|
+
const { number } = doc.lineAt(main.anchor);
|
|
1886
|
+
const { from } = doc.line(number);
|
|
1887
|
+
snippets.table(view, null, from, from);
|
|
1888
|
+
};
|
|
1889
|
+
var toggleBold = toggleStyle("**");
|
|
1890
|
+
var toggleItalic = toggleStyle("_");
|
|
1891
|
+
var toggleStrikethrough = toggleStyle("~~");
|
|
1892
|
+
var toggleList = (view) => {
|
|
1893
|
+
};
|
|
1894
|
+
var formatting = (options = {}) => {
|
|
1895
|
+
return [
|
|
1896
|
+
keymap5.of([
|
|
1897
|
+
{
|
|
1898
|
+
key: "meta-b",
|
|
1899
|
+
run: toggleBold
|
|
1900
|
+
}
|
|
1901
|
+
]),
|
|
1902
|
+
styling()
|
|
1903
|
+
];
|
|
1904
|
+
};
|
|
1905
|
+
var styling = () => {
|
|
1906
|
+
const buildDecorations7 = (view) => {
|
|
1907
|
+
const builder = new RangeSetBuilder2();
|
|
1908
|
+
const { state } = view;
|
|
1909
|
+
const cursor = state.selection.main.head;
|
|
1910
|
+
const replace = (node, marks) => {
|
|
1911
|
+
if (cursor <= node.from || cursor >= node.to) {
|
|
1912
|
+
for (const mark2 of marks) {
|
|
1913
|
+
builder.add(mark2.from, mark2.to, Decoration4.replace({}));
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
};
|
|
1917
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1918
|
+
syntaxTree2(state).iterate({
|
|
1919
|
+
enter: (node) => {
|
|
1920
|
+
switch (node.name) {
|
|
1921
|
+
case "Emphasis":
|
|
1922
|
+
case "StrongEmphasis": {
|
|
1923
|
+
const marks = node.node.getChildren("EmphasisMark");
|
|
1924
|
+
replace(node, marks);
|
|
1925
|
+
break;
|
|
1926
|
+
}
|
|
1927
|
+
case "Strikethrough": {
|
|
1928
|
+
const marks = node.node.getChildren("StrikethroughMark");
|
|
1929
|
+
replace(node, marks);
|
|
1930
|
+
break;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
},
|
|
1934
|
+
from,
|
|
1935
|
+
to
|
|
1936
|
+
});
|
|
1937
|
+
}
|
|
1938
|
+
return builder.finish();
|
|
1939
|
+
};
|
|
1940
|
+
return [
|
|
1941
|
+
ViewPlugin4.fromClass(class {
|
|
1942
|
+
constructor(view) {
|
|
1943
|
+
this.decorations = buildDecorations7(view);
|
|
1944
|
+
}
|
|
1945
|
+
update(update2) {
|
|
1946
|
+
this.decorations = buildDecorations7(update2.view);
|
|
1947
|
+
}
|
|
1948
|
+
}, {
|
|
1949
|
+
decorations: (value) => value.decorations
|
|
1950
|
+
})
|
|
1951
|
+
];
|
|
1952
|
+
};
|
|
1953
|
+
|
|
1954
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
|
|
1955
|
+
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1956
|
+
import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
|
|
1957
|
+
import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
1958
|
+
var buildDecorations2 = (view) => {
|
|
1959
|
+
const builder = new RangeSetBuilder3();
|
|
1960
|
+
const { state } = view;
|
|
1961
|
+
const cursor = state.selection.main.head;
|
|
1962
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1963
|
+
syntaxTree3(state).iterate({
|
|
1964
|
+
enter: (node) => {
|
|
1965
|
+
switch (node.name) {
|
|
1966
|
+
case "ATXHeading1":
|
|
1967
|
+
case "ATXHeading2":
|
|
1968
|
+
case "ATXHeading3":
|
|
1969
|
+
case "ATXHeading4":
|
|
1970
|
+
case "ATXHeading5":
|
|
1971
|
+
case "ATXHeading6": {
|
|
1972
|
+
const mark2 = node.node.getChild("HeaderMark");
|
|
1973
|
+
if (mark2) {
|
|
1974
|
+
if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1975
|
+
builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
},
|
|
1981
|
+
from,
|
|
1982
|
+
to
|
|
1983
|
+
});
|
|
1984
|
+
}
|
|
1985
|
+
return builder.finish();
|
|
1986
|
+
};
|
|
1987
|
+
var heading2 = () => {
|
|
1988
|
+
return [
|
|
1989
|
+
ViewPlugin5.fromClass(class {
|
|
1990
|
+
constructor(view) {
|
|
1991
|
+
this.decorations = buildDecorations2(view);
|
|
1992
|
+
}
|
|
1993
|
+
update(update2) {
|
|
1994
|
+
this.decorations = buildDecorations2(update2.view);
|
|
1995
|
+
}
|
|
1996
|
+
}, {
|
|
1997
|
+
decorations: (value) => value.decorations
|
|
1998
|
+
})
|
|
1999
|
+
];
|
|
2000
|
+
};
|
|
2001
|
+
|
|
2002
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
|
|
2003
|
+
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
2004
|
+
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({
|
|
2007
|
+
"& .cm-hr": {
|
|
2008
|
+
// Note that block-level decorations should not have vertical margins,
|
|
2009
|
+
borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
2010
|
+
}
|
|
2011
|
+
});
|
|
2012
|
+
var HorizontalRuleWidget = class extends WidgetType3 {
|
|
2013
|
+
toDOM() {
|
|
2014
|
+
const el = document.createElement("div");
|
|
2015
|
+
el.className = "cm-hr";
|
|
2016
|
+
return el;
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
var decoration = Decoration6.replace({
|
|
2020
|
+
widget: new HorizontalRuleWidget()
|
|
2021
|
+
});
|
|
2022
|
+
var buildDecorations3 = (view) => {
|
|
2023
|
+
const builder = new RangeSetBuilder4();
|
|
2024
|
+
const { state } = view;
|
|
2025
|
+
const cursor = state.selection.main.head;
|
|
2026
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2027
|
+
syntaxTree4(state).iterate({
|
|
2028
|
+
enter: (node) => {
|
|
2029
|
+
if (node.name === "HorizontalRule") {
|
|
2030
|
+
if (cursor <= node.from || cursor >= node.to) {
|
|
2031
|
+
builder.add(node.from, node.to, decoration);
|
|
2032
|
+
}
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
from,
|
|
2036
|
+
to
|
|
2037
|
+
});
|
|
2038
|
+
}
|
|
2039
|
+
return builder.finish();
|
|
2040
|
+
};
|
|
2041
|
+
var hr = () => {
|
|
2042
|
+
return [
|
|
2043
|
+
ViewPlugin6.fromClass(class {
|
|
2044
|
+
constructor(view) {
|
|
2045
|
+
this.decorations = buildDecorations3(view);
|
|
2046
|
+
}
|
|
2047
|
+
update(update2) {
|
|
2048
|
+
this.decorations = buildDecorations3(update2.view);
|
|
2049
|
+
}
|
|
2050
|
+
}, {
|
|
2051
|
+
decorations: (value) => value.decorations
|
|
2052
|
+
}),
|
|
2053
|
+
styles4
|
|
2054
|
+
];
|
|
2055
|
+
};
|
|
2056
|
+
|
|
2057
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/image.ts
|
|
2058
|
+
import { syntaxTree as syntaxTree5 } from "@codemirror/language";
|
|
2059
|
+
import { StateField as StateField3 } from "@codemirror/state";
|
|
2060
|
+
import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
2061
|
+
var image = (options = {}) => {
|
|
2062
|
+
return StateField3.define({
|
|
2063
|
+
create: (state) => {
|
|
2064
|
+
return Decoration7.set(buildDecorations4(0, state.doc.length, state));
|
|
2065
|
+
},
|
|
2066
|
+
update: (value, tr) => {
|
|
2067
|
+
if (!tr.docChanged && !tr.selection) {
|
|
2068
|
+
return value;
|
|
2069
|
+
}
|
|
2070
|
+
const cursor = tr.state.selection.main.head;
|
|
2071
|
+
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
2072
|
+
let from = Math.min(cursor, oldCursor);
|
|
2073
|
+
let to = Math.max(cursor, oldCursor);
|
|
2074
|
+
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
2075
|
+
from = Math.min(from, fromB);
|
|
2076
|
+
to = Math.max(to, toB);
|
|
2077
|
+
});
|
|
2078
|
+
from = tr.state.doc.lineAt(from).from;
|
|
2079
|
+
to = tr.state.doc.lineAt(to).to;
|
|
2080
|
+
return value.map(tr.changes).update({
|
|
2081
|
+
filterFrom: from,
|
|
2082
|
+
filterTo: to,
|
|
2083
|
+
filter: () => false,
|
|
2084
|
+
add: buildDecorations4(from, to, tr.state)
|
|
2085
|
+
});
|
|
2086
|
+
},
|
|
2087
|
+
provide: (field) => EditorView10.decorations.from(field)
|
|
2088
|
+
});
|
|
2089
|
+
};
|
|
2090
|
+
var buildDecorations4 = (from, to, state) => {
|
|
2091
|
+
const decorations = [];
|
|
2092
|
+
const cursor = state.selection.main.head;
|
|
2093
|
+
syntaxTree5(state).iterate({
|
|
2094
|
+
enter: (node) => {
|
|
2095
|
+
if (node.name === "Image") {
|
|
2096
|
+
const urlNode = node.node.getChild("URL");
|
|
2097
|
+
if (urlNode) {
|
|
2098
|
+
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
2099
|
+
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
2100
|
+
decorations.push(Decoration7.replace({
|
|
2101
|
+
block: true,
|
|
2102
|
+
widget: new ImageWidget(url)
|
|
2103
|
+
}).range(hide ? node.from : node.to, node.to));
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
},
|
|
2107
|
+
from,
|
|
2108
|
+
to
|
|
2109
|
+
});
|
|
2110
|
+
return decorations;
|
|
2111
|
+
};
|
|
2112
|
+
var ImageWidget = class extends WidgetType4 {
|
|
2113
|
+
constructor(_url) {
|
|
2114
|
+
super();
|
|
2115
|
+
this._url = _url;
|
|
2116
|
+
}
|
|
2117
|
+
eq(other) {
|
|
2118
|
+
return this._url === other._url;
|
|
2119
|
+
}
|
|
2120
|
+
toDOM(view) {
|
|
2121
|
+
const img = document.createElement("img");
|
|
2122
|
+
img.setAttribute("src", this._url);
|
|
2123
|
+
img.setAttribute("class", "cm-image");
|
|
2124
|
+
img.onload = () => img.classList.add("cm-loaded-image");
|
|
2125
|
+
return img;
|
|
2126
|
+
}
|
|
2127
|
+
};
|
|
2128
|
+
|
|
2129
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/link.ts
|
|
2130
|
+
import { syntaxTree as syntaxTree6 } from "@codemirror/language";
|
|
2131
|
+
import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
|
|
2132
|
+
import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
2133
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
2134
|
+
var link = (options = {}) => {
|
|
2135
|
+
const extensions = [
|
|
2136
|
+
ViewPlugin7.fromClass(class {
|
|
2137
|
+
constructor(view) {
|
|
2138
|
+
this.decorations = buildDecorations5(view, options);
|
|
2139
|
+
}
|
|
2140
|
+
update(update2) {
|
|
2141
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
2142
|
+
this.decorations = buildDecorations5(update2.view, options);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
}, {
|
|
2146
|
+
decorations: (value) => value.decorations
|
|
2147
|
+
})
|
|
2148
|
+
];
|
|
2149
|
+
if (options.onHover) {
|
|
2150
|
+
extensions.push(
|
|
2151
|
+
// https://codemirror.net/examples/tooltip
|
|
2152
|
+
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
2153
|
+
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
2154
|
+
hoverTooltip2((view, pos, side) => {
|
|
2155
|
+
const syntax = syntaxTree6(view.state).resolveInner(pos, side);
|
|
2156
|
+
let link2 = null;
|
|
2157
|
+
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
2158
|
+
link2 = node.name === "Link" ? node : null;
|
|
2159
|
+
}
|
|
2160
|
+
const url = link2 && link2.getChild("URL");
|
|
2161
|
+
if (!url || !link2) {
|
|
2162
|
+
return null;
|
|
2163
|
+
}
|
|
2164
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
2165
|
+
return {
|
|
2166
|
+
pos: link2.from,
|
|
2167
|
+
end: link2.to,
|
|
2168
|
+
above: true,
|
|
2169
|
+
create: () => {
|
|
2170
|
+
const el = document.createElement("div");
|
|
2171
|
+
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
2172
|
+
options.onHover(el, urlText);
|
|
2173
|
+
return {
|
|
2174
|
+
dom: el,
|
|
2175
|
+
offset: {
|
|
2176
|
+
x: 0,
|
|
2177
|
+
y: 4
|
|
2178
|
+
}
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
2181
|
+
};
|
|
2182
|
+
})
|
|
2183
|
+
);
|
|
2184
|
+
}
|
|
2185
|
+
return extensions;
|
|
2186
|
+
};
|
|
2187
|
+
var LinkButton = class extends WidgetType5 {
|
|
2188
|
+
constructor(_url, _onAttach) {
|
|
2189
|
+
super();
|
|
2190
|
+
this._url = _url;
|
|
2191
|
+
this._onAttach = _onAttach;
|
|
2192
|
+
}
|
|
2193
|
+
eq(other) {
|
|
2194
|
+
return this._url === other._url;
|
|
2195
|
+
}
|
|
2196
|
+
toDOM(view) {
|
|
2197
|
+
const el = document.createElement("span");
|
|
2198
|
+
this._onAttach(el, this._url);
|
|
2199
|
+
return el;
|
|
2200
|
+
}
|
|
2201
|
+
};
|
|
2202
|
+
var LinkText = class extends WidgetType5 {
|
|
2203
|
+
constructor(_text, _url) {
|
|
2204
|
+
super();
|
|
2205
|
+
this._text = _text;
|
|
2206
|
+
this._url = _url;
|
|
2207
|
+
}
|
|
2208
|
+
eq(other) {
|
|
2209
|
+
return this._url === other._url;
|
|
2210
|
+
}
|
|
2211
|
+
toDOM(view) {
|
|
2212
|
+
const link2 = document.createElement("a");
|
|
2213
|
+
link2.setAttribute("class", "cm-link");
|
|
2214
|
+
link2.textContent = this._text;
|
|
2215
|
+
if (this._url) {
|
|
2216
|
+
link2.setAttribute("rel", "noreferrer");
|
|
2217
|
+
link2.setAttribute("target", "_blank");
|
|
2218
|
+
link2.href = this._url;
|
|
2219
|
+
} else {
|
|
2220
|
+
link2.onclick = () => {
|
|
2221
|
+
view.dispatch({
|
|
2222
|
+
selection: {
|
|
2223
|
+
anchor: view.posAtDOM(link2)
|
|
2224
|
+
}
|
|
2225
|
+
});
|
|
2226
|
+
};
|
|
2227
|
+
}
|
|
2228
|
+
return link2;
|
|
2229
|
+
}
|
|
2230
|
+
};
|
|
2231
|
+
var buildDecorations5 = (view, options) => {
|
|
2232
|
+
const builder = new RangeSetBuilder5();
|
|
2233
|
+
const { state } = view;
|
|
2234
|
+
const cursor = state.selection.main.head;
|
|
2235
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2236
|
+
syntaxTree6(state).iterate({
|
|
2237
|
+
enter: (node) => {
|
|
2238
|
+
if (node.name === "Link") {
|
|
2239
|
+
const marks = node.node.getChildren("LinkMark");
|
|
2240
|
+
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : "";
|
|
2241
|
+
const urlNode = node.node.getChild("URL");
|
|
2242
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
2243
|
+
if (!url) {
|
|
2244
|
+
return false;
|
|
2245
|
+
}
|
|
2246
|
+
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
2247
|
+
builder.add(node.from, node.to, Decoration8.replace({
|
|
2248
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
2249
|
+
}));
|
|
2250
|
+
}
|
|
2251
|
+
if (options.onRender) {
|
|
2252
|
+
builder.add(node.to, node.to, Decoration8.replace({
|
|
2253
|
+
widget: new LinkButton(url, options.onRender)
|
|
2254
|
+
}));
|
|
2255
|
+
}
|
|
2256
|
+
return false;
|
|
2257
|
+
}
|
|
2258
|
+
},
|
|
2259
|
+
from,
|
|
2260
|
+
to
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2263
|
+
return builder.finish();
|
|
2264
|
+
};
|
|
2265
|
+
|
|
2266
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/table.ts
|
|
2267
|
+
import { syntaxTree as syntaxTree7 } from "@codemirror/language";
|
|
2268
|
+
import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
|
|
2269
|
+
import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
|
|
2270
|
+
var table = (options = {}) => {
|
|
2271
|
+
return StateField4.define({
|
|
2272
|
+
create: (state) => update(state, options),
|
|
2273
|
+
update: (_, tr) => update(tr.state, options),
|
|
2274
|
+
provide: (field) => EditorView11.decorations.from(field)
|
|
2275
|
+
});
|
|
2276
|
+
};
|
|
2277
|
+
var update = (state, options) => {
|
|
2278
|
+
const builder = new RangeSetBuilder6();
|
|
1942
2279
|
const cursor = state.selection.main.head;
|
|
1943
2280
|
const tables = [];
|
|
1944
2281
|
const getTable = () => tables[tables.length - 1];
|
|
@@ -1946,7 +2283,7 @@ var update = (state, options) => {
|
|
|
1946
2283
|
const table2 = getTable();
|
|
1947
2284
|
return table2.rows?.[table2.rows.length - 1];
|
|
1948
2285
|
};
|
|
1949
|
-
|
|
2286
|
+
syntaxTree7(state).iterate({
|
|
1950
2287
|
enter: (node) => {
|
|
1951
2288
|
switch (node.name) {
|
|
1952
2289
|
case "Table": {
|
|
@@ -1978,16 +2315,16 @@ var update = (state, options) => {
|
|
|
1978
2315
|
});
|
|
1979
2316
|
tables.forEach((table2) => {
|
|
1980
2317
|
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1981
|
-
hide && builder.add(table2.from, table2.to,
|
|
2318
|
+
hide && builder.add(table2.from, table2.to, Decoration9.replace({
|
|
1982
2319
|
widget: new TableWidget(table2)
|
|
1983
2320
|
}));
|
|
1984
|
-
builder.add(table2.from, table2.to,
|
|
2321
|
+
builder.add(table2.from, table2.to, Decoration9.mark({
|
|
1985
2322
|
class: "cm-table"
|
|
1986
2323
|
}));
|
|
1987
2324
|
});
|
|
1988
2325
|
return builder.finish();
|
|
1989
2326
|
};
|
|
1990
|
-
var TableWidget = class extends
|
|
2327
|
+
var TableWidget = class extends WidgetType6 {
|
|
1991
2328
|
constructor(_table) {
|
|
1992
2329
|
super();
|
|
1993
2330
|
this._table = _table;
|
|
@@ -2024,92 +2361,139 @@ var TableWidget = class extends WidgetType5 {
|
|
|
2024
2361
|
}
|
|
2025
2362
|
};
|
|
2026
2363
|
|
|
2027
|
-
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
2028
|
-
import {
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2364
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
|
|
2365
|
+
import { syntaxTree as syntaxTree8 } from "@codemirror/language";
|
|
2366
|
+
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({
|
|
2369
|
+
"& .cm-task": {
|
|
2370
|
+
color: getToken("extend.colors.blue.500")
|
|
2371
|
+
},
|
|
2372
|
+
"& .cm-task-checkbox": {
|
|
2373
|
+
marginLeft: "4px",
|
|
2374
|
+
marginRight: "4px"
|
|
2033
2375
|
}
|
|
2034
2376
|
});
|
|
2035
|
-
var CheckboxWidget = class extends
|
|
2036
|
-
constructor(_checked
|
|
2377
|
+
var CheckboxWidget = class extends WidgetType7 {
|
|
2378
|
+
constructor(_checked) {
|
|
2037
2379
|
super();
|
|
2038
2380
|
this._checked = _checked;
|
|
2039
|
-
this._indent = _indent;
|
|
2040
|
-
this._onCheck = _onCheck;
|
|
2041
2381
|
}
|
|
2042
2382
|
eq(other) {
|
|
2043
|
-
return this._checked === other._checked
|
|
2383
|
+
return this._checked === other._checked;
|
|
2044
2384
|
}
|
|
2045
2385
|
toDOM(view) {
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2048
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
2049
|
-
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
2050
|
-
const input = wrap.appendChild(document.createElement("input"));
|
|
2386
|
+
const input = document.createElement("input");
|
|
2387
|
+
input.className = "cm-task-checkbox";
|
|
2051
2388
|
input.type = "checkbox";
|
|
2052
2389
|
input.checked = this._checked;
|
|
2053
|
-
if (
|
|
2390
|
+
if (view.state.readOnly) {
|
|
2054
2391
|
input.setAttribute("disabled", "true");
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2392
|
+
} else {
|
|
2393
|
+
input.onmousedown = (event) => {
|
|
2394
|
+
const pos = view.posAtDOM(input);
|
|
2395
|
+
const text = view.state.sliceDoc(pos, pos + 3);
|
|
2396
|
+
if (text === (this._checked ? "[x]" : "[ ]")) {
|
|
2397
|
+
view.dispatch({
|
|
2398
|
+
changes: {
|
|
2399
|
+
from: pos + 1,
|
|
2400
|
+
to: pos + 2,
|
|
2401
|
+
insert: this._checked ? " " : "x"
|
|
2402
|
+
}
|
|
2403
|
+
});
|
|
2404
|
+
event.preventDefault();
|
|
2405
|
+
}
|
|
2060
2406
|
};
|
|
2061
2407
|
}
|
|
2062
|
-
return
|
|
2408
|
+
return input;
|
|
2063
2409
|
}
|
|
2064
2410
|
ignoreEvent() {
|
|
2065
2411
|
return false;
|
|
2066
2412
|
}
|
|
2067
2413
|
};
|
|
2414
|
+
var checkedDecoration = Decoration10.replace({
|
|
2415
|
+
widget: new CheckboxWidget(true)
|
|
2416
|
+
});
|
|
2417
|
+
var uncheckedDecoration = Decoration10.replace({
|
|
2418
|
+
widget: new CheckboxWidget(false)
|
|
2419
|
+
});
|
|
2420
|
+
var buildDecorations6 = (view) => {
|
|
2421
|
+
const builder = new RangeSetBuilder7();
|
|
2422
|
+
const { state } = view;
|
|
2423
|
+
const cursor = state.selection.main.head;
|
|
2424
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2425
|
+
syntaxTree8(state).iterate({
|
|
2426
|
+
enter: (node) => {
|
|
2427
|
+
if (node.name === "TaskMarker") {
|
|
2428
|
+
if (cursor < node.from || cursor > node.to) {
|
|
2429
|
+
const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
|
|
2430
|
+
builder.add(node.from - 2, node.from - 1, Decoration10.mark({
|
|
2431
|
+
class: "cm-task"
|
|
2432
|
+
}));
|
|
2433
|
+
builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
|
|
2434
|
+
}
|
|
2435
|
+
}
|
|
2436
|
+
},
|
|
2437
|
+
from,
|
|
2438
|
+
to
|
|
2439
|
+
});
|
|
2440
|
+
}
|
|
2441
|
+
return builder.finish();
|
|
2442
|
+
};
|
|
2068
2443
|
var tasklist = (options = {}) => {
|
|
2069
|
-
const taskMatcher = new MatchDecorator2({
|
|
2070
|
-
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
2071
|
-
decoration: (match, view, pos) => {
|
|
2072
|
-
const indent = Math.floor(match[1].length / 2);
|
|
2073
|
-
const checked = match[2] === "x" || match[2] === "X";
|
|
2074
|
-
return Decoration8.replace({
|
|
2075
|
-
widget: new CheckboxWidget(checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
2076
|
-
const idx = pos + match[0].indexOf("[") + 1;
|
|
2077
|
-
view.dispatch({
|
|
2078
|
-
changes: {
|
|
2079
|
-
from: idx,
|
|
2080
|
-
to: idx + 1,
|
|
2081
|
-
insert: checked2 ? "x" : " "
|
|
2082
|
-
},
|
|
2083
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
2084
|
-
selection: {
|
|
2085
|
-
anchor: idx + 3
|
|
2086
|
-
}
|
|
2087
|
-
});
|
|
2088
|
-
})
|
|
2089
|
-
});
|
|
2090
|
-
}
|
|
2091
|
-
});
|
|
2092
|
-
const tasks = ViewPlugin6.fromClass(class {
|
|
2093
|
-
constructor(view) {
|
|
2094
|
-
this.tasks = taskMatcher.createDeco(view);
|
|
2095
|
-
}
|
|
2096
|
-
update(update2) {
|
|
2097
|
-
this.tasks = taskMatcher.updateDeco(update2, this.tasks);
|
|
2098
|
-
}
|
|
2099
|
-
}, {
|
|
2100
|
-
decorations: (value) => value.tasks,
|
|
2101
|
-
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2102
|
-
return view.plugin(plugin)?.tasks || Decoration8.none;
|
|
2103
|
-
})
|
|
2104
|
-
});
|
|
2105
2444
|
return [
|
|
2106
|
-
|
|
2445
|
+
ViewPlugin8.fromClass(class {
|
|
2446
|
+
constructor(view) {
|
|
2447
|
+
this.decorations = buildDecorations6(view);
|
|
2448
|
+
}
|
|
2449
|
+
update(update2) {
|
|
2450
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
2451
|
+
this.decorations = buildDecorations6(update2.view);
|
|
2452
|
+
}
|
|
2453
|
+
}
|
|
2454
|
+
}, {
|
|
2455
|
+
decorations: (value) => value.decorations
|
|
2456
|
+
}),
|
|
2107
2457
|
styles5
|
|
2108
2458
|
];
|
|
2109
2459
|
};
|
|
2110
2460
|
|
|
2461
|
+
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
2462
|
+
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
2463
|
+
var mention = ({ onSearch }) => {
|
|
2464
|
+
return autocompletion2({
|
|
2465
|
+
// TODO(burdon): Not working.
|
|
2466
|
+
activateOnTyping: true,
|
|
2467
|
+
// activateOnTypingDelay: 100,
|
|
2468
|
+
// selectOnOpen: true,
|
|
2469
|
+
closeOnBlur: false,
|
|
2470
|
+
// defaultKeymap: false,
|
|
2471
|
+
icons: false,
|
|
2472
|
+
override: [
|
|
2473
|
+
(context) => {
|
|
2474
|
+
console.log(context);
|
|
2475
|
+
const match = context.matchBefore(/@(\w+)?/);
|
|
2476
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
2477
|
+
return null;
|
|
2478
|
+
}
|
|
2479
|
+
return {
|
|
2480
|
+
from: match.from,
|
|
2481
|
+
options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
|
|
2482
|
+
label: `@${value}`
|
|
2483
|
+
}))
|
|
2484
|
+
};
|
|
2485
|
+
}
|
|
2486
|
+
]
|
|
2487
|
+
});
|
|
2488
|
+
};
|
|
2489
|
+
|
|
2490
|
+
// packages/ui/react-ui-editor/src/extensions/outliner.ts
|
|
2491
|
+
var outliner = (options = {}) => {
|
|
2492
|
+
return [];
|
|
2493
|
+
};
|
|
2494
|
+
|
|
2111
2495
|
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
2112
|
-
import { keymap as
|
|
2496
|
+
import { keymap as keymap6 } from "@codemirror/view";
|
|
2113
2497
|
var defaultItems = [
|
|
2114
2498
|
"hello world!",
|
|
2115
2499
|
"this is a test.",
|
|
@@ -2119,7 +2503,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
|
2119
2503
|
let t;
|
|
2120
2504
|
let idx = 0;
|
|
2121
2505
|
return [
|
|
2122
|
-
|
|
2506
|
+
keymap6.of([
|
|
2123
2507
|
{
|
|
2124
2508
|
// Reset.
|
|
2125
2509
|
key: "alt-meta-'",
|
|
@@ -2165,27 +2549,6 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
|
2165
2549
|
];
|
|
2166
2550
|
};
|
|
2167
2551
|
|
|
2168
|
-
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2169
|
-
import { yCollab } from "y-codemirror.next";
|
|
2170
|
-
|
|
2171
|
-
// packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
|
|
2172
|
-
import * as Y from "yjs";
|
|
2173
|
-
import { arrayToString, stringToArray } from "@dxos/util";
|
|
2174
|
-
var cursorConverter2 = (text) => ({
|
|
2175
|
-
toCursor: (index, assoc) => {
|
|
2176
|
-
return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
|
|
2177
|
-
},
|
|
2178
|
-
fromCursor: (cursor) => {
|
|
2179
|
-
return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
|
|
2180
|
-
}
|
|
2181
|
-
});
|
|
2182
|
-
|
|
2183
|
-
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2184
|
-
var yjs = (content, awareness2) => [
|
|
2185
|
-
Cursor.converter.of(cursorConverter2(content)),
|
|
2186
|
-
yCollab(content, awareness2)
|
|
2187
|
-
];
|
|
2188
|
-
|
|
2189
2552
|
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
2190
2553
|
import get3 from "lodash.get";
|
|
2191
2554
|
var defaultTheme = {
|
|
@@ -2217,8 +2580,9 @@ var defaultTheme = {
|
|
|
2217
2580
|
outline: "none"
|
|
2218
2581
|
},
|
|
2219
2582
|
".cm-scroller": {
|
|
2220
|
-
overflow:
|
|
2221
|
-
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
2583
|
+
// overflow: 'visible',
|
|
2584
|
+
fontFamily: get3(tokens, "fontFamily.mono", []).join(","),
|
|
2585
|
+
lineHeight: 1.4
|
|
2222
2586
|
},
|
|
2223
2587
|
".cm-content": {
|
|
2224
2588
|
padding: 0,
|
|
@@ -2252,7 +2616,7 @@ var defaultTheme = {
|
|
|
2252
2616
|
paddingInline: 0
|
|
2253
2617
|
},
|
|
2254
2618
|
".cm-activeLine": {
|
|
2255
|
-
background: "
|
|
2619
|
+
background: "transparent"
|
|
2256
2620
|
},
|
|
2257
2621
|
//
|
|
2258
2622
|
// Selection
|
|
@@ -2423,90 +2787,88 @@ var codeTheme = {
|
|
|
2423
2787
|
};
|
|
2424
2788
|
|
|
2425
2789
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
2790
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
|
|
2426
2791
|
var EditorModes = [
|
|
2427
2792
|
"default",
|
|
2428
2793
|
"vim"
|
|
2429
2794
|
];
|
|
2430
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model,
|
|
2431
|
-
const tabsterDOMAttribute = useFocusableGroup({
|
|
2432
|
-
tabBehavior: "limited"
|
|
2433
|
-
});
|
|
2795
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
|
|
2434
2796
|
const { themeMode } = useThemeContext();
|
|
2435
2797
|
const rootRef = useRef(null);
|
|
2436
2798
|
const [view, setView] = useState(null);
|
|
2437
2799
|
useImperativeHandle(forwardedRef, () => view, [
|
|
2438
2800
|
view
|
|
2439
2801
|
]);
|
|
2440
|
-
|
|
2441
|
-
if (
|
|
2442
|
-
view.
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
awareness2.setLocalStateField("user", {
|
|
2452
|
-
name: peer.name ?? generateName(peer.id),
|
|
2453
|
-
color: getColorForValue({
|
|
2454
|
-
value: peer.id,
|
|
2455
|
-
type: "color"
|
|
2456
|
-
}),
|
|
2457
|
-
colorLight: getColorForValue({
|
|
2458
|
-
value: peer.id,
|
|
2459
|
-
themeMode,
|
|
2460
|
-
type: "highlight"
|
|
2461
|
-
})
|
|
2462
|
-
});
|
|
2463
|
-
}
|
|
2464
|
-
}, [
|
|
2465
|
-
awareness2,
|
|
2466
|
-
peer,
|
|
2467
|
-
themeMode
|
|
2468
|
-
]);
|
|
2469
|
-
useEffect(() => {
|
|
2470
|
-
if (view && comments2 !== void 0) {
|
|
2471
|
-
view.dispatch({
|
|
2472
|
-
effects: setCommentRange.of({
|
|
2473
|
-
model,
|
|
2474
|
-
comments: comments2
|
|
2475
|
-
})
|
|
2476
|
-
});
|
|
2802
|
+
useEffect2(() => {
|
|
2803
|
+
if (autoFocus && !view?.hasFocus) {
|
|
2804
|
+
if (view?.state.selection.main?.from === 0) {
|
|
2805
|
+
const { to } = view.state.doc.lineAt(0);
|
|
2806
|
+
view?.dispatch({
|
|
2807
|
+
selection: {
|
|
2808
|
+
anchor: to
|
|
2809
|
+
}
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
view?.focus();
|
|
2477
2813
|
}
|
|
2478
2814
|
}, [
|
|
2479
2815
|
view,
|
|
2480
|
-
|
|
2816
|
+
autoFocus
|
|
2481
2817
|
]);
|
|
2482
|
-
|
|
2818
|
+
useEffect2(() => {
|
|
2483
2819
|
if (!model || !rootRef.current) {
|
|
2484
2820
|
return;
|
|
2485
2821
|
}
|
|
2486
2822
|
const state = EditorState2.create({
|
|
2487
2823
|
doc: model.text(),
|
|
2488
|
-
// TODO(burdon): Composer should store and set selection when switching documents.
|
|
2489
2824
|
selection,
|
|
2490
2825
|
extensions: [
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2826
|
+
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
2827
|
+
EditorView13.exceptionSink.of((err) => {
|
|
2828
|
+
log2.catch(err, void 0, {
|
|
2829
|
+
F: __dxlog_file3,
|
|
2830
|
+
L: 134,
|
|
2831
|
+
S: void 0,
|
|
2832
|
+
C: (f, a) => f(...a)
|
|
2833
|
+
});
|
|
2834
|
+
}),
|
|
2494
2835
|
// Theme.
|
|
2495
|
-
// TODO(burdon): Make
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2836
|
+
// TODO(burdon): Make configurable.
|
|
2837
|
+
EditorView13.baseTheme(defaultTheme),
|
|
2838
|
+
EditorView13.theme(theme ?? {}),
|
|
2839
|
+
EditorView13.darkTheme.of(themeMode === "dark"),
|
|
2840
|
+
EditorView13.editorAttributes.of({
|
|
2841
|
+
class: slots.editor?.className ?? ""
|
|
2842
|
+
}),
|
|
2843
|
+
EditorView13.contentAttributes.of({
|
|
2844
|
+
class: slots.content?.className ?? ""
|
|
2845
|
+
}),
|
|
2846
|
+
// State.
|
|
2847
|
+
EditorView13.editable.of(!readonly),
|
|
2848
|
+
EditorState2.readOnly.of(!!readonly),
|
|
2499
2849
|
// Storage and replication.
|
|
2850
|
+
// NOTE: This must come before user extensions.
|
|
2500
2851
|
model.extension,
|
|
2852
|
+
// TODO(burdon): Factor out? (Requires special handling for Escape/Enter below).
|
|
2853
|
+
editorMode === "vim" && vim(),
|
|
2501
2854
|
// Custom.
|
|
2502
2855
|
...extensions
|
|
2503
|
-
].filter(
|
|
2856
|
+
].filter(isNotFalsy3)
|
|
2504
2857
|
});
|
|
2505
|
-
|
|
2506
|
-
|
|
2858
|
+
const newView = new EditorView13({
|
|
2859
|
+
state,
|
|
2507
2860
|
parent: rootRef.current,
|
|
2508
|
-
|
|
2861
|
+
scrollTo,
|
|
2862
|
+
// NOTE: Uncomment to debug/monitor all transactions.
|
|
2863
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
2864
|
+
dispatchTransactions: (trs, view2) => {
|
|
2865
|
+
if (debug) {
|
|
2866
|
+
logChanges(trs);
|
|
2867
|
+
}
|
|
2868
|
+
view2.update(trs);
|
|
2869
|
+
}
|
|
2509
2870
|
});
|
|
2871
|
+
view?.destroy();
|
|
2510
2872
|
setView(newView);
|
|
2511
2873
|
return () => {
|
|
2512
2874
|
newView?.destroy();
|
|
@@ -2522,8 +2884,14 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
|
|
|
2522
2884
|
const handleKeyUp = useCallback((event) => {
|
|
2523
2885
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
2524
2886
|
switch (key) {
|
|
2887
|
+
case "Enter": {
|
|
2888
|
+
view?.focus();
|
|
2889
|
+
break;
|
|
2890
|
+
}
|
|
2525
2891
|
case "Escape": {
|
|
2526
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)
|
|
2892
|
+
if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
2893
|
+
view?.focus();
|
|
2894
|
+
}
|
|
2527
2895
|
break;
|
|
2528
2896
|
}
|
|
2529
2897
|
}
|
|
@@ -2534,24 +2902,24 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
|
|
|
2534
2902
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2535
2903
|
key: model.id,
|
|
2536
2904
|
role: "none",
|
|
2537
|
-
ref: rootRef,
|
|
2538
2905
|
tabIndex: 0,
|
|
2539
|
-
onKeyUp: handleKeyUp,
|
|
2540
2906
|
...slots.root,
|
|
2541
|
-
...editorMode !==
|
|
2907
|
+
// {...(editorMode !== 'vim' && tabsterDOMAttribute)}
|
|
2908
|
+
onKeyUp: handleKeyUp,
|
|
2909
|
+
ref: rootRef
|
|
2542
2910
|
});
|
|
2543
2911
|
});
|
|
2544
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3,
|
|
2912
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
2545
2913
|
const { themeMode } = useThemeContext();
|
|
2546
2914
|
const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
|
|
2547
2915
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2548
2916
|
ref: forwardedRef,
|
|
2549
2917
|
readonly,
|
|
2550
2918
|
extensions: [
|
|
2551
|
-
|
|
2552
|
-
readonly,
|
|
2919
|
+
createBasicBundle({
|
|
2553
2920
|
themeMode,
|
|
2554
|
-
placeholder: placeholder3
|
|
2921
|
+
placeholder: placeholder3,
|
|
2922
|
+
lineWrapping
|
|
2555
2923
|
}),
|
|
2556
2924
|
...extensions
|
|
2557
2925
|
],
|
|
@@ -2560,15 +2928,14 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
|
|
|
2560
2928
|
...props
|
|
2561
2929
|
});
|
|
2562
2930
|
});
|
|
2563
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3,
|
|
2931
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
2564
2932
|
const { themeMode } = useThemeContext();
|
|
2565
2933
|
const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
|
|
2566
2934
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2567
2935
|
ref: forwardedRef,
|
|
2568
2936
|
readonly,
|
|
2569
2937
|
extensions: [
|
|
2570
|
-
|
|
2571
|
-
readonly,
|
|
2938
|
+
createMarkdownExtensions({
|
|
2572
2939
|
themeMode,
|
|
2573
2940
|
placeholder: placeholder3
|
|
2574
2941
|
}),
|
|
@@ -2581,7 +2948,11 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
|
|
|
2581
2948
|
});
|
|
2582
2949
|
var defaultSlots = {
|
|
2583
2950
|
root: {
|
|
2584
|
-
|
|
2951
|
+
// TODO(burdon): Add focusRing by default/as property?
|
|
2952
|
+
className: mx3("flex flex-col grow overflow-y-auto", attentionSurface)
|
|
2953
|
+
},
|
|
2954
|
+
editor: {
|
|
2955
|
+
className: "h-full p-2"
|
|
2585
2956
|
}
|
|
2586
2957
|
};
|
|
2587
2958
|
var defaultTextSlots = {
|
|
@@ -2591,22 +2962,178 @@ var defaultMarkdownSlots = {
|
|
|
2591
2962
|
...defaultSlots
|
|
2592
2963
|
};
|
|
2593
2964
|
|
|
2594
|
-
// packages/ui/react-ui-editor/src/
|
|
2595
|
-
import
|
|
2596
|
-
import {
|
|
2597
|
-
import
|
|
2965
|
+
// 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";
|
|
2967
|
+
import { createContext } from "@radix-ui/react-context";
|
|
2968
|
+
import React2 from "react";
|
|
2969
|
+
import { Button, DensityProvider, Select } from "@dxos/react-ui";
|
|
2970
|
+
import { getSize } from "@dxos/react-ui-theme";
|
|
2971
|
+
var [ToolbarContextProvider, useToolbarContext] = createContext("Toolbar");
|
|
2972
|
+
var ToolbarRoot = ({ children, onAction }) => {
|
|
2973
|
+
return /* @__PURE__ */ React2.createElement(ToolbarContextProvider, {
|
|
2974
|
+
onAction
|
|
2975
|
+
}, /* @__PURE__ */ React2.createElement(DensityProvider, {
|
|
2976
|
+
density: "fine"
|
|
2977
|
+
}, /* @__PURE__ */ React2.createElement("div", {
|
|
2978
|
+
role: "toolbar",
|
|
2979
|
+
className: "flex w-full shrink-0 p-2 gap-4 items-center whitespace-nowrap overflow-hidden"
|
|
2980
|
+
}, children)));
|
|
2981
|
+
};
|
|
2982
|
+
var ToolbarButton = ({ Icon, onClick, title }) => {
|
|
2983
|
+
const { onAction } = useToolbarContext("ToolbarButton");
|
|
2984
|
+
const handleClick = () => {
|
|
2985
|
+
const action = onClick();
|
|
2986
|
+
if (action) {
|
|
2987
|
+
onAction?.(action);
|
|
2988
|
+
}
|
|
2989
|
+
};
|
|
2990
|
+
return /* @__PURE__ */ React2.createElement(Button, {
|
|
2991
|
+
variant: "ghost",
|
|
2992
|
+
classNames: "p-2",
|
|
2993
|
+
onClick: handleClick,
|
|
2994
|
+
title
|
|
2995
|
+
}, /* @__PURE__ */ React2.createElement(Icon, {
|
|
2996
|
+
className: getSize(5)
|
|
2997
|
+
}));
|
|
2998
|
+
};
|
|
2999
|
+
var ToolbarSeparator = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3000
|
+
className: "grow"
|
|
3001
|
+
});
|
|
3002
|
+
var MarkdownHeading = () => {
|
|
3003
|
+
const { onAction } = useToolbarContext("MarkdownFormatting");
|
|
3004
|
+
return /* @__PURE__ */ React2.createElement(Select.Root, {
|
|
3005
|
+
defaultValue: "0",
|
|
3006
|
+
onValueChange: (value) => onAction?.({
|
|
3007
|
+
type: "heading",
|
|
3008
|
+
data: parseInt(value)
|
|
3009
|
+
})
|
|
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, {
|
|
3013
|
+
value: "0"
|
|
3014
|
+
}, "Paragraph"), [
|
|
3015
|
+
1,
|
|
3016
|
+
2,
|
|
3017
|
+
3,
|
|
3018
|
+
4,
|
|
3019
|
+
5,
|
|
3020
|
+
6
|
|
3021
|
+
].map((level) => /* @__PURE__ */ React2.createElement(Select.Option, {
|
|
3022
|
+
key: level,
|
|
3023
|
+
value: String(level)
|
|
3024
|
+
}, "Heading ", level))))));
|
|
3025
|
+
};
|
|
3026
|
+
var MarkdownStyles = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3027
|
+
role: "none"
|
|
3028
|
+
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3029
|
+
Icon: TextB,
|
|
3030
|
+
title: "Bold",
|
|
3031
|
+
onClick: () => ({
|
|
3032
|
+
type: "bold"
|
|
3033
|
+
})
|
|
3034
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3035
|
+
Icon: TextItalic,
|
|
3036
|
+
title: "Italic",
|
|
3037
|
+
onClick: () => ({
|
|
3038
|
+
type: "italic"
|
|
3039
|
+
})
|
|
3040
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3041
|
+
Icon: TextStrikethrough,
|
|
3042
|
+
title: "Strike-through",
|
|
3043
|
+
onClick: () => ({
|
|
3044
|
+
type: "strikethrough"
|
|
3045
|
+
})
|
|
3046
|
+
}));
|
|
3047
|
+
var MarkdownLists = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3048
|
+
role: "none"
|
|
3049
|
+
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3050
|
+
Icon: ListBullets,
|
|
3051
|
+
title: "Bullet list",
|
|
3052
|
+
onClick: () => ({
|
|
3053
|
+
type: "list"
|
|
3054
|
+
})
|
|
3055
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3056
|
+
Icon: ListNumbers,
|
|
3057
|
+
title: "Numbered list",
|
|
3058
|
+
onClick: () => ({
|
|
3059
|
+
type: "list-ordered"
|
|
3060
|
+
})
|
|
3061
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3062
|
+
Icon: ListChecks,
|
|
3063
|
+
title: "Task list",
|
|
3064
|
+
onClick: () => ({
|
|
3065
|
+
type: "list-tasks"
|
|
3066
|
+
})
|
|
3067
|
+
}));
|
|
3068
|
+
var MarkdownBlocks = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3069
|
+
role: "none"
|
|
3070
|
+
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3071
|
+
Icon: CaretRight,
|
|
3072
|
+
title: "Block quite",
|
|
3073
|
+
onClick: () => ({
|
|
3074
|
+
type: "blockquote"
|
|
3075
|
+
})
|
|
3076
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3077
|
+
Icon: Code,
|
|
3078
|
+
title: "Code block",
|
|
3079
|
+
onClick: () => ({
|
|
3080
|
+
type: "codeblock"
|
|
3081
|
+
})
|
|
3082
|
+
}), /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3083
|
+
Icon: Table2,
|
|
3084
|
+
title: "Table",
|
|
3085
|
+
onClick: () => ({
|
|
3086
|
+
type: "table"
|
|
3087
|
+
})
|
|
3088
|
+
}));
|
|
3089
|
+
var MarkdownLinks = () => /* @__PURE__ */ React2.createElement("div", {
|
|
3090
|
+
role: "none"
|
|
3091
|
+
}, /* @__PURE__ */ React2.createElement(ToolbarButton, {
|
|
3092
|
+
Icon: Link,
|
|
3093
|
+
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"
|
|
3108
|
+
})
|
|
3109
|
+
}));
|
|
3110
|
+
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));
|
|
3111
|
+
var MarkdownExtended = () => /* @__PURE__ */ React2.createElement(Toolbar.Button, {
|
|
3112
|
+
Icon: ChatText,
|
|
3113
|
+
title: "Create comment",
|
|
3114
|
+
onClick: () => ({
|
|
3115
|
+
type: "comment"
|
|
3116
|
+
})
|
|
3117
|
+
});
|
|
3118
|
+
var Toolbar = {
|
|
3119
|
+
Root: ToolbarRoot,
|
|
3120
|
+
Button: ToolbarButton,
|
|
3121
|
+
Separator: ToolbarSeparator,
|
|
3122
|
+
Markdown: MarkdownStandard,
|
|
3123
|
+
Extended: MarkdownExtended
|
|
3124
|
+
};
|
|
2598
3125
|
|
|
2599
3126
|
// packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
|
|
2600
3127
|
import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
|
|
2601
3128
|
import { Context as Context2 } from "@dxos/context";
|
|
2602
|
-
import { invariant as
|
|
3129
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
2603
3130
|
import { log as log3 } from "@dxos/log";
|
|
2604
3131
|
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
|
|
2605
3132
|
var DEBOUNCE_INTERVAL = 100;
|
|
2606
3133
|
var SpaceAwarenessProvider = class {
|
|
2607
3134
|
constructor(params) {
|
|
2608
|
-
this.remoteStateChange = new Event2();
|
|
2609
3135
|
this._remoteStates = /* @__PURE__ */ new Map();
|
|
3136
|
+
this.remoteStateChange = new Event2();
|
|
2610
3137
|
this._space = params.space;
|
|
2611
3138
|
this._channel = params.channel;
|
|
2612
3139
|
this._peerId = params.peerId;
|
|
@@ -2623,7 +3150,7 @@ var SpaceAwarenessProvider = class {
|
|
|
2623
3150
|
await sleep(DEBOUNCE_INTERVAL);
|
|
2624
3151
|
}
|
|
2625
3152
|
});
|
|
2626
|
-
|
|
3153
|
+
this._ctx.onDispose(this._space.listen(this._channel, (message) => {
|
|
2627
3154
|
switch (message.payload.kind) {
|
|
2628
3155
|
case "query": {
|
|
2629
3156
|
this._handleQueryMessage();
|
|
@@ -2634,8 +3161,7 @@ var SpaceAwarenessProvider = class {
|
|
|
2634
3161
|
break;
|
|
2635
3162
|
}
|
|
2636
3163
|
}
|
|
2637
|
-
});
|
|
2638
|
-
this._ctx.onDispose(unsubscribe);
|
|
3164
|
+
}));
|
|
2639
3165
|
void this._space.postMessage(this._channel, {
|
|
2640
3166
|
kind: "query"
|
|
2641
3167
|
}).catch((err) => {
|
|
@@ -2643,7 +3169,7 @@ var SpaceAwarenessProvider = class {
|
|
|
2643
3169
|
err
|
|
2644
3170
|
}, {
|
|
2645
3171
|
F: __dxlog_file4,
|
|
2646
|
-
L:
|
|
3172
|
+
L: 89,
|
|
2647
3173
|
S: this,
|
|
2648
3174
|
C: (f, a) => f(...a)
|
|
2649
3175
|
});
|
|
@@ -2654,10 +3180,13 @@ var SpaceAwarenessProvider = class {
|
|
|
2654
3180
|
this._ctx = void 0;
|
|
2655
3181
|
this._postTask = void 0;
|
|
2656
3182
|
}
|
|
3183
|
+
getRemoteStates() {
|
|
3184
|
+
return Array.from(this._remoteStates.values());
|
|
3185
|
+
}
|
|
2657
3186
|
update(position) {
|
|
2658
|
-
|
|
3187
|
+
invariant2(this._postTask, void 0, {
|
|
2659
3188
|
F: __dxlog_file4,
|
|
2660
|
-
L:
|
|
3189
|
+
L: 104,
|
|
2661
3190
|
S: this,
|
|
2662
3191
|
A: [
|
|
2663
3192
|
"this._postTask",
|
|
@@ -2671,13 +3200,10 @@ var SpaceAwarenessProvider = class {
|
|
|
2671
3200
|
};
|
|
2672
3201
|
this._postTask.schedule();
|
|
2673
3202
|
}
|
|
2674
|
-
getRemoteStates() {
|
|
2675
|
-
return Array.from(this._remoteStates.values());
|
|
2676
|
-
}
|
|
2677
3203
|
_handleQueryMessage() {
|
|
2678
|
-
|
|
3204
|
+
invariant2(this._postTask, void 0, {
|
|
2679
3205
|
F: __dxlog_file4,
|
|
2680
|
-
L:
|
|
3206
|
+
L: 115,
|
|
2681
3207
|
S: this,
|
|
2682
3208
|
A: [
|
|
2683
3209
|
"this._postTask",
|
|
@@ -2687,9 +3213,9 @@ var SpaceAwarenessProvider = class {
|
|
|
2687
3213
|
this._postTask.schedule();
|
|
2688
3214
|
}
|
|
2689
3215
|
_handlePostMessage(message) {
|
|
2690
|
-
|
|
3216
|
+
invariant2(message.kind === "post", void 0, {
|
|
2691
3217
|
F: __dxlog_file4,
|
|
2692
|
-
L:
|
|
3218
|
+
L: 120,
|
|
2693
3219
|
S: this,
|
|
2694
3220
|
A: [
|
|
2695
3221
|
"message.kind === 'post'",
|
|
@@ -2701,199 +3227,231 @@ var SpaceAwarenessProvider = class {
|
|
|
2701
3227
|
}
|
|
2702
3228
|
};
|
|
2703
3229
|
|
|
2704
|
-
// packages/ui/react-ui-editor/src/hooks/
|
|
3230
|
+
// packages/ui/react-ui-editor/src/hooks/defs.ts
|
|
2705
3231
|
import { StateField as StateField5 } from "@codemirror/state";
|
|
2706
|
-
|
|
2707
|
-
|
|
3232
|
+
var modelState = StateField5.define({
|
|
3233
|
+
create: () => void 0,
|
|
3234
|
+
update: (model) => model
|
|
3235
|
+
});
|
|
2708
3236
|
|
|
2709
|
-
// packages/ui/react-ui-editor/src/hooks/
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
import { invariant as invariant4 } from "@dxos/invariant";
|
|
2715
|
-
import { log as log4 } from "@dxos/log";
|
|
2716
|
-
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
|
|
2717
|
-
var createYjsModel = ({ identity, space, text }) => {
|
|
2718
|
-
invariant4(text?.doc && text?.content, void 0, {
|
|
2719
|
-
F: __dxlog_file5,
|
|
2720
|
-
L: 21,
|
|
2721
|
-
S: void 0,
|
|
2722
|
-
A: [
|
|
2723
|
-
"text?.doc && text?.content",
|
|
2724
|
-
""
|
|
2725
|
-
]
|
|
2726
|
-
});
|
|
2727
|
-
const provider = space ? new YAwarenessProvider({
|
|
2728
|
-
space,
|
|
2729
|
-
doc: text.doc,
|
|
2730
|
-
channel: `yjs.awareness.${text.id}`
|
|
2731
|
-
}) : void 0;
|
|
2732
|
-
const model = {
|
|
2733
|
-
id: text.doc.guid,
|
|
2734
|
-
content: text.content,
|
|
2735
|
-
text: () => text.content.toString(),
|
|
2736
|
-
extension: [
|
|
2737
|
-
modelState.init(() => model),
|
|
2738
|
-
yjs(text.content, provider?.awareness)
|
|
2739
|
-
],
|
|
2740
|
-
awareness: provider?.awareness,
|
|
2741
|
-
peer: identity ? {
|
|
2742
|
-
id: identity.identityKey.toHex(),
|
|
2743
|
-
name: identity.profile?.displayName
|
|
2744
|
-
} : void 0
|
|
2745
|
-
};
|
|
2746
|
-
return model;
|
|
2747
|
-
};
|
|
2748
|
-
var messageAwareness = 1;
|
|
2749
|
-
var messageQueryAwareness = 3;
|
|
2750
|
-
var YAwarenessProvider = class extends Observable {
|
|
2751
|
-
constructor({ space, doc, channel, awareness: awareness2 }) {
|
|
2752
|
-
super();
|
|
2753
|
-
this._space = space;
|
|
2754
|
-
this._awareness = awareness2 ?? new YP.Awareness(doc);
|
|
2755
|
-
this._channel = channel;
|
|
2756
|
-
this._clientId = doc.clientID;
|
|
2757
|
-
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
2758
|
-
this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
|
|
2759
|
-
if (typeof window !== "undefined") {
|
|
2760
|
-
window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
|
|
2761
|
-
} else if (typeof process !== "undefined") {
|
|
2762
|
-
process.on("exit", this._handleBeforeUnload.bind(this));
|
|
3237
|
+
// packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
|
|
3238
|
+
var useActionHandler = (view) => {
|
|
3239
|
+
return (action) => {
|
|
3240
|
+
if (!view) {
|
|
3241
|
+
return;
|
|
2763
3242
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
_handleSpaceMessage({ payload }) {
|
|
2796
|
-
log4("space message", payload, {
|
|
2797
|
-
F: __dxlog_file5,
|
|
2798
|
-
L: 109,
|
|
2799
|
-
S: this,
|
|
2800
|
-
C: (f, a) => f(...a)
|
|
3243
|
+
switch (action.type) {
|
|
3244
|
+
case "heading":
|
|
3245
|
+
setHeading(parseInt(action.data))(view);
|
|
3246
|
+
break;
|
|
3247
|
+
case "bold":
|
|
3248
|
+
toggleBold(view);
|
|
3249
|
+
break;
|
|
3250
|
+
case "italic":
|
|
3251
|
+
toggleItalic(view);
|
|
3252
|
+
break;
|
|
3253
|
+
case "strikethrough":
|
|
3254
|
+
toggleStrikethrough(view);
|
|
3255
|
+
break;
|
|
3256
|
+
case "list":
|
|
3257
|
+
toggleList(view);
|
|
3258
|
+
break;
|
|
3259
|
+
case "codeblock":
|
|
3260
|
+
insertCodeblock(view);
|
|
3261
|
+
break;
|
|
3262
|
+
case "table":
|
|
3263
|
+
insertTable(view);
|
|
3264
|
+
break;
|
|
3265
|
+
case "comment":
|
|
3266
|
+
createComment(view);
|
|
3267
|
+
break;
|
|
3268
|
+
}
|
|
3269
|
+
setTimeout(() => {
|
|
3270
|
+
view.focus();
|
|
3271
|
+
view.dispatch({
|
|
3272
|
+
selection: view.state.selection
|
|
3273
|
+
});
|
|
2801
3274
|
});
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
3275
|
+
};
|
|
3276
|
+
};
|
|
3277
|
+
|
|
3278
|
+
// packages/ui/react-ui-editor/src/hooks/useEditorView.ts
|
|
3279
|
+
import { useState as useState2 } from "react";
|
|
3280
|
+
var useEditorView = () => {
|
|
3281
|
+
const [view, setView] = useState2(null);
|
|
3282
|
+
return [
|
|
3283
|
+
(ref) => {
|
|
3284
|
+
setView(ref);
|
|
3285
|
+
},
|
|
3286
|
+
view
|
|
3287
|
+
];
|
|
3288
|
+
};
|
|
3289
|
+
|
|
3290
|
+
// 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";
|
|
3294
|
+
import { log as log4 } from "@dxos/log";
|
|
3295
|
+
import { isNotFalsy as isNotFalsy4 } from "@dxos/util";
|
|
3296
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
|
|
3297
|
+
var useTextEditor = ({ autoFocus, scrollTo, debug, doc, selection, extensions } = {}) => {
|
|
3298
|
+
const onUpdate = useRef2();
|
|
3299
|
+
const [view, setView] = useState3();
|
|
3300
|
+
const parentRef = useRef2(null);
|
|
3301
|
+
useEffect3(() => {
|
|
3302
|
+
if (parentRef.current) {
|
|
3303
|
+
const state = EditorState3.create({
|
|
3304
|
+
doc,
|
|
3305
|
+
selection,
|
|
3306
|
+
extensions: [
|
|
3307
|
+
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
3308
|
+
EditorView14.exceptionSink.of((err) => {
|
|
3309
|
+
log4.catch(err, void 0, {
|
|
3310
|
+
F: __dxlog_file5,
|
|
3311
|
+
L: 57,
|
|
3312
|
+
S: void 0,
|
|
3313
|
+
C: (f, a) => f(...a)
|
|
3314
|
+
});
|
|
3315
|
+
}),
|
|
3316
|
+
extensions,
|
|
3317
|
+
EditorView14.updateListener.of(() => {
|
|
3318
|
+
onUpdate.current?.();
|
|
3319
|
+
})
|
|
3320
|
+
].filter(isNotFalsy4)
|
|
3321
|
+
});
|
|
3322
|
+
const view2 = new EditorView14({
|
|
3323
|
+
parent: parentRef.current,
|
|
3324
|
+
scrollTo,
|
|
3325
|
+
state,
|
|
3326
|
+
// NOTE: Uncomment to debug/monitor all transactions.
|
|
3327
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
3328
|
+
dispatchTransactions: (trs, view3) => {
|
|
3329
|
+
if (debug) {
|
|
3330
|
+
logChanges(trs);
|
|
3331
|
+
}
|
|
3332
|
+
view3.update(trs);
|
|
3333
|
+
}
|
|
3334
|
+
});
|
|
3335
|
+
setView(view2);
|
|
2806
3336
|
}
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
3337
|
+
return () => {
|
|
3338
|
+
view?.destroy();
|
|
3339
|
+
};
|
|
3340
|
+
}, [
|
|
3341
|
+
parentRef
|
|
3342
|
+
]);
|
|
3343
|
+
useEffect3(() => {
|
|
3344
|
+
if (view) {
|
|
3345
|
+
if (!selection && !view.state.selection.main.anchor) {
|
|
3346
|
+
selection = EditorSelection.single(view.state.doc.line(1).to);
|
|
2817
3347
|
}
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
3348
|
+
if (selection || scrollTo) {
|
|
3349
|
+
onUpdate.current = () => {
|
|
3350
|
+
onUpdate.current = void 0;
|
|
3351
|
+
view.dispatch({
|
|
3352
|
+
selection,
|
|
3353
|
+
effects: scrollTo && [
|
|
3354
|
+
scrollTo
|
|
3355
|
+
],
|
|
3356
|
+
scrollIntoView: !scrollTo
|
|
3357
|
+
});
|
|
3358
|
+
};
|
|
2823
3359
|
}
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
return encoder;
|
|
3360
|
+
if (autoFocus) {
|
|
3361
|
+
view.focus();
|
|
2827
3362
|
}
|
|
2828
3363
|
}
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
}
|
|
3364
|
+
}, [
|
|
3365
|
+
view,
|
|
3366
|
+
autoFocus,
|
|
3367
|
+
selection,
|
|
3368
|
+
scrollTo
|
|
3369
|
+
]);
|
|
3370
|
+
return {
|
|
3371
|
+
parentRef,
|
|
3372
|
+
view
|
|
3373
|
+
};
|
|
3374
|
+
};
|
|
3375
|
+
var createDataExtensions = ({ readonly = false } = {}) => {
|
|
3376
|
+
return [
|
|
3377
|
+
//
|
|
3378
|
+
EditorState3.readOnly.of(readonly),
|
|
3379
|
+
EditorView14.editable.of(!readonly)
|
|
3380
|
+
];
|
|
3381
|
+
};
|
|
3382
|
+
var createThemeExtensions = ({ theme, themeMode, slots } = {}) => {
|
|
3383
|
+
return [
|
|
3384
|
+
//
|
|
3385
|
+
EditorView14.baseTheme(defaultTheme),
|
|
3386
|
+
theme && EditorView14.theme(theme),
|
|
3387
|
+
EditorView14.darkTheme.of(themeMode === "dark"),
|
|
3388
|
+
EditorView14.editorAttributes.of({
|
|
3389
|
+
class: slots?.editor?.className ?? ""
|
|
3390
|
+
}),
|
|
3391
|
+
EditorView14.contentAttributes.of({
|
|
3392
|
+
class: slots?.content?.className ?? ""
|
|
3393
|
+
})
|
|
3394
|
+
].filter(isNotFalsy4);
|
|
2839
3395
|
};
|
|
2840
3396
|
|
|
2841
3397
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
}
|
|
3398
|
+
import get4 from "lodash.get";
|
|
3399
|
+
import { useEffect as useEffect4, useState as useState4 } from "react";
|
|
3400
|
+
import { generateName } from "@dxos/display-name";
|
|
3401
|
+
import { getRawDoc } from "@dxos/echo-schema";
|
|
3402
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
3403
|
+
import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
3404
|
+
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
2846
3405
|
var useTextModel = (props) => {
|
|
2847
3406
|
const { identity, space, text } = props;
|
|
2848
|
-
const [model, setModel] =
|
|
2849
|
-
|
|
3407
|
+
const [model, setModel] = useState4();
|
|
3408
|
+
useEffect4(() => setModel(createModel(props)), [
|
|
2850
3409
|
identity,
|
|
2851
3410
|
space,
|
|
2852
3411
|
text
|
|
2853
3412
|
]);
|
|
2854
3413
|
return model;
|
|
2855
3414
|
};
|
|
2856
|
-
var createModel = (
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
};
|
|
2866
|
-
|
|
2867
|
-
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2868
|
-
var createAutomergeModel = ({ space, identity, text }) => {
|
|
3415
|
+
var createModel = ({ space, identity, text }) => {
|
|
3416
|
+
invariant3(isAutomergeObject(text), void 0, {
|
|
3417
|
+
F: __dxlog_file6,
|
|
3418
|
+
L: 34,
|
|
3419
|
+
S: void 0,
|
|
3420
|
+
A: [
|
|
3421
|
+
"isAutomergeObject(text)",
|
|
3422
|
+
""
|
|
3423
|
+
]
|
|
3424
|
+
});
|
|
2869
3425
|
const obj = text;
|
|
2870
3426
|
const doc = getRawDoc(obj, [
|
|
2871
3427
|
obj.field
|
|
2872
3428
|
]);
|
|
2873
|
-
const
|
|
3429
|
+
const awarenessProvider2 = space && new SpaceAwarenessProvider({
|
|
2874
3430
|
space,
|
|
2875
3431
|
channel: `automerge.awareness.${obj.id}`,
|
|
2876
3432
|
info: {
|
|
2877
|
-
displayName: identity
|
|
3433
|
+
displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
|
|
2878
3434
|
color: cursorColor.color,
|
|
2879
3435
|
lightColor: cursorColor.light
|
|
2880
3436
|
},
|
|
2881
3437
|
peerId: identity?.identityKey.toHex() ?? "Anonymous"
|
|
2882
3438
|
});
|
|
3439
|
+
const extensions = [
|
|
3440
|
+
//
|
|
3441
|
+
modelState.init(() => model),
|
|
3442
|
+
automerge({
|
|
3443
|
+
handle: doc.handle,
|
|
3444
|
+
path: doc.path
|
|
3445
|
+
})
|
|
3446
|
+
];
|
|
3447
|
+
if (awarenessProvider2) {
|
|
3448
|
+
extensions.push(awareness(awarenessProvider2));
|
|
3449
|
+
}
|
|
2883
3450
|
const model = {
|
|
2884
3451
|
id: obj.id,
|
|
2885
3452
|
content: doc,
|
|
2886
3453
|
text: () => get4(doc.handle.docSync(), doc.path),
|
|
2887
|
-
|
|
2888
|
-
extension: [
|
|
2889
|
-
modelState.init(() => model),
|
|
2890
|
-
automerge3({
|
|
2891
|
-
handle: doc.handle,
|
|
2892
|
-
path: doc.path
|
|
2893
|
-
}),
|
|
2894
|
-
awarenessProvider && AwarenessProvider.of(awarenessProvider),
|
|
2895
|
-
awareness()
|
|
2896
|
-
].filter(isNotNullOrUndefined),
|
|
3454
|
+
extension: extensions,
|
|
2897
3455
|
peer: identity ? {
|
|
2898
3456
|
id: identity.identityKey.toHex(),
|
|
2899
3457
|
name: identity.profile?.displayName
|
|
@@ -2902,7 +3460,6 @@ var createAutomergeModel = ({ space, identity, text }) => {
|
|
|
2902
3460
|
return model;
|
|
2903
3461
|
};
|
|
2904
3462
|
export {
|
|
2905
|
-
AwarenessProvider,
|
|
2906
3463
|
BaseTextEditor,
|
|
2907
3464
|
Cursor,
|
|
2908
3465
|
Doc,
|
|
@@ -2912,47 +3469,64 @@ export {
|
|
|
2912
3469
|
SpaceAwarenessProvider,
|
|
2913
3470
|
TextEditor,
|
|
2914
3471
|
TextKind,
|
|
2915
|
-
|
|
3472
|
+
Toolbar,
|
|
2916
3473
|
YText,
|
|
2917
3474
|
YXmlFragment,
|
|
2918
3475
|
autocomplete,
|
|
2919
|
-
|
|
3476
|
+
automerge,
|
|
2920
3477
|
awareness,
|
|
2921
|
-
|
|
3478
|
+
awarenessProvider,
|
|
2922
3479
|
blast,
|
|
2923
3480
|
callbackWrapper,
|
|
2924
3481
|
code2 as code,
|
|
2925
3482
|
codeTheme,
|
|
2926
3483
|
comments,
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
3484
|
+
createBasicBundle,
|
|
3485
|
+
createComment,
|
|
3486
|
+
createDataExtensions,
|
|
3487
|
+
createMarkdownExtensions,
|
|
3488
|
+
createThemeExtensions,
|
|
2930
3489
|
defaultMarkdownSlots,
|
|
2931
3490
|
defaultOptions,
|
|
2932
3491
|
defaultSlots,
|
|
2933
3492
|
defaultTextSlots,
|
|
2934
3493
|
defaultTheme,
|
|
3494
|
+
focusComment,
|
|
3495
|
+
formatting,
|
|
2935
3496
|
getToken,
|
|
3497
|
+
heading2 as heading,
|
|
2936
3498
|
hr,
|
|
2937
3499
|
image,
|
|
3500
|
+
insertCodeblock,
|
|
3501
|
+
insertTable,
|
|
2938
3502
|
link,
|
|
2939
3503
|
listener,
|
|
2940
|
-
|
|
3504
|
+
logChanges,
|
|
2941
3505
|
markdownHighlightStyle,
|
|
2942
3506
|
markdownTags,
|
|
2943
3507
|
markdownTagsExtensions,
|
|
2944
3508
|
markdownTheme,
|
|
2945
3509
|
mention,
|
|
2946
3510
|
modelState,
|
|
2947
|
-
|
|
2948
|
-
|
|
3511
|
+
outliner,
|
|
3512
|
+
setComments,
|
|
3513
|
+
setHeading,
|
|
2949
3514
|
setSelection,
|
|
2950
3515
|
table,
|
|
2951
3516
|
tags2 as tags,
|
|
2952
3517
|
tasklist,
|
|
2953
3518
|
textTheme,
|
|
3519
|
+
toggleBold,
|
|
3520
|
+
toggleItalic,
|
|
3521
|
+
toggleList,
|
|
3522
|
+
toggleStrikethrough,
|
|
3523
|
+
toggleStyle,
|
|
2954
3524
|
typewriter,
|
|
3525
|
+
useActionHandler,
|
|
3526
|
+
useComments,
|
|
3527
|
+
useEditorView,
|
|
3528
|
+
useTextEditor,
|
|
2955
3529
|
useTextModel,
|
|
2956
|
-
|
|
3530
|
+
useToolbarContext
|
|
2957
3531
|
};
|
|
2958
3532
|
//# sourceMappingURL=index.mjs.map
|