@dxos/react-ui-editor 0.3.11-main.008b7c6 → 0.3.11-main.08776d7
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 +745 -810
- 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 +12 -5
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +16 -19
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -6
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts +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/cursor.d.ts +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/defs.d.ts +6 -8
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/semaphore.d.ts +7 -11
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
- package/dist/types/src/extensions/code.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +26 -17
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/{util → extensions}/cursor.d.ts +10 -2
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/hr.d.ts.map +1 -1
- package/dist/types/src/extensions/image.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +3 -1
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/link.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.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/extensions/sync.d.ts +7 -0
- package/dist/types/src/extensions/sync.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts +5 -1
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -1
- package/dist/types/src/extensions/yjs/cursor.d.ts +1 -1
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +1 -1
- package/dist/types/src/hooks/automerge.d.ts +2 -1
- package/dist/types/src/hooks/automerge.d.ts.map +1 -1
- package/dist/types/src/hooks/defs.d.ts +28 -0
- package/dist/types/src/hooks/defs.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +2 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextModel.d.ts +2 -32
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/hooks/yjs.d.ts +2 -1
- package/dist/types/src/hooks/yjs.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +1 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/themes/default.d.ts.map +1 -1
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +25 -24
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +45 -41
- package/src/components/TextEditor/TextEditor.stories.tsx +12 -46
- package/src/components/TextEditor/TextEditor.tsx +101 -89
- package/src/components/TextEditor/comments.stories.tsx +362 -0
- package/src/extensions/autocomplete.ts +18 -17
- package/src/extensions/automerge/automerge.ts +15 -46
- package/src/extensions/automerge/cursor.ts +2 -1
- package/src/extensions/automerge/defs.ts +13 -14
- package/src/extensions/automerge/semaphore.ts +21 -16
- package/src/extensions/awareness.ts +27 -33
- package/src/extensions/code.ts +28 -23
- package/src/extensions/comments.ts +234 -236
- package/src/extensions/cursor.ts +50 -0
- package/src/extensions/hr.ts +10 -12
- package/src/extensions/image.ts +40 -22
- package/src/extensions/index.ts +3 -1
- package/src/extensions/link.ts +86 -88
- package/src/extensions/markdown/highlight.ts +3 -12
- package/src/extensions/outliner.ts +12 -0
- package/src/extensions/sync.ts +15 -0
- package/src/extensions/table.ts +10 -4
- package/src/extensions/tasklist.ts +54 -79
- package/src/extensions/yjs/cursor.ts +2 -1
- package/src/extensions/yjs/yjs.test.ts +1 -1
- package/src/extensions/yjs/yjs.ts +2 -2
- package/src/hooks/automerge.ts +2 -1
- package/src/hooks/defs.ts +47 -0
- package/src/hooks/index.ts +3 -1
- package/src/hooks/useTextModel.ts +3 -49
- package/src/hooks/yjs.ts +2 -23
- package/src/index.ts +3 -0
- package/src/styles/markdown.ts +0 -2
- package/src/styles/tokens.ts +3 -0
- package/src/themes/default.ts +51 -78
- package/dist/types/src/extensions/mermaid.d.ts +0 -3
- package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
- package/dist/types/src/util/cursor.d.ts.map +0 -1
- package/dist/types/src/util/index.d.ts +0 -3
- package/dist/types/src/util/index.d.ts.map +0 -1
- package/dist/types/src/util/util.d.ts.map +0 -1
- package/src/extensions/mermaid.ts +0 -11
- package/src/util/cursor.ts +0 -29
- package/src/util/index.ts +0 -6
- /package/dist/types/src/{util/util.d.ts → util.d.ts} +0 -0
- /package/src/{util/util.ts → util.ts} +0 -0
|
@@ -7,51 +7,51 @@ 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
|
|
10
|
+
import { EditorView as EditorView12 } from "@codemirror/view";
|
|
11
11
|
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
12
12
|
import { vim } from "@replit/codemirror-vim";
|
|
13
13
|
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
14
|
-
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState } from "react";
|
|
14
|
+
import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useState, useRef } from "react";
|
|
15
15
|
import { generateName } from "@dxos/display-name";
|
|
16
16
|
import { useThemeContext } from "@dxos/react-ui";
|
|
17
17
|
import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
|
|
18
18
|
|
|
19
19
|
// packages/ui/react-ui-editor/src/extensions/autocomplete.ts
|
|
20
20
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
21
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
21
22
|
import { keymap } from "@codemirror/view";
|
|
22
23
|
var autocomplete = ({ onSearch }) => {
|
|
23
24
|
return [
|
|
24
25
|
// https://codemirror.net/docs/ref/#view.keymap
|
|
25
26
|
// https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
|
|
27
|
+
// TODO(burdon): Set custom keymap.
|
|
26
28
|
keymap.of(completionKeymap),
|
|
27
29
|
// https://codemirror.net/examples/autocompletion
|
|
28
30
|
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
29
31
|
autocompletion({
|
|
30
|
-
|
|
31
|
-
// defaultKeymap: false,
|
|
32
|
+
defaultKeymap: false,
|
|
32
33
|
// Don't start unless key press.
|
|
33
|
-
activateOnTyping: false
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return {
|
|
43
|
-
from: match.from,
|
|
44
|
-
options: onSearch(match.text.toLowerCase())
|
|
45
|
-
};
|
|
34
|
+
activateOnTyping: false
|
|
35
|
+
}),
|
|
36
|
+
// TODO(burdon): Option to create new page?
|
|
37
|
+
// TODO(burdon): Optional decoration via addToOptions
|
|
38
|
+
markdownLanguage.data.of({
|
|
39
|
+
autocomplete: (context) => {
|
|
40
|
+
const match = context.matchBefore(/\w*/);
|
|
41
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
42
|
+
return null;
|
|
46
43
|
}
|
|
47
|
-
|
|
44
|
+
return {
|
|
45
|
+
from: match.from,
|
|
46
|
+
options: onSearch(match.text.toLowerCase())
|
|
47
|
+
};
|
|
48
|
+
}
|
|
48
49
|
})
|
|
49
50
|
];
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
53
|
-
import {
|
|
54
|
-
import { ViewPlugin } from "@codemirror/view";
|
|
54
|
+
import { StateField } from "@codemirror/state";
|
|
55
55
|
import { next as A } from "@dxos/automerge/automerge";
|
|
56
56
|
|
|
57
57
|
// packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
|
|
@@ -93,12 +93,12 @@ var cursorConverter = (handle, path) => ({
|
|
|
93
93
|
// packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
|
|
94
94
|
import { Annotation, StateEffect } from "@codemirror/state";
|
|
95
95
|
var effectType = StateEffect.define({});
|
|
96
|
+
var reconcileAnnotationType = Annotation.define();
|
|
97
|
+
var getPath = (state, field) => state.field(field).path;
|
|
98
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
96
99
|
var updateHeads = (newHeads) => effectType.of({
|
|
97
100
|
newHeads
|
|
98
101
|
});
|
|
99
|
-
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
100
|
-
var getPath = (state, field) => state.field(field).path;
|
|
101
|
-
var reconcileAnnotationType = Annotation.define();
|
|
102
102
|
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
103
103
|
|
|
104
104
|
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
@@ -236,85 +236,101 @@ var charPath = (textPath, candidatePath) => {
|
|
|
236
236
|
|
|
237
237
|
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
238
238
|
var PatchSemaphore = class {
|
|
239
|
-
|
|
239
|
+
// prettier-ignore
|
|
240
|
+
constructor(_handle, _field) {
|
|
241
|
+
this._handle = _handle;
|
|
240
242
|
this._field = _field;
|
|
241
243
|
this._inReconcile = false;
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
changes: inverted,
|
|
258
|
-
annotations: reconcileAnnotationType.of(true)
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
|
|
262
|
-
if (newHeads === null || newHeads === void 0) {
|
|
263
|
-
newHeads = automerge2.getHeads(handle.docSync());
|
|
264
|
-
}
|
|
265
|
-
const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(handle.docSync(), oldHeads, newHeads);
|
|
266
|
-
updateCodeMirror(view, selection, path, diff);
|
|
244
|
+
}
|
|
245
|
+
// NOTE: Cannot destruct view.state.
|
|
246
|
+
reconcile(view) {
|
|
247
|
+
if (this._inReconcile) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this._inReconcile = true;
|
|
251
|
+
const path = getPath(view.state, this._field);
|
|
252
|
+
const oldHeads = getLastHeads(view.state, this._field);
|
|
253
|
+
let selection = view.state.selection;
|
|
254
|
+
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
255
|
+
const toInvert = transactions.slice().reverse();
|
|
256
|
+
for (const tx of toInvert) {
|
|
257
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
258
|
+
selection = selection.map(inverted);
|
|
267
259
|
view.dispatch({
|
|
268
|
-
|
|
269
|
-
annotations: reconcileAnnotationType.of(
|
|
260
|
+
changes: inverted,
|
|
261
|
+
annotations: reconcileAnnotationType.of(true)
|
|
270
262
|
});
|
|
271
|
-
|
|
272
|
-
|
|
263
|
+
}
|
|
264
|
+
let newHeads = updateAutomerge(this._field, this._handle, transactions, view.state);
|
|
265
|
+
if (newHeads === null || newHeads === void 0) {
|
|
266
|
+
newHeads = automerge2.getHeads(this._handle.docSync());
|
|
267
|
+
}
|
|
268
|
+
const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(this._handle.docSync(), oldHeads, newHeads);
|
|
269
|
+
updateCodeMirror(view, selection, path, diff);
|
|
270
|
+
view.dispatch({
|
|
271
|
+
effects: updateHeads(newHeads),
|
|
272
|
+
annotations: reconcileAnnotationType.of({})
|
|
273
|
+
});
|
|
274
|
+
this._inReconcile = false;
|
|
273
275
|
}
|
|
274
276
|
};
|
|
275
277
|
|
|
276
|
-
// packages/ui/react-ui-editor/src/
|
|
278
|
+
// packages/ui/react-ui-editor/src/extensions/cursor.ts
|
|
277
279
|
import { Facet } from "@codemirror/state";
|
|
278
|
-
var
|
|
280
|
+
var defaultCursorConverter = {
|
|
279
281
|
toCursor: (position) => position.toString(),
|
|
280
282
|
fromCursor: (cursor) => parseInt(cursor)
|
|
281
283
|
};
|
|
282
|
-
var
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
// packages/ui/react-ui-editor/src/util/util.ts
|
|
287
|
-
import { log } from "@dxos/log";
|
|
288
|
-
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/util.ts";
|
|
289
|
-
var callbackWrapper = (fn) => (...args) => {
|
|
290
|
-
try {
|
|
291
|
-
return fn(...args);
|
|
292
|
-
} catch (error) {
|
|
293
|
-
log.catch(error, void 0, {
|
|
294
|
-
F: __dxlog_file,
|
|
295
|
-
L: 16,
|
|
296
|
-
S: void 0,
|
|
297
|
-
C: (f, a) => f(...a)
|
|
284
|
+
var Cursor = class _Cursor {
|
|
285
|
+
static {
|
|
286
|
+
this.converter = Facet.define({
|
|
287
|
+
combine: (providers) => providers[0] ?? defaultCursorConverter
|
|
298
288
|
});
|
|
299
289
|
}
|
|
290
|
+
static {
|
|
291
|
+
this.getCursorFromRange = (state, range) => {
|
|
292
|
+
const cursorConverter3 = state.facet(_Cursor.converter);
|
|
293
|
+
const from = cursorConverter3.toCursor(range.from);
|
|
294
|
+
const to = cursorConverter3.toCursor(range.to, -1);
|
|
295
|
+
return [
|
|
296
|
+
from,
|
|
297
|
+
to
|
|
298
|
+
].join(":");
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
static {
|
|
302
|
+
this.getRangeFromCursor = (state, cursor) => {
|
|
303
|
+
const cursorConverter3 = state.facet(_Cursor.converter);
|
|
304
|
+
const parts = cursor.split(":");
|
|
305
|
+
const from = cursorConverter3.fromCursor(parts[0]);
|
|
306
|
+
const to = cursorConverter3.fromCursor(parts[1]);
|
|
307
|
+
return from !== void 0 && to !== void 0 ? {
|
|
308
|
+
from,
|
|
309
|
+
to
|
|
310
|
+
} : void 0;
|
|
311
|
+
};
|
|
312
|
+
}
|
|
300
313
|
};
|
|
301
314
|
|
|
302
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
303
|
-
|
|
315
|
+
// packages/ui/react-ui-editor/src/extensions/sync.ts
|
|
316
|
+
import { Facet as Facet2 } from "@codemirror/state";
|
|
317
|
+
var syncFacet = Facet2.define({
|
|
304
318
|
combine: (values) => values.at(-1)
|
|
305
319
|
});
|
|
320
|
+
|
|
321
|
+
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
306
322
|
var automerge3 = ({ handle, path }) => {
|
|
307
323
|
const stateField = StateField.define({
|
|
308
324
|
create: () => ({
|
|
325
|
+
path: path.slice(),
|
|
309
326
|
lastHeads: A.getHeads(handle.docSync()),
|
|
310
|
-
unreconciledTransactions: []
|
|
311
|
-
path: path.slice()
|
|
327
|
+
unreconciledTransactions: []
|
|
312
328
|
}),
|
|
313
329
|
update: (value, tr) => {
|
|
314
330
|
const result = {
|
|
331
|
+
path: path.slice(),
|
|
315
332
|
lastHeads: value.lastHeads,
|
|
316
|
-
unreconciledTransactions: value.unreconciledTransactions.slice()
|
|
317
|
-
path: path.slice()
|
|
333
|
+
unreconciledTransactions: value.unreconciledTransactions.slice()
|
|
318
334
|
};
|
|
319
335
|
let clearUnreconciled = false;
|
|
320
336
|
for (const effect of tr.effects) {
|
|
@@ -333,39 +349,19 @@ var automerge3 = ({ handle, path }) => {
|
|
|
333
349
|
return result;
|
|
334
350
|
}
|
|
335
351
|
});
|
|
336
|
-
const semaphore = new PatchSemaphore(stateField);
|
|
337
|
-
const viewPlugin = ViewPlugin.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
338
|
-
constructor(_view) {
|
|
339
|
-
this._view = _view;
|
|
340
|
-
this._handleChange = () => {
|
|
341
|
-
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
342
|
-
};
|
|
343
|
-
handle.addListener("change", this._handleChange);
|
|
344
|
-
}
|
|
345
|
-
update(update4) {
|
|
346
|
-
if (update4.transactions.length > 0 && update4.transactions.some((tr) => !isReconcileTx(tr))) {
|
|
347
|
-
queueMicrotask(() => {
|
|
348
|
-
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
destroy() {
|
|
353
|
-
handle.addListener("change", this._handleChange);
|
|
354
|
-
}
|
|
355
|
-
});
|
|
352
|
+
const semaphore = new PatchSemaphore(handle, stateField);
|
|
356
353
|
return {
|
|
357
354
|
extension: [
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
stateField
|
|
361
|
-
viewPlugin
|
|
355
|
+
Cursor.converter.of(cursorConverter(handle, path)),
|
|
356
|
+
syncFacet.of(semaphore),
|
|
357
|
+
stateField
|
|
362
358
|
]
|
|
363
359
|
};
|
|
364
360
|
};
|
|
365
361
|
|
|
366
362
|
// packages/ui/react-ui-editor/src/extensions/awareness.ts
|
|
367
363
|
import { Annotation as Annotation2, Facet as Facet3, RangeSet } from "@codemirror/state";
|
|
368
|
-
import { Decoration, EditorView, ViewPlugin
|
|
364
|
+
import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view";
|
|
369
365
|
import { Event } from "@dxos/async";
|
|
370
366
|
import { Context } from "@dxos/context";
|
|
371
367
|
var dummyProvider = {
|
|
@@ -385,7 +381,7 @@ var RemoteSelectionChangedAnnotation = Annotation2.define();
|
|
|
385
381
|
var awareness = (provider = dummyProvider) => {
|
|
386
382
|
return [
|
|
387
383
|
AwarenessProvider.of(provider),
|
|
388
|
-
|
|
384
|
+
ViewPlugin.fromClass(RemoteSelectionsDecorator, {
|
|
389
385
|
decorations: (value) => value.decorations
|
|
390
386
|
}),
|
|
391
387
|
styles
|
|
@@ -397,7 +393,7 @@ var RemoteSelectionsDecorator = class {
|
|
|
397
393
|
this._ctx = new Context();
|
|
398
394
|
this._lastAnchor = void 0;
|
|
399
395
|
this._lastHead = void 0;
|
|
400
|
-
this._cursorConverter = view.state.facet(
|
|
396
|
+
this._cursorConverter = view.state.facet(Cursor.converter);
|
|
401
397
|
this._provider = view.state.facet(AwarenessProvider);
|
|
402
398
|
this._provider.open();
|
|
403
399
|
this._provider.remoteStateChange.on(this._ctx, () => {
|
|
@@ -412,13 +408,13 @@ var RemoteSelectionsDecorator = class {
|
|
|
412
408
|
void this._ctx.dispose();
|
|
413
409
|
this._provider.close();
|
|
414
410
|
}
|
|
415
|
-
update(
|
|
416
|
-
this._updateLocalSelection(
|
|
417
|
-
this._updateRemoteSelections(
|
|
411
|
+
update(update2) {
|
|
412
|
+
this._updateLocalSelection(update2);
|
|
413
|
+
this._updateRemoteSelections(update2);
|
|
418
414
|
}
|
|
419
|
-
_updateLocalSelection(
|
|
420
|
-
const hasFocus =
|
|
421
|
-
const { anchor = void 0, head = void 0 } = hasFocus ?
|
|
415
|
+
_updateLocalSelection(update2) {
|
|
416
|
+
const hasFocus = update2.view.hasFocus && update2.view.dom.ownerDocument.hasFocus();
|
|
417
|
+
const { anchor = void 0, head = void 0 } = hasFocus ? update2.state.selection.main : {};
|
|
422
418
|
if (this._lastAnchor === anchor && this._lastHead === head) {
|
|
423
419
|
return;
|
|
424
420
|
}
|
|
@@ -429,7 +425,7 @@ var RemoteSelectionsDecorator = class {
|
|
|
429
425
|
head: this._cursorConverter.toCursor(head)
|
|
430
426
|
} : void 0);
|
|
431
427
|
}
|
|
432
|
-
_updateRemoteSelections(
|
|
428
|
+
_updateRemoteSelections(update2) {
|
|
433
429
|
const decorations = [];
|
|
434
430
|
const awarenessStates = this._provider.getRemoteStates();
|
|
435
431
|
for (const state of awarenessStates) {
|
|
@@ -438,10 +434,10 @@ var RemoteSelectionsDecorator = class {
|
|
|
438
434
|
if (anchor == null || head == null) {
|
|
439
435
|
continue;
|
|
440
436
|
}
|
|
441
|
-
const start = Math.min(Math.min(anchor, head),
|
|
442
|
-
const end = Math.min(Math.max(anchor, head),
|
|
443
|
-
const startLine =
|
|
444
|
-
const endLine =
|
|
437
|
+
const start = Math.min(Math.min(anchor, head), update2.view.state.doc.length);
|
|
438
|
+
const end = Math.min(Math.max(anchor, head), update2.view.state.doc.length);
|
|
439
|
+
const startLine = update2.view.state.doc.lineAt(start);
|
|
440
|
+
const endLine = update2.view.state.doc.lineAt(end);
|
|
445
441
|
const color = state.info.color ?? "#30bced";
|
|
446
442
|
const lightColor = state.info.lightColor ?? color + "33";
|
|
447
443
|
if (startLine.number === endLine.number) {
|
|
@@ -452,7 +448,7 @@ var RemoteSelectionsDecorator = class {
|
|
|
452
448
|
attributes: {
|
|
453
449
|
style: `background-color: ${lightColor}`
|
|
454
450
|
},
|
|
455
|
-
class: "cm-
|
|
451
|
+
class: "cm-collab-selection"
|
|
456
452
|
})
|
|
457
453
|
});
|
|
458
454
|
} else {
|
|
@@ -463,7 +459,7 @@ var RemoteSelectionsDecorator = class {
|
|
|
463
459
|
attributes: {
|
|
464
460
|
style: `background-color: ${color}`
|
|
465
461
|
},
|
|
466
|
-
class: "cm-
|
|
462
|
+
class: "cm-collab-selection"
|
|
467
463
|
})
|
|
468
464
|
});
|
|
469
465
|
decorations.push({
|
|
@@ -473,18 +469,18 @@ var RemoteSelectionsDecorator = class {
|
|
|
473
469
|
attributes: {
|
|
474
470
|
style: `background-color: ${color}`
|
|
475
471
|
},
|
|
476
|
-
class: "cm-
|
|
472
|
+
class: "cm-collab-selection"
|
|
477
473
|
})
|
|
478
474
|
});
|
|
479
475
|
for (let i = startLine.number + 1; i < endLine.number; i++) {
|
|
480
|
-
const linePos =
|
|
476
|
+
const linePos = update2.view.state.doc.line(i).from;
|
|
481
477
|
decorations.push({
|
|
482
478
|
from: linePos,
|
|
483
479
|
to: linePos,
|
|
484
480
|
value: Decoration.line({
|
|
485
481
|
attributes: {
|
|
486
482
|
style: `background-color: ${color}`,
|
|
487
|
-
class: "cm-
|
|
483
|
+
class: "cm-collab-selectionLine"
|
|
488
484
|
}
|
|
489
485
|
})
|
|
490
486
|
});
|
|
@@ -504,23 +500,21 @@ var RemoteSelectionsDecorator = class {
|
|
|
504
500
|
}
|
|
505
501
|
};
|
|
506
502
|
var RemoteCaretWidget = class extends WidgetType {
|
|
507
|
-
constructor(
|
|
503
|
+
constructor(_name, _color) {
|
|
508
504
|
super();
|
|
509
|
-
this.
|
|
510
|
-
this.
|
|
511
|
-
this.name = name;
|
|
512
|
-
this.color = color;
|
|
505
|
+
this._name = _name;
|
|
506
|
+
this._color = _color;
|
|
513
507
|
}
|
|
514
508
|
toDOM() {
|
|
515
509
|
const span = document.createElement("span");
|
|
516
|
-
span.className = "cm-
|
|
517
|
-
span.style.backgroundColor = this.
|
|
518
|
-
span.style.borderColor = this.
|
|
510
|
+
span.className = "cm-collab-selectionCaret";
|
|
511
|
+
span.style.backgroundColor = this._color;
|
|
512
|
+
span.style.borderColor = this._color;
|
|
519
513
|
const dot = document.createElement("div");
|
|
520
|
-
dot.className = "cm-
|
|
514
|
+
dot.className = "cm-collab-selectionCaretDot";
|
|
521
515
|
const info = document.createElement("div");
|
|
522
|
-
info.className = "cm-
|
|
523
|
-
info.innerText = this.
|
|
516
|
+
info.className = "cm-collab-selectionInfo";
|
|
517
|
+
info.innerText = this._name;
|
|
524
518
|
span.appendChild(document.createTextNode("\u2060"));
|
|
525
519
|
span.appendChild(dot);
|
|
526
520
|
span.appendChild(document.createTextNode("\u2060"));
|
|
@@ -528,15 +522,12 @@ var RemoteCaretWidget = class extends WidgetType {
|
|
|
528
522
|
span.appendChild(document.createTextNode("\u2060"));
|
|
529
523
|
return span;
|
|
530
524
|
}
|
|
531
|
-
eq(widget) {
|
|
532
|
-
return widget.color === this.color;
|
|
533
|
-
}
|
|
534
|
-
compare(widget) {
|
|
535
|
-
return widget.color === this.color;
|
|
536
|
-
}
|
|
537
525
|
updateDOM() {
|
|
538
526
|
return false;
|
|
539
527
|
}
|
|
528
|
+
eq(widget) {
|
|
529
|
+
return widget._color === this._color;
|
|
530
|
+
}
|
|
540
531
|
get estimatedHeight() {
|
|
541
532
|
return -1;
|
|
542
533
|
}
|
|
@@ -545,12 +536,12 @@ var RemoteCaretWidget = class extends WidgetType {
|
|
|
545
536
|
}
|
|
546
537
|
};
|
|
547
538
|
var styles = EditorView.baseTheme({
|
|
548
|
-
".cm-
|
|
549
|
-
".cm-
|
|
539
|
+
".cm-collab-selection": {},
|
|
540
|
+
".cm-collab-selectionLine": {
|
|
550
541
|
padding: 0,
|
|
551
542
|
margin: "0px 2px 0px 4px"
|
|
552
543
|
},
|
|
553
|
-
".cm-
|
|
544
|
+
".cm-collab-selectionCaret": {
|
|
554
545
|
position: "relative",
|
|
555
546
|
borderLeft: "1px solid black",
|
|
556
547
|
borderRight: "1px solid black",
|
|
@@ -559,7 +550,7 @@ var styles = EditorView.baseTheme({
|
|
|
559
550
|
boxSizing: "border-box",
|
|
560
551
|
display: "inline"
|
|
561
552
|
},
|
|
562
|
-
".cm-
|
|
553
|
+
".cm-collab-selectionCaretDot": {
|
|
563
554
|
borderRadius: "50%",
|
|
564
555
|
position: "absolute",
|
|
565
556
|
width: ".4em",
|
|
@@ -570,11 +561,11 @@ var styles = EditorView.baseTheme({
|
|
|
570
561
|
transition: "transform .3s ease-in-out",
|
|
571
562
|
boxSizing: "border-box"
|
|
572
563
|
},
|
|
573
|
-
".cm-
|
|
564
|
+
".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
|
|
574
565
|
transformOrigin: "bottom center",
|
|
575
566
|
transform: "scale(0)"
|
|
576
567
|
},
|
|
577
|
-
".cm-
|
|
568
|
+
".cm-collab-selectionInfo": {
|
|
578
569
|
position: "absolute",
|
|
579
570
|
top: "-1.05em",
|
|
580
571
|
left: "-1px",
|
|
@@ -590,12 +581,12 @@ var styles = EditorView.baseTheme({
|
|
|
590
581
|
zIndex: 101,
|
|
591
582
|
transition: "opacity .3s ease-in-out",
|
|
592
583
|
backgroundColor: "inherit",
|
|
593
|
-
//
|
|
584
|
+
// These should be separate.
|
|
594
585
|
opacity: 0,
|
|
595
586
|
transitionDelay: "0s",
|
|
596
587
|
whiteSpace: "nowrap"
|
|
597
588
|
},
|
|
598
|
-
".cm-
|
|
589
|
+
".cm-collab-selectionCaret:hover > .cm-collab-selectionInfo": {
|
|
599
590
|
opacity: 1,
|
|
600
591
|
transitionDelay: "0s"
|
|
601
592
|
}
|
|
@@ -622,7 +613,7 @@ var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
|
|
|
622
613
|
import { EditorView as EditorView3, keymap as keymap2 } from "@codemirror/view";
|
|
623
614
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
624
615
|
import { invariant } from "@dxos/invariant";
|
|
625
|
-
var
|
|
616
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
|
|
626
617
|
var defaultOptions = {
|
|
627
618
|
effect: 2,
|
|
628
619
|
maxParticles: 200,
|
|
@@ -667,12 +658,12 @@ var blast = (options = defaultOptions) => {
|
|
|
667
658
|
};
|
|
668
659
|
return [
|
|
669
660
|
// Cursor moved.
|
|
670
|
-
EditorView3.updateListener.of((
|
|
671
|
-
if (blaster?.node !==
|
|
661
|
+
EditorView3.updateListener.of((update2) => {
|
|
662
|
+
if (blaster?.node !== update2.view.scrollDOM) {
|
|
672
663
|
if (blaster) {
|
|
673
664
|
blaster.destroy();
|
|
674
665
|
}
|
|
675
|
-
blaster = new Blaster(
|
|
666
|
+
blaster = new Blaster(update2.view.scrollDOM, defaultsDeep({
|
|
676
667
|
particleGravity: 0.2,
|
|
677
668
|
particleShrinkRate: 0.995,
|
|
678
669
|
color: () => [
|
|
@@ -686,10 +677,10 @@ var blast = (options = defaultOptions) => {
|
|
|
686
677
|
} else {
|
|
687
678
|
blaster.resize();
|
|
688
679
|
}
|
|
689
|
-
const current =
|
|
680
|
+
const current = update2.state.selection.main.head;
|
|
690
681
|
if (current !== last) {
|
|
691
682
|
last = current;
|
|
692
|
-
const { element, point } = getPoint(
|
|
683
|
+
const { element, point } = getPoint(update2.view, current - 1);
|
|
693
684
|
if (element && point) {
|
|
694
685
|
blaster.spawn({
|
|
695
686
|
element,
|
|
@@ -749,7 +740,7 @@ var Blaster = class {
|
|
|
749
740
|
}
|
|
750
741
|
initialize() {
|
|
751
742
|
invariant(!this._canvas && !this._ctx, void 0, {
|
|
752
|
-
F:
|
|
743
|
+
F: __dxlog_file,
|
|
753
744
|
L: 138,
|
|
754
745
|
S: this,
|
|
755
746
|
A: [
|
|
@@ -786,7 +777,7 @@ var Blaster = class {
|
|
|
786
777
|
}
|
|
787
778
|
start() {
|
|
788
779
|
invariant(this._canvas && this._ctx, void 0, {
|
|
789
|
-
F:
|
|
780
|
+
F: __dxlog_file,
|
|
790
781
|
L: 177,
|
|
791
782
|
S: this,
|
|
792
783
|
A: [
|
|
@@ -929,8 +920,7 @@ var random = (min, max) => {
|
|
|
929
920
|
};
|
|
930
921
|
|
|
931
922
|
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
932
|
-
import { ViewPlugin as
|
|
933
|
-
import get2 from "lodash.get";
|
|
923
|
+
import { ViewPlugin as ViewPlugin2, EditorView as EditorView4, Decoration as Decoration2 } from "@codemirror/view";
|
|
934
924
|
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
935
925
|
|
|
936
926
|
// packages/ui/react-ui-editor/src/styles/cursors.ts
|
|
@@ -1014,20 +1004,22 @@ var inlineUrl = mx(code, "px-1");
|
|
|
1014
1004
|
var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
|
|
1015
1005
|
|
|
1016
1006
|
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
1007
|
+
import get2 from "lodash.get";
|
|
1017
1008
|
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
1018
1009
|
var tokens = tailwindConfig({}).theme;
|
|
1010
|
+
var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
|
|
1019
1011
|
|
|
1020
1012
|
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
1021
1013
|
var CODE_REGEX = /```[\s\S]*?```/gs;
|
|
1022
1014
|
var styles2 = EditorView4.baseTheme({
|
|
1023
1015
|
"& .cm-codeblock": {
|
|
1024
|
-
fontFamily:
|
|
1016
|
+
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
1025
1017
|
},
|
|
1026
1018
|
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
1027
|
-
background:
|
|
1019
|
+
background: getToken("extend.colors.neutral.50")
|
|
1028
1020
|
},
|
|
1029
1021
|
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
1030
|
-
background:
|
|
1022
|
+
background: getToken("extend.colors.neutral.850")
|
|
1031
1023
|
},
|
|
1032
1024
|
'& [aria-readonly="true"] .cm-codeblock': {
|
|
1033
1025
|
display: "block",
|
|
@@ -1053,32 +1045,35 @@ var getLineRange = (lines, from, to) => {
|
|
|
1053
1045
|
];
|
|
1054
1046
|
};
|
|
1055
1047
|
var code2 = () => {
|
|
1056
|
-
const
|
|
1048
|
+
const buildDecorations4 = (view) => {
|
|
1057
1049
|
const decorations = [];
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
}).range(block.from));
|
|
1069
|
-
}
|
|
1050
|
+
const text = view.state.doc.sliceString(0);
|
|
1051
|
+
const matches = text.matchAll(CODE_REGEX);
|
|
1052
|
+
const blocks = view.viewportLineBlocks;
|
|
1053
|
+
for (const match of matches) {
|
|
1054
|
+
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
1055
|
+
for (let i = range[0]; i <= range[1]; i++) {
|
|
1056
|
+
const block = blocks[i];
|
|
1057
|
+
decorations.push(Decoration2.line({
|
|
1058
|
+
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
1059
|
+
}).range(block.from));
|
|
1070
1060
|
}
|
|
1071
1061
|
}
|
|
1072
1062
|
return Decoration2.set(decorations);
|
|
1073
1063
|
};
|
|
1074
1064
|
return [
|
|
1075
|
-
|
|
1065
|
+
ViewPlugin2.fromClass(class {
|
|
1076
1066
|
constructor(view) {
|
|
1077
|
-
this.
|
|
1067
|
+
this.hasFocus = false;
|
|
1068
|
+
this.decorations = buildDecorations4(view);
|
|
1078
1069
|
}
|
|
1079
|
-
update(
|
|
1080
|
-
if (
|
|
1081
|
-
|
|
1070
|
+
update(update2) {
|
|
1071
|
+
if (
|
|
1072
|
+
// TODO(burdon): Generalize for other extensions.
|
|
1073
|
+
this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
|
|
1074
|
+
) {
|
|
1075
|
+
this.hasFocus = update2.view.hasFocus;
|
|
1076
|
+
this.decorations = buildDecorations4(update2.view);
|
|
1082
1077
|
}
|
|
1083
1078
|
}
|
|
1084
1079
|
}, {
|
|
@@ -1090,29 +1085,36 @@ var code2 = () => {
|
|
|
1090
1085
|
|
|
1091
1086
|
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1092
1087
|
import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
1093
|
-
import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5
|
|
1094
|
-
import get3 from "lodash.get";
|
|
1088
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
|
|
1095
1089
|
import sortBy from "lodash.sortby";
|
|
1096
1090
|
import { debounce } from "@dxos/async";
|
|
1097
1091
|
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1098
|
-
import { log as log2 } from "@dxos/log";
|
|
1099
1092
|
import { nonNullable } from "@dxos/util";
|
|
1093
|
+
|
|
1094
|
+
// packages/ui/react-ui-editor/src/util.ts
|
|
1095
|
+
import { log } from "@dxos/log";
|
|
1096
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
|
|
1097
|
+
var callbackWrapper = (fn) => (...args) => {
|
|
1098
|
+
try {
|
|
1099
|
+
return fn(...args);
|
|
1100
|
+
} catch (error) {
|
|
1101
|
+
log.catch(error, void 0, {
|
|
1102
|
+
F: __dxlog_file2,
|
|
1103
|
+
L: 16,
|
|
1104
|
+
S: void 0,
|
|
1105
|
+
C: (f, a) => f(...a)
|
|
1106
|
+
});
|
|
1107
|
+
}
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1100
1111
|
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
|
|
1101
1112
|
var styles3 = EditorView5.baseTheme({
|
|
1102
|
-
"& .cm-bookmark": {
|
|
1103
|
-
cursor: "pointer",
|
|
1104
|
-
margin: "4px",
|
|
1105
|
-
padding: "4px",
|
|
1106
|
-
backgroundColor: "yellow"
|
|
1107
|
-
},
|
|
1108
|
-
"& .cm-bookmark-selected": {
|
|
1109
|
-
backgroundColor: "orange"
|
|
1110
|
-
},
|
|
1111
1113
|
"& .cm-comment": {
|
|
1112
|
-
backgroundColor:
|
|
1114
|
+
backgroundColor: getToken("extend.colors.yellow.50")
|
|
1113
1115
|
},
|
|
1114
1116
|
"& .cm-comment-active": {
|
|
1115
|
-
backgroundColor:
|
|
1117
|
+
backgroundColor: getToken("extend.colors.yellow.100")
|
|
1116
1118
|
}
|
|
1117
1119
|
});
|
|
1118
1120
|
var marks = {
|
|
@@ -1124,8 +1126,8 @@ var marks = {
|
|
|
1124
1126
|
})
|
|
1125
1127
|
};
|
|
1126
1128
|
var setFocus = (view, thread) => {
|
|
1127
|
-
const
|
|
1128
|
-
if (!
|
|
1129
|
+
const comment = view.state.field(commentsState).comments.find((range) => range.comment.id === thread);
|
|
1130
|
+
if (!comment) {
|
|
1129
1131
|
return;
|
|
1130
1132
|
}
|
|
1131
1133
|
view.dispatch({
|
|
@@ -1133,39 +1135,41 @@ var setFocus = (view, thread) => {
|
|
|
1133
1135
|
active: thread
|
|
1134
1136
|
}),
|
|
1135
1137
|
selection: {
|
|
1136
|
-
anchor: range.from
|
|
1138
|
+
anchor: comment.range.from
|
|
1137
1139
|
},
|
|
1138
1140
|
scrollIntoView: true
|
|
1139
1141
|
});
|
|
1140
1142
|
};
|
|
1141
|
-
var
|
|
1143
|
+
var setComments = StateEffect2.define();
|
|
1142
1144
|
var setSelection = StateEffect2.define();
|
|
1143
1145
|
var setCommentState = StateEffect2.define();
|
|
1144
|
-
var
|
|
1146
|
+
var commentsState = StateField2.define({
|
|
1145
1147
|
create: () => ({
|
|
1146
|
-
|
|
1148
|
+
comments: [],
|
|
1149
|
+
selection: {}
|
|
1147
1150
|
}),
|
|
1148
1151
|
update: (value, tr) => {
|
|
1149
|
-
const cursorConverter3 = tr.state.facet(CursorConverter);
|
|
1150
1152
|
for (const effect of tr.effects) {
|
|
1151
1153
|
if (effect.is(setSelection)) {
|
|
1152
1154
|
return {
|
|
1153
1155
|
...value,
|
|
1154
|
-
|
|
1156
|
+
selection: effect.value
|
|
1155
1157
|
};
|
|
1156
1158
|
}
|
|
1157
|
-
if (effect.is(
|
|
1158
|
-
const
|
|
1159
|
-
|
|
1160
|
-
|
|
1159
|
+
if (effect.is(setComments)) {
|
|
1160
|
+
const comments2 = effect.value.map((comment) => {
|
|
1161
|
+
if (!comment.cursor) {
|
|
1162
|
+
return void 0;
|
|
1163
|
+
}
|
|
1164
|
+
const range = Cursor.getRangeFromCursor(tr.state, comment.cursor);
|
|
1161
1165
|
return range && {
|
|
1162
|
-
|
|
1163
|
-
|
|
1166
|
+
comment,
|
|
1167
|
+
range
|
|
1164
1168
|
};
|
|
1165
1169
|
}).filter(nonNullable);
|
|
1166
1170
|
return {
|
|
1167
1171
|
...value,
|
|
1168
|
-
|
|
1172
|
+
comments: comments2
|
|
1169
1173
|
};
|
|
1170
1174
|
}
|
|
1171
1175
|
if (effect.is(setCommentState)) {
|
|
@@ -1176,77 +1180,109 @@ var commentsStateField = StateField2.define({
|
|
|
1176
1180
|
}
|
|
1177
1181
|
});
|
|
1178
1182
|
var highlightDecorations = EditorView5.decorations.compute([
|
|
1179
|
-
|
|
1183
|
+
commentsState
|
|
1180
1184
|
], (state) => {
|
|
1181
|
-
const { active,
|
|
1182
|
-
const decorations = sortBy(
|
|
1183
|
-
|
|
1185
|
+
const { selection: { active }, comments: comments2 } = state.field(commentsState);
|
|
1186
|
+
const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
|
|
1187
|
+
const range = comment.range;
|
|
1188
|
+
if (!range || range.from === range.to) {
|
|
1189
|
+
console.warn("Invalid range:", range);
|
|
1184
1190
|
return void 0;
|
|
1185
1191
|
}
|
|
1186
|
-
|
|
1187
|
-
from: selection.from,
|
|
1188
|
-
to: selection.to
|
|
1189
|
-
};
|
|
1190
|
-
if (selection.id === active) {
|
|
1192
|
+
if (comment.comment.id === active) {
|
|
1191
1193
|
return marks.highlightActive.range(range.from, range.to);
|
|
1192
1194
|
} else {
|
|
1193
1195
|
return marks.highlight.range(range.from, range.to);
|
|
1194
1196
|
}
|
|
1195
|
-
}).filter(nonNullable)
|
|
1197
|
+
}).filter(nonNullable);
|
|
1196
1198
|
return Decoration3.set(decorations);
|
|
1197
1199
|
});
|
|
1198
|
-
var
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
return this._anchor === other._anchor && this._id === other._id;
|
|
1216
|
-
}
|
|
1217
|
-
toDOM() {
|
|
1218
|
-
const span = document.createElement("span");
|
|
1219
|
-
span.className = "cm-bookmark";
|
|
1220
|
-
span.textContent = "\u203B";
|
|
1221
|
-
if (this._handleClick) {
|
|
1222
|
-
span.onclick = () => this._handleClick();
|
|
1200
|
+
var trackPastedComments = (onUpdate) => {
|
|
1201
|
+
let tracked = null;
|
|
1202
|
+
const handleTrack = (event, view) => {
|
|
1203
|
+
const comments2 = view.state.field(commentsState);
|
|
1204
|
+
const { main } = view.state.selection;
|
|
1205
|
+
const selectedRanges = comments2.comments.filter(({ range }) => range.from >= main.from && range.to <= main.to && range.from < range.to);
|
|
1206
|
+
if (!selectedRanges.length) {
|
|
1207
|
+
tracked = null;
|
|
1208
|
+
} else {
|
|
1209
|
+
tracked = {
|
|
1210
|
+
text: view.state.doc.slice(main.from, main.to),
|
|
1211
|
+
comments: selectedRanges.map(({ comment, range }) => ({
|
|
1212
|
+
id: comment.id,
|
|
1213
|
+
from: range.from - main.from,
|
|
1214
|
+
to: range.to - main.from
|
|
1215
|
+
}))
|
|
1216
|
+
};
|
|
1223
1217
|
}
|
|
1224
|
-
|
|
1225
|
-
|
|
1218
|
+
};
|
|
1219
|
+
return [
|
|
1220
|
+
EditorView5.domEventHandlers({
|
|
1221
|
+
cut: handleTrack,
|
|
1222
|
+
copy: handleTrack
|
|
1223
|
+
}),
|
|
1224
|
+
// Handle paste.
|
|
1225
|
+
EditorView5.updateListener.of(({ view, state, transactions }) => {
|
|
1226
|
+
if (tracked) {
|
|
1227
|
+
const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
|
|
1228
|
+
if (paste) {
|
|
1229
|
+
let found = -1;
|
|
1230
|
+
paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
1231
|
+
if (text.eq(tracked.text)) {
|
|
1232
|
+
for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
|
|
1233
|
+
fromB = transactions[i].changes.mapPos(fromB);
|
|
1234
|
+
}
|
|
1235
|
+
found = fromB;
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
if (found > -1) {
|
|
1239
|
+
state.facet(syncFacet)?.reconcile(view);
|
|
1240
|
+
for (const moved of tracked.comments) {
|
|
1241
|
+
const range = {
|
|
1242
|
+
from: found + moved.from,
|
|
1243
|
+
to: found + moved.to
|
|
1244
|
+
};
|
|
1245
|
+
const cursor = Cursor.getCursorFromRange(state, range);
|
|
1246
|
+
onUpdate(moved.id, cursor);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
tracked = null;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
})
|
|
1253
|
+
];
|
|
1226
1254
|
};
|
|
1227
1255
|
var comments = (options = {}) => {
|
|
1256
|
+
const { key: shortcut = "meta-'" } = options;
|
|
1228
1257
|
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1229
1258
|
const createCommentThread = (view) => {
|
|
1230
|
-
const cursorConverter3 = view.state.facet(CursorConverter);
|
|
1231
1259
|
invariant2(options.onCreate, void 0, {
|
|
1232
1260
|
F: __dxlog_file3,
|
|
1233
|
-
L:
|
|
1261
|
+
L: 262,
|
|
1234
1262
|
S: void 0,
|
|
1235
1263
|
A: [
|
|
1236
1264
|
"options.onCreate",
|
|
1237
1265
|
""
|
|
1238
1266
|
]
|
|
1239
1267
|
});
|
|
1240
|
-
const {
|
|
1268
|
+
const { from, to } = view.state.selection.main;
|
|
1241
1269
|
if (from === to) {
|
|
1242
1270
|
return false;
|
|
1243
1271
|
}
|
|
1244
|
-
|
|
1272
|
+
if (to === view.state.doc.length) {
|
|
1273
|
+
view.dispatch({
|
|
1274
|
+
changes: {
|
|
1275
|
+
from: to,
|
|
1276
|
+
insert: "\n"
|
|
1277
|
+
}
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
const cursor = Cursor.getCursorFromRange(view.state, {
|
|
1245
1281
|
from,
|
|
1246
1282
|
to
|
|
1247
1283
|
});
|
|
1248
|
-
if (
|
|
1249
|
-
const id = callbackWrapper(options.onCreate)(
|
|
1284
|
+
if (cursor) {
|
|
1285
|
+
const id = callbackWrapper(options.onCreate)(cursor, view.coordsAtPos(from));
|
|
1250
1286
|
if (id) {
|
|
1251
1287
|
view.dispatch({
|
|
1252
1288
|
effects: setSelection.of({
|
|
@@ -1256,53 +1292,13 @@ var comments = (options = {}) => {
|
|
|
1256
1292
|
anchor: from
|
|
1257
1293
|
}
|
|
1258
1294
|
});
|
|
1259
|
-
if (options.footnote) {
|
|
1260
|
-
const tag = `[^${id}]`;
|
|
1261
|
-
view.dispatch({
|
|
1262
|
-
changes: {
|
|
1263
|
-
from: head,
|
|
1264
|
-
insert: tag
|
|
1265
|
-
},
|
|
1266
|
-
selection: {
|
|
1267
|
-
anchor: head + tag.length
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
1295
|
return true;
|
|
1272
1296
|
}
|
|
1273
1297
|
}
|
|
1274
1298
|
return false;
|
|
1275
1299
|
};
|
|
1276
|
-
const bookmarksViewPlugin = ViewPlugin4.fromClass(class BookmarkViewPlugin {
|
|
1277
|
-
static {
|
|
1278
|
-
// Match markdown footnotes (option).
|
|
1279
|
-
// https://www.markdownguide.org/extended-syntax/#footnotes
|
|
1280
|
-
this.bookmarkMatcher = new MatchDecorator({
|
|
1281
|
-
regexp: /\[\^(\w+)\]/g,
|
|
1282
|
-
decoration: (match, view, pos) => {
|
|
1283
|
-
const id = match[1];
|
|
1284
|
-
return Decoration3.replace({
|
|
1285
|
-
id,
|
|
1286
|
-
widget: new BookmarkWidget(pos, id)
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1289
|
-
});
|
|
1290
|
-
}
|
|
1291
|
-
constructor(view) {
|
|
1292
|
-
this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.createDeco(view);
|
|
1293
|
-
}
|
|
1294
|
-
update(update4) {
|
|
1295
|
-
this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.updateDeco(update4, this.bookmarks);
|
|
1296
|
-
}
|
|
1297
|
-
}, {
|
|
1298
|
-
decorations: (instance) => instance.bookmarks,
|
|
1299
|
-
provide: (plugin) => EditorView5.atomicRanges.of((view) => {
|
|
1300
|
-
return view.plugin(plugin)?.bookmarks || Decoration3.none;
|
|
1301
|
-
})
|
|
1302
|
-
});
|
|
1303
1300
|
return [
|
|
1304
|
-
|
|
1305
|
-
commentsStateField,
|
|
1301
|
+
commentsState,
|
|
1306
1302
|
highlightDecorations,
|
|
1307
1303
|
styles3,
|
|
1308
1304
|
//
|
|
@@ -1310,7 +1306,8 @@ var comments = (options = {}) => {
|
|
|
1310
1306
|
//
|
|
1311
1307
|
options.onCreate ? keymap3.of([
|
|
1312
1308
|
{
|
|
1313
|
-
key:
|
|
1309
|
+
key: shortcut,
|
|
1310
|
+
// TODO(burdon): Check if there's a better way?
|
|
1314
1311
|
run: callbackWrapper(createCommentThread)
|
|
1315
1312
|
}
|
|
1316
1313
|
]) : [],
|
|
@@ -1327,7 +1324,7 @@ var comments = (options = {}) => {
|
|
|
1327
1324
|
above: true,
|
|
1328
1325
|
create: () => {
|
|
1329
1326
|
const el = document.createElement("div");
|
|
1330
|
-
options.onHover
|
|
1327
|
+
options.onHover(el, shortcut);
|
|
1331
1328
|
return {
|
|
1332
1329
|
dom: el,
|
|
1333
1330
|
offset: {
|
|
@@ -1340,112 +1337,89 @@ var comments = (options = {}) => {
|
|
|
1340
1337
|
}
|
|
1341
1338
|
return null;
|
|
1342
1339
|
}, {
|
|
1343
|
-
|
|
1340
|
+
// TODO(burdon): Hide on change triggered immediately?
|
|
1341
|
+
// hideOnChange: true,
|
|
1344
1342
|
hoverTime: 1e3
|
|
1345
1343
|
}) : [],
|
|
1346
1344
|
//
|
|
1347
|
-
//
|
|
1348
|
-
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1345
|
+
// Track deleted ranges and update ranges for decorations.
|
|
1349
1346
|
//
|
|
1350
1347
|
EditorView5.updateListener.of(({ view, state, changes }) => {
|
|
1351
|
-
|
|
1352
|
-
{
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
if (
|
|
1358
|
-
|
|
1359
|
-
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1360
|
-
log2.info("deleted comment", {
|
|
1361
|
-
thread: range.id
|
|
1362
|
-
}, {
|
|
1363
|
-
F: __dxlog_file3,
|
|
1364
|
-
L: 359,
|
|
1365
|
-
S: void 0,
|
|
1366
|
-
C: (f, a) => f(...a)
|
|
1367
|
-
});
|
|
1368
|
-
}
|
|
1348
|
+
let mod = false;
|
|
1349
|
+
const { comments: comments2, ...value } = state.field(commentsState);
|
|
1350
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1351
|
+
comments2.forEach(({ comment, range }) => {
|
|
1352
|
+
if (from2 === to2) {
|
|
1353
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1354
|
+
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1355
|
+
options.onDelete?.(comment.id);
|
|
1369
1356
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
}
|
|
1357
|
+
}
|
|
1358
|
+
if (from <= range.to) {
|
|
1359
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1360
|
+
Object.assign(range, newRange);
|
|
1361
|
+
mod = true;
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
});
|
|
1365
|
+
if (mod) {
|
|
1366
|
+
view.dispatch({
|
|
1367
|
+
effects: setCommentState.of({
|
|
1368
|
+
comments: comments2,
|
|
1369
|
+
...value
|
|
1370
|
+
})
|
|
1376
1371
|
});
|
|
1377
|
-
if (mod) {
|
|
1378
|
-
view.dispatch({
|
|
1379
|
-
effects: setCommentState.of({
|
|
1380
|
-
active,
|
|
1381
|
-
ranges
|
|
1382
|
-
})
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
1372
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1373
|
+
}),
|
|
1374
|
+
//
|
|
1375
|
+
// Track selection/proximity.
|
|
1376
|
+
//
|
|
1377
|
+
EditorView5.updateListener.of(({ view, state, changes }) => {
|
|
1378
|
+
let min = Infinity;
|
|
1379
|
+
const { selection: { active, closest }, comments: comments2 } = state.field(commentsState);
|
|
1380
|
+
const { head } = state.selection.main;
|
|
1381
|
+
const selection = {};
|
|
1382
|
+
comments2.forEach(({ comment, range }) => {
|
|
1383
|
+
if (head >= range.from && head <= range.to) {
|
|
1384
|
+
selection.active = comment.id;
|
|
1385
|
+
selection.closest = void 0;
|
|
1386
|
+
}
|
|
1387
|
+
if (!selection.active) {
|
|
1388
|
+
const d = Math.min(Math.abs(head - range.from), Math.abs(head - range.to));
|
|
1399
1389
|
if (d < min) {
|
|
1400
|
-
|
|
1390
|
+
selection.closest = comment.id;
|
|
1401
1391
|
min = d;
|
|
1402
1392
|
}
|
|
1403
|
-
});
|
|
1404
|
-
if (selected.active !== active || selected.closest !== closest) {
|
|
1405
|
-
view.dispatch({
|
|
1406
|
-
effects: setSelection.of(selected)
|
|
1407
|
-
});
|
|
1408
|
-
handleSelect({
|
|
1409
|
-
...selected,
|
|
1410
|
-
ranges: ranges.map((range) => ({
|
|
1411
|
-
...range,
|
|
1412
|
-
location: view.coordsAtPos(range.from)
|
|
1413
|
-
}))
|
|
1414
|
-
});
|
|
1415
1393
|
}
|
|
1394
|
+
});
|
|
1395
|
+
if (selection.active !== active || selection.closest !== closest) {
|
|
1396
|
+
view.dispatch({
|
|
1397
|
+
effects: setSelection.of(selection)
|
|
1398
|
+
});
|
|
1399
|
+
handleSelect({
|
|
1400
|
+
selection,
|
|
1401
|
+
comments: comments2.map(({ comment, range }) => ({
|
|
1402
|
+
comment,
|
|
1403
|
+
range,
|
|
1404
|
+
location: view.coordsAtPos(range.from)
|
|
1405
|
+
}))
|
|
1406
|
+
});
|
|
1416
1407
|
}
|
|
1417
|
-
})
|
|
1408
|
+
}),
|
|
1409
|
+
options.onUpdate ? trackPastedComments(options.onUpdate) : []
|
|
1418
1410
|
];
|
|
1419
1411
|
};
|
|
1420
|
-
var getCursorFromRange = (cursorConverter3, range) => {
|
|
1421
|
-
const from = cursorConverter3.toCursor(range.from);
|
|
1422
|
-
const to = cursorConverter3.toCursor(range.to, -1);
|
|
1423
|
-
return [
|
|
1424
|
-
from,
|
|
1425
|
-
to
|
|
1426
|
-
].join(":");
|
|
1427
|
-
};
|
|
1428
|
-
var getRangeFromCursor = (cursorConverter3, cursor) => {
|
|
1429
|
-
const parts = cursor.split(":");
|
|
1430
|
-
const from = cursorConverter3.fromCursor(parts[0]);
|
|
1431
|
-
const to = cursorConverter3.fromCursor(parts[1]);
|
|
1432
|
-
return from && to ? {
|
|
1433
|
-
from,
|
|
1434
|
-
to
|
|
1435
|
-
} : void 0;
|
|
1436
|
-
};
|
|
1437
1412
|
|
|
1438
1413
|
// packages/ui/react-ui-editor/src/extensions/hr.ts
|
|
1439
|
-
import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator
|
|
1440
|
-
import get4 from "lodash.get";
|
|
1414
|
+
import { Decoration as Decoration4, EditorView as EditorView6, MatchDecorator, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1441
1415
|
var styles4 = EditorView6.baseTheme({
|
|
1442
1416
|
"& .cm-hr": {
|
|
1443
1417
|
// TODO(burdon): ???
|
|
1444
1418
|
// Note that block-level decorations should not have vertical margins,
|
|
1445
|
-
borderBottom: `1px solid ${
|
|
1419
|
+
borderBottom: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
1446
1420
|
}
|
|
1447
1421
|
});
|
|
1448
|
-
var HorizontalRuleWidget = class extends
|
|
1422
|
+
var HorizontalRuleWidget = class extends WidgetType2 {
|
|
1449
1423
|
constructor(_pos) {
|
|
1450
1424
|
super();
|
|
1451
1425
|
this._pos = _pos;
|
|
@@ -1459,43 +1433,62 @@ var HorizontalRuleWidget = class extends WidgetType3 {
|
|
|
1459
1433
|
return el;
|
|
1460
1434
|
}
|
|
1461
1435
|
};
|
|
1462
|
-
var placeholderMatcher = new
|
|
1463
|
-
regexp:
|
|
1436
|
+
var placeholderMatcher = new MatchDecorator({
|
|
1437
|
+
// regexp: /(?<=\n\n)---/gs,
|
|
1438
|
+
regexp: /^---$/gs,
|
|
1464
1439
|
decoration: (match, view, pos) => Decoration4.replace({
|
|
1465
1440
|
widget: new HorizontalRuleWidget(pos)
|
|
1466
1441
|
})
|
|
1467
1442
|
});
|
|
1468
1443
|
var hr = () => [
|
|
1469
1444
|
styles4,
|
|
1470
|
-
|
|
1445
|
+
ViewPlugin3.fromClass(class {
|
|
1471
1446
|
constructor(view) {
|
|
1472
1447
|
this.rules = placeholderMatcher.createDeco(view);
|
|
1473
1448
|
}
|
|
1474
|
-
update(
|
|
1475
|
-
this.rules = placeholderMatcher.updateDeco(
|
|
1449
|
+
update(update2) {
|
|
1450
|
+
this.rules = placeholderMatcher.updateDeco(update2, this.rules);
|
|
1476
1451
|
}
|
|
1477
1452
|
}, {
|
|
1478
|
-
decorations: (instance) => instance.rules
|
|
1479
|
-
// TODO(burdon): Is this really atomic (cursor can move into ---).
|
|
1480
|
-
provide: (plugin) => EditorView6.atomicRanges.of((view) => {
|
|
1481
|
-
return view.plugin(plugin)?.rules || Decoration4.none;
|
|
1482
|
-
})
|
|
1453
|
+
decorations: (instance) => instance.rules
|
|
1483
1454
|
})
|
|
1484
1455
|
];
|
|
1485
1456
|
|
|
1486
1457
|
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
1487
1458
|
import { syntaxTree } from "@codemirror/language";
|
|
1488
|
-
import {
|
|
1489
|
-
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as
|
|
1459
|
+
import { StateField as StateField3 } from "@codemirror/state";
|
|
1460
|
+
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
1490
1461
|
var image = (options = {}) => {
|
|
1491
1462
|
return StateField3.define({
|
|
1492
|
-
create: (state) =>
|
|
1493
|
-
|
|
1463
|
+
create: (state) => {
|
|
1464
|
+
return Decoration5.set(buildDecorations(0, state.doc.length, state));
|
|
1465
|
+
},
|
|
1466
|
+
update: (value, tr) => {
|
|
1467
|
+
if (!tr.docChanged && !tr.selection) {
|
|
1468
|
+
return value;
|
|
1469
|
+
}
|
|
1470
|
+
const cursor = tr.state.selection.main.head;
|
|
1471
|
+
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
1472
|
+
let from = Math.min(cursor, oldCursor);
|
|
1473
|
+
let to = Math.max(cursor, oldCursor);
|
|
1474
|
+
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
1475
|
+
from = Math.min(from, fromB);
|
|
1476
|
+
to = Math.max(to, toB);
|
|
1477
|
+
});
|
|
1478
|
+
from = tr.state.doc.lineAt(from).from;
|
|
1479
|
+
to = tr.state.doc.lineAt(to).to;
|
|
1480
|
+
return value.map(tr.changes).update({
|
|
1481
|
+
filterFrom: from,
|
|
1482
|
+
filterTo: to,
|
|
1483
|
+
filter: () => false,
|
|
1484
|
+
add: buildDecorations(from, to, tr.state)
|
|
1485
|
+
});
|
|
1486
|
+
},
|
|
1494
1487
|
provide: (field) => EditorView7.decorations.from(field)
|
|
1495
1488
|
});
|
|
1496
1489
|
};
|
|
1497
|
-
var
|
|
1498
|
-
const
|
|
1490
|
+
var buildDecorations = (from, to, state) => {
|
|
1491
|
+
const decorations = [];
|
|
1499
1492
|
const cursor = state.selection.main.head;
|
|
1500
1493
|
syntaxTree(state).iterate({
|
|
1501
1494
|
enter: (node) => {
|
|
@@ -1504,22 +1497,23 @@ var update = (state, options) => {
|
|
|
1504
1497
|
if (urlNode) {
|
|
1505
1498
|
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
1506
1499
|
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
1507
|
-
|
|
1500
|
+
decorations.push(Decoration5.replace({
|
|
1508
1501
|
block: true,
|
|
1509
1502
|
widget: new ImageWidget(url)
|
|
1510
|
-
}));
|
|
1503
|
+
}).range(hide ? node.from : node.to, node.to));
|
|
1511
1504
|
}
|
|
1512
1505
|
}
|
|
1513
|
-
}
|
|
1506
|
+
},
|
|
1507
|
+
from,
|
|
1508
|
+
to
|
|
1514
1509
|
});
|
|
1515
|
-
return
|
|
1510
|
+
return decorations;
|
|
1516
1511
|
};
|
|
1517
|
-
var ImageWidget = class extends
|
|
1512
|
+
var ImageWidget = class extends WidgetType3 {
|
|
1518
1513
|
constructor(_url) {
|
|
1519
1514
|
super();
|
|
1520
1515
|
this._url = _url;
|
|
1521
1516
|
}
|
|
1522
|
-
// TODO(burdon): Does this need to be unique for each position?
|
|
1523
1517
|
eq(other) {
|
|
1524
1518
|
return this._url === other._url;
|
|
1525
1519
|
}
|
|
@@ -1527,22 +1521,29 @@ var ImageWidget = class extends WidgetType4 {
|
|
|
1527
1521
|
const img = document.createElement("img");
|
|
1528
1522
|
img.setAttribute("src", this._url);
|
|
1529
1523
|
img.setAttribute("class", "cm-image");
|
|
1524
|
+
img.onload = () => img.classList.add("cm-loaded-image");
|
|
1530
1525
|
return img;
|
|
1531
1526
|
}
|
|
1532
1527
|
};
|
|
1533
1528
|
|
|
1534
1529
|
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
1535
1530
|
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1536
|
-
import { RangeSetBuilder
|
|
1537
|
-
import {
|
|
1531
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
1532
|
+
import { hoverTooltip as hoverTooltip2, Decoration as Decoration6, ViewPlugin as ViewPlugin4, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
1538
1533
|
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1539
|
-
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1540
1534
|
var link = (options = {}) => {
|
|
1541
1535
|
const extensions = [
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1536
|
+
ViewPlugin4.fromClass(class {
|
|
1537
|
+
constructor(view) {
|
|
1538
|
+
this.decorations = buildDecorations2(view, options);
|
|
1539
|
+
}
|
|
1540
|
+
update(update2) {
|
|
1541
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
1542
|
+
this.decorations = buildDecorations2(update2.view, options);
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
}, {
|
|
1546
|
+
decorations: (v) => v.decorations
|
|
1546
1547
|
})
|
|
1547
1548
|
];
|
|
1548
1549
|
if (options.onHover) {
|
|
@@ -1550,35 +1551,25 @@ var link = (options = {}) => {
|
|
|
1550
1551
|
// https://codemirror.net/examples/tooltip
|
|
1551
1552
|
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1552
1553
|
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1553
|
-
hoverTooltip2((view, pos) => {
|
|
1554
|
-
const
|
|
1555
|
-
|
|
1556
|
-
let
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
match = void 0;
|
|
1562
|
-
break;
|
|
1563
|
-
}
|
|
1564
|
-
idx = text.indexOf(match[0]);
|
|
1565
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
1566
|
-
break;
|
|
1567
|
-
}
|
|
1568
|
-
idx += match[0].length;
|
|
1569
|
-
} while (true);
|
|
1570
|
-
if (!match) {
|
|
1554
|
+
hoverTooltip2((view, pos, side) => {
|
|
1555
|
+
const syntax = syntaxTree2(view.state).resolveInner(pos, side);
|
|
1556
|
+
let link2 = null;
|
|
1557
|
+
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
1558
|
+
link2 = node.name === "Link" ? node : null;
|
|
1559
|
+
}
|
|
1560
|
+
const url = link2 && link2.getChild("URL");
|
|
1561
|
+
if (!url || !link2) {
|
|
1571
1562
|
return null;
|
|
1572
1563
|
}
|
|
1573
|
-
const
|
|
1564
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
1574
1565
|
return {
|
|
1575
|
-
pos:
|
|
1576
|
-
end:
|
|
1566
|
+
pos: link2.from,
|
|
1567
|
+
end: link2.to,
|
|
1577
1568
|
above: true,
|
|
1578
1569
|
create: () => {
|
|
1579
1570
|
const el = document.createElement("div");
|
|
1580
1571
|
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1581
|
-
options.onHover(el,
|
|
1572
|
+
options.onHover(el, urlText);
|
|
1582
1573
|
return {
|
|
1583
1574
|
dom: el,
|
|
1584
1575
|
offset: {
|
|
@@ -1593,15 +1584,14 @@ var link = (options = {}) => {
|
|
|
1593
1584
|
}
|
|
1594
1585
|
return extensions;
|
|
1595
1586
|
};
|
|
1596
|
-
var LinkButton = class extends
|
|
1597
|
-
constructor(
|
|
1587
|
+
var LinkButton = class extends WidgetType4 {
|
|
1588
|
+
constructor(_url, _onAttach) {
|
|
1598
1589
|
super();
|
|
1599
|
-
this._pos = _pos;
|
|
1600
1590
|
this._url = _url;
|
|
1601
1591
|
this._onAttach = _onAttach;
|
|
1602
1592
|
}
|
|
1603
1593
|
eq(other) {
|
|
1604
|
-
return this.
|
|
1594
|
+
return this._url === other._url;
|
|
1605
1595
|
}
|
|
1606
1596
|
toDOM(view) {
|
|
1607
1597
|
const el = document.createElement("span");
|
|
@@ -1609,7 +1599,7 @@ var LinkButton = class extends WidgetType5 {
|
|
|
1609
1599
|
return el;
|
|
1610
1600
|
}
|
|
1611
1601
|
};
|
|
1612
|
-
var LinkText = class extends
|
|
1602
|
+
var LinkText = class extends WidgetType4 {
|
|
1613
1603
|
constructor(_text, _url) {
|
|
1614
1604
|
super();
|
|
1615
1605
|
this._text = _text;
|
|
@@ -1638,46 +1628,51 @@ var LinkText = class extends WidgetType5 {
|
|
|
1638
1628
|
return link2;
|
|
1639
1629
|
}
|
|
1640
1630
|
};
|
|
1641
|
-
var
|
|
1642
|
-
const builder = new
|
|
1631
|
+
var buildDecorations2 = (view, options) => {
|
|
1632
|
+
const builder = new RangeSetBuilder();
|
|
1633
|
+
const { state } = view;
|
|
1643
1634
|
const cursor = state.selection.main.head;
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1635
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1636
|
+
syntaxTree2(state).iterate({
|
|
1637
|
+
enter: (node) => {
|
|
1638
|
+
if (node.name === "Link") {
|
|
1639
|
+
const marks2 = node.node.getChildren("LinkMark");
|
|
1640
|
+
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1641
|
+
const urlNode = node.node.getChild("URL");
|
|
1642
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1643
|
+
if (!url) {
|
|
1644
|
+
return false;
|
|
1645
|
+
}
|
|
1646
|
+
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1647
|
+
builder.add(node.from, node.to, Decoration6.replace({
|
|
1648
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
1649
|
+
}));
|
|
1650
|
+
}
|
|
1651
|
+
if (options.onRender) {
|
|
1652
|
+
builder.add(node.to, node.to, Decoration6.replace({
|
|
1653
|
+
widget: new LinkButton(url, options.onRender)
|
|
1654
|
+
}));
|
|
1655
|
+
}
|
|
1652
1656
|
return false;
|
|
1653
1657
|
}
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
if (options.onRender) {
|
|
1660
|
-
builder.add(node.to, node.to, Decoration6.replace({
|
|
1661
|
-
widget: new LinkButton(node.from, url, options.onRender)
|
|
1662
|
-
}));
|
|
1663
|
-
}
|
|
1664
|
-
return false;
|
|
1665
|
-
}
|
|
1666
|
-
}
|
|
1667
|
-
});
|
|
1658
|
+
},
|
|
1659
|
+
from,
|
|
1660
|
+
to
|
|
1661
|
+
});
|
|
1662
|
+
}
|
|
1668
1663
|
return builder.finish();
|
|
1669
1664
|
};
|
|
1670
1665
|
|
|
1671
1666
|
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1672
|
-
import { EditorView as
|
|
1667
|
+
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
1673
1668
|
var listener = ({ onFocus, onChange }) => {
|
|
1674
1669
|
const extensions = [];
|
|
1675
|
-
onFocus && extensions.push(
|
|
1670
|
+
onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
|
|
1676
1671
|
onFocus(focused);
|
|
1677
1672
|
return null;
|
|
1678
1673
|
}));
|
|
1679
|
-
onChange && extensions.push(
|
|
1680
|
-
onChange(
|
|
1674
|
+
onChange && extensions.push(EditorView8.updateListener.of((update2) => {
|
|
1675
|
+
onChange(update2.state.doc.toString());
|
|
1681
1676
|
}));
|
|
1682
1677
|
return extensions;
|
|
1683
1678
|
};
|
|
@@ -1685,27 +1680,25 @@ var listener = ({ onFocus, onChange }) => {
|
|
|
1685
1680
|
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1686
1681
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
1687
1682
|
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1688
|
-
import { markdownLanguage as
|
|
1683
|
+
import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
|
|
1689
1684
|
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
1690
1685
|
import { languages } from "@codemirror/language-data";
|
|
1691
1686
|
import { searchKeymap } from "@codemirror/search";
|
|
1692
1687
|
import { EditorState } from "@codemirror/state";
|
|
1693
1688
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
1694
|
-
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as
|
|
1689
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
1695
1690
|
|
|
1696
1691
|
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1697
|
-
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
1692
|
+
import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
|
|
1698
1693
|
import { HighlightStyle } from "@codemirror/language";
|
|
1699
1694
|
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
1700
1695
|
import { Table } from "@lezer/markdown";
|
|
1701
|
-
import get5 from "lodash.get";
|
|
1702
1696
|
var markdownTags = {
|
|
1703
1697
|
Blockquote: Tag.define(),
|
|
1704
1698
|
CodeMark: Tag.define(),
|
|
1705
1699
|
CodeText: Tag.define(),
|
|
1706
1700
|
EmphasisMark: Tag.define(),
|
|
1707
1701
|
HeaderMark: Tag.define(),
|
|
1708
|
-
// HorizontalRule: Tag.define(),
|
|
1709
1702
|
InlineCode: Tag.define(),
|
|
1710
1703
|
LinkLabel: Tag.define(),
|
|
1711
1704
|
LinkReference: Tag.define(),
|
|
@@ -1869,12 +1862,6 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1869
1862
|
],
|
|
1870
1863
|
class: blockquote
|
|
1871
1864
|
},
|
|
1872
|
-
// TODO(burdon): Replace with extension.
|
|
1873
|
-
// {
|
|
1874
|
-
// tag: [markdownTags.HorizontalRule],
|
|
1875
|
-
// class: mx(horizontalRule, readonly && '-indent-[100rem]'),
|
|
1876
|
-
// },
|
|
1877
|
-
// TODO(burdon): Only if being edited.
|
|
1878
1865
|
{
|
|
1879
1866
|
tag: [
|
|
1880
1867
|
markdownTags.TableCell
|
|
@@ -1882,9 +1869,9 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1882
1869
|
class: "font-mono"
|
|
1883
1870
|
}
|
|
1884
1871
|
], {
|
|
1885
|
-
scope:
|
|
1872
|
+
scope: markdownLanguage2,
|
|
1886
1873
|
all: {
|
|
1887
|
-
fontFamily:
|
|
1874
|
+
fontFamily: getToken("fontFamily.body", []).join(",")
|
|
1888
1875
|
}
|
|
1889
1876
|
});
|
|
1890
1877
|
};
|
|
@@ -1894,7 +1881,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1894
1881
|
return [
|
|
1895
1882
|
EditorState.allowMultipleSelections.of(true),
|
|
1896
1883
|
EditorState.tabSize.of(2),
|
|
1897
|
-
|
|
1884
|
+
EditorView9.lineWrapping,
|
|
1898
1885
|
customKeymap(),
|
|
1899
1886
|
bracketMatching2(),
|
|
1900
1887
|
closeBrackets2(),
|
|
@@ -1914,7 +1901,7 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1914
1901
|
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
1915
1902
|
// https://github.github.com/gfm
|
|
1916
1903
|
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
1917
|
-
base:
|
|
1904
|
+
base: markdownLanguage3,
|
|
1918
1905
|
// Languages for syntax highlighting fenced code blocks.
|
|
1919
1906
|
codeLanguages: languages,
|
|
1920
1907
|
// Parser extensions.
|
|
@@ -1965,24 +1952,24 @@ var mention = ({ onSearch }) => {
|
|
|
1965
1952
|
});
|
|
1966
1953
|
};
|
|
1967
1954
|
|
|
1968
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
1969
|
-
var
|
|
1955
|
+
// packages/ui/react-ui-editor/src/extensions/outliner.ts
|
|
1956
|
+
var outliner = (options = {}) => {
|
|
1970
1957
|
return [];
|
|
1971
1958
|
};
|
|
1972
1959
|
|
|
1973
1960
|
// packages/ui/react-ui-editor/src/extensions/table.ts
|
|
1974
1961
|
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1975
|
-
import { RangeSetBuilder as
|
|
1976
|
-
import { Decoration as Decoration7, EditorView as
|
|
1962
|
+
import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField4 } from "@codemirror/state";
|
|
1963
|
+
import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
1977
1964
|
var table = (options = {}) => {
|
|
1978
|
-
return
|
|
1979
|
-
create: (state) =>
|
|
1980
|
-
update: (_, tr) =>
|
|
1981
|
-
provide: (field) =>
|
|
1965
|
+
return StateField4.define({
|
|
1966
|
+
create: (state) => update(state, options),
|
|
1967
|
+
update: (_, tr) => update(tr.state, options),
|
|
1968
|
+
provide: (field) => EditorView10.decorations.from(field)
|
|
1982
1969
|
});
|
|
1983
1970
|
};
|
|
1984
|
-
var
|
|
1985
|
-
const builder = new
|
|
1971
|
+
var update = (state, options) => {
|
|
1972
|
+
const builder = new RangeSetBuilder2();
|
|
1986
1973
|
const cursor = state.selection.main.head;
|
|
1987
1974
|
const tables = [];
|
|
1988
1975
|
const getTable = () => tables[tables.length - 1];
|
|
@@ -2023,7 +2010,6 @@ var update3 = (state, options) => {
|
|
|
2023
2010
|
tables.forEach((table2) => {
|
|
2024
2011
|
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
2025
2012
|
hide && builder.add(table2.from, table2.to, Decoration7.replace({
|
|
2026
|
-
block: true,
|
|
2027
2013
|
widget: new TableWidget(table2)
|
|
2028
2014
|
}));
|
|
2029
2015
|
builder.add(table2.from, table2.to, Decoration7.mark({
|
|
@@ -2032,13 +2018,13 @@ var update3 = (state, options) => {
|
|
|
2032
2018
|
});
|
|
2033
2019
|
return builder.finish();
|
|
2034
2020
|
};
|
|
2035
|
-
var TableWidget = class extends
|
|
2021
|
+
var TableWidget = class extends WidgetType5 {
|
|
2036
2022
|
constructor(_table) {
|
|
2037
2023
|
super();
|
|
2038
2024
|
this._table = _table;
|
|
2039
2025
|
}
|
|
2040
2026
|
eq(other) {
|
|
2041
|
-
return this._table.
|
|
2027
|
+
return this._table.header?.join() === other._table.header?.join() && this._table.rows?.join() === other._table.rows?.join();
|
|
2042
2028
|
}
|
|
2043
2029
|
toDOM(view) {
|
|
2044
2030
|
const table2 = document.createElement("table");
|
|
@@ -2064,91 +2050,92 @@ var TableWidget = class extends WidgetType6 {
|
|
|
2064
2050
|
}
|
|
2065
2051
|
return table2;
|
|
2066
2052
|
}
|
|
2053
|
+
ignoreEvent(e) {
|
|
2054
|
+
return !/^mouse/.test(e.type);
|
|
2055
|
+
}
|
|
2067
2056
|
};
|
|
2068
2057
|
|
|
2069
2058
|
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
2070
|
-
import {
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
}
|
|
2076
|
-
});
|
|
2077
|
-
var CheckboxWidget = class extends WidgetType7 {
|
|
2078
|
-
constructor(_pos, _checked, _indent, _onCheck) {
|
|
2059
|
+
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
2060
|
+
import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
|
|
2061
|
+
import { EditorView as EditorView11, Decoration as Decoration8, WidgetType as WidgetType6, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
2062
|
+
var CheckboxWidget = class extends WidgetType6 {
|
|
2063
|
+
constructor(_checked) {
|
|
2079
2064
|
super();
|
|
2080
|
-
this._pos = _pos;
|
|
2081
2065
|
this._checked = _checked;
|
|
2082
|
-
this._indent = _indent;
|
|
2083
|
-
this._onCheck = _onCheck;
|
|
2084
2066
|
}
|
|
2085
2067
|
eq(other) {
|
|
2086
|
-
return this.
|
|
2068
|
+
return this._checked === other._checked;
|
|
2087
2069
|
}
|
|
2088
2070
|
toDOM(view) {
|
|
2089
|
-
const
|
|
2090
|
-
wrap.className = "cm-task-item";
|
|
2091
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
2092
|
-
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
2093
|
-
const input = wrap.appendChild(document.createElement("input"));
|
|
2071
|
+
const input = document.createElement("input");
|
|
2094
2072
|
input.type = "checkbox";
|
|
2095
2073
|
input.checked = this._checked;
|
|
2096
|
-
if (
|
|
2074
|
+
if (view.state.readOnly) {
|
|
2097
2075
|
input.setAttribute("disabled", "true");
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2076
|
+
} else {
|
|
2077
|
+
input.onmousedown = (event) => {
|
|
2078
|
+
const pos = view.posAtDOM(input);
|
|
2079
|
+
const text = view.state.sliceDoc(pos, pos + 3);
|
|
2080
|
+
if (text === (this._checked ? "[x]" : "[ ]")) {
|
|
2081
|
+
view.dispatch({
|
|
2082
|
+
changes: {
|
|
2083
|
+
from: pos + 1,
|
|
2084
|
+
to: pos + 2,
|
|
2085
|
+
insert: this._checked ? " " : "x"
|
|
2086
|
+
}
|
|
2087
|
+
});
|
|
2088
|
+
event.preventDefault();
|
|
2089
|
+
}
|
|
2103
2090
|
};
|
|
2104
2091
|
}
|
|
2105
|
-
return
|
|
2092
|
+
return input;
|
|
2106
2093
|
}
|
|
2107
2094
|
ignoreEvent() {
|
|
2108
2095
|
return false;
|
|
2109
2096
|
}
|
|
2110
2097
|
};
|
|
2098
|
+
var checkedDeco = Decoration8.replace({
|
|
2099
|
+
widget: new CheckboxWidget(true)
|
|
2100
|
+
});
|
|
2101
|
+
var uncheckedDeco = Decoration8.replace({
|
|
2102
|
+
widget: new CheckboxWidget(false)
|
|
2103
|
+
});
|
|
2111
2104
|
var tasklist = (options = {}) => {
|
|
2112
|
-
|
|
2113
|
-
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
2114
|
-
decoration: (match, view, pos) => {
|
|
2115
|
-
const indent = Math.floor(match[1].length / 2);
|
|
2116
|
-
const checked = match[2] === "x" || match[2] === "X";
|
|
2117
|
-
return Decoration8.replace({
|
|
2118
|
-
widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
2119
|
-
const idx = pos + match[0].indexOf("[") + 1;
|
|
2120
|
-
view.dispatch({
|
|
2121
|
-
changes: {
|
|
2122
|
-
from: idx,
|
|
2123
|
-
to: idx + 1,
|
|
2124
|
-
insert: checked2 ? "x" : " "
|
|
2125
|
-
},
|
|
2126
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
2127
|
-
selection: {
|
|
2128
|
-
anchor: idx + 3
|
|
2129
|
-
}
|
|
2130
|
-
});
|
|
2131
|
-
})
|
|
2132
|
-
});
|
|
2133
|
-
}
|
|
2134
|
-
});
|
|
2135
|
-
const tasks = ViewPlugin6.fromClass(class {
|
|
2105
|
+
return ViewPlugin5.fromClass(class {
|
|
2136
2106
|
constructor(view) {
|
|
2137
|
-
this.
|
|
2107
|
+
this.decorations = buildDecorations3(view);
|
|
2138
2108
|
}
|
|
2139
|
-
update(
|
|
2140
|
-
|
|
2109
|
+
update(update2) {
|
|
2110
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
2111
|
+
this.decorations = buildDecorations3(update2.view);
|
|
2112
|
+
}
|
|
2141
2113
|
}
|
|
2142
2114
|
}, {
|
|
2143
|
-
decorations: (
|
|
2144
|
-
|
|
2145
|
-
|
|
2115
|
+
decorations: (v) => v.decorations,
|
|
2116
|
+
// TODO(burdon): Is this still required?
|
|
2117
|
+
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2118
|
+
return view.plugin(plugin)?.decorations || Decoration8.none;
|
|
2146
2119
|
})
|
|
2147
2120
|
});
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2121
|
+
};
|
|
2122
|
+
var buildDecorations3 = (view) => {
|
|
2123
|
+
const builder = new RangeSetBuilder3();
|
|
2124
|
+
const { state } = view;
|
|
2125
|
+
const cursor = state.selection.main.head;
|
|
2126
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2127
|
+
syntaxTree4(state).iterate({
|
|
2128
|
+
enter: (node) => {
|
|
2129
|
+
if (node.name === "TaskMarker" && (cursor < node.from || cursor > node.to)) {
|
|
2130
|
+
const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
|
|
2131
|
+
builder.add(node.from, node.to, checked ? checkedDeco : uncheckedDeco);
|
|
2132
|
+
}
|
|
2133
|
+
},
|
|
2134
|
+
from,
|
|
2135
|
+
to
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
return builder.finish();
|
|
2152
2139
|
};
|
|
2153
2140
|
|
|
2154
2141
|
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
@@ -2225,12 +2212,12 @@ var cursorConverter2 = (text) => ({
|
|
|
2225
2212
|
|
|
2226
2213
|
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2227
2214
|
var yjs = (content, awareness2) => [
|
|
2228
|
-
|
|
2215
|
+
Cursor.converter.of(cursorConverter2(content)),
|
|
2229
2216
|
yCollab(content, awareness2)
|
|
2230
2217
|
];
|
|
2231
2218
|
|
|
2232
2219
|
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
2233
|
-
import
|
|
2220
|
+
import get3 from "lodash.get";
|
|
2234
2221
|
var defaultTheme = {
|
|
2235
2222
|
//
|
|
2236
2223
|
// Main layout:
|
|
@@ -2259,21 +2246,21 @@ var defaultTheme = {
|
|
|
2259
2246
|
"&.cm-focused": {
|
|
2260
2247
|
outline: "none"
|
|
2261
2248
|
},
|
|
2262
|
-
"
|
|
2249
|
+
".cm-scroller": {
|
|
2263
2250
|
overflow: "visible",
|
|
2264
|
-
fontFamily:
|
|
2251
|
+
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
2265
2252
|
},
|
|
2266
|
-
"
|
|
2253
|
+
".cm-content": {
|
|
2267
2254
|
padding: 0,
|
|
2268
2255
|
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
2269
2256
|
fontSize: "16px"
|
|
2270
2257
|
},
|
|
2271
2258
|
"&light .cm-content": {
|
|
2272
|
-
color:
|
|
2259
|
+
color: get3(tokens, "extend.colors.neutral.900", "black"),
|
|
2273
2260
|
caretColor: "black"
|
|
2274
2261
|
},
|
|
2275
2262
|
"&dark .cm-content": {
|
|
2276
|
-
color:
|
|
2263
|
+
color: get3(tokens, "extend.colors.neutral.100", "white"),
|
|
2277
2264
|
caretColor: "white"
|
|
2278
2265
|
},
|
|
2279
2266
|
//
|
|
@@ -2285,158 +2272,120 @@ var defaultTheme = {
|
|
|
2285
2272
|
"&dark .cm-cursor, &dark .cm-dropCursor": {
|
|
2286
2273
|
borderLeft: "2px solid white"
|
|
2287
2274
|
},
|
|
2288
|
-
"
|
|
2275
|
+
".cm-placeholder": {
|
|
2289
2276
|
fontWeight: 100
|
|
2290
2277
|
},
|
|
2291
2278
|
//
|
|
2292
2279
|
// line
|
|
2293
2280
|
//
|
|
2294
|
-
"
|
|
2281
|
+
".cm-line": {
|
|
2295
2282
|
paddingInline: 0
|
|
2296
2283
|
},
|
|
2297
|
-
"
|
|
2284
|
+
".cm-activeLine": {
|
|
2298
2285
|
background: "inherit"
|
|
2299
2286
|
},
|
|
2300
2287
|
//
|
|
2301
2288
|
// Selection
|
|
2302
2289
|
//
|
|
2303
2290
|
"&light .cm-selectionBackground": {
|
|
2304
|
-
background:
|
|
2291
|
+
background: get3(tokens, "extend.colors.primary.100")
|
|
2305
2292
|
},
|
|
2306
2293
|
"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2307
|
-
background:
|
|
2294
|
+
background: get3(tokens, "extend.colors.primary.200")
|
|
2308
2295
|
},
|
|
2309
2296
|
"&dark .cm-selectionBackground": {
|
|
2310
|
-
background:
|
|
2297
|
+
background: get3(tokens, "extend.colors.primary.700")
|
|
2311
2298
|
},
|
|
2312
2299
|
"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2313
|
-
background:
|
|
2300
|
+
background: get3(tokens, "extend.colors.primary.600")
|
|
2314
2301
|
},
|
|
2315
2302
|
//
|
|
2316
2303
|
// Search
|
|
2317
2304
|
//
|
|
2318
2305
|
"&light .cm-searchMatch": {
|
|
2319
|
-
backgroundColor:
|
|
2306
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.100")
|
|
2320
2307
|
},
|
|
2321
2308
|
"&dark .cm-searchMatch": {
|
|
2322
|
-
backgroundColor:
|
|
2323
|
-
},
|
|
2324
|
-
//
|
|
2325
|
-
// collaboration
|
|
2326
|
-
// TODO(burdon): Review classnames (YJS dependent?) Note: e2e tests depend on this class.
|
|
2327
|
-
//
|
|
2328
|
-
"&light .cm-ySelection, &light .cm-yLineSelection": {
|
|
2329
|
-
mixBlendMode: "multiply"
|
|
2330
|
-
},
|
|
2331
|
-
"&dark .cm-ySelection, &dark .cm-yLineSelection": {
|
|
2332
|
-
mixBlendMode: "screen"
|
|
2333
|
-
},
|
|
2334
|
-
"& .cm-ySelectionInfo": {
|
|
2335
|
-
padding: "2px 4px",
|
|
2336
|
-
marginBlockStart: "-4px"
|
|
2337
|
-
},
|
|
2338
|
-
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
2339
|
-
paddingBlockStart: ".15em",
|
|
2340
|
-
paddingBlockEnd: ".15em"
|
|
2341
|
-
},
|
|
2342
|
-
"& .cm-ySelectionCaret": {
|
|
2343
|
-
display: "inline-block",
|
|
2344
|
-
insetBlockStart: ".1em",
|
|
2345
|
-
blockSize: "1.4em",
|
|
2346
|
-
verticalAlign: "top"
|
|
2347
|
-
},
|
|
2348
|
-
"& .cm-yLineSelection": {
|
|
2349
|
-
margin: 0
|
|
2309
|
+
backgroundColor: get3(tokens, "extend.colors.yellow.700")
|
|
2350
2310
|
},
|
|
2351
2311
|
//
|
|
2352
2312
|
// link
|
|
2353
2313
|
//
|
|
2354
|
-
"
|
|
2355
|
-
color:
|
|
2314
|
+
".cm-link": {
|
|
2315
|
+
color: get3(tokens, "extend.colors.primary.500"),
|
|
2356
2316
|
textDecorationLine: "underline",
|
|
2357
2317
|
textDecorationThickness: "1px",
|
|
2358
2318
|
textUnderlineOffset: "2px",
|
|
2359
2319
|
borderRadius: ".125rem",
|
|
2360
|
-
fontFamily:
|
|
2320
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2361
2321
|
},
|
|
2362
2322
|
//
|
|
2363
2323
|
// tooltip
|
|
2364
2324
|
//
|
|
2365
|
-
"
|
|
2325
|
+
".cm-tooltip": {
|
|
2366
2326
|
border: "none"
|
|
2367
2327
|
},
|
|
2368
|
-
"
|
|
2328
|
+
".cm-tooltip-below": {},
|
|
2369
2329
|
//
|
|
2370
2330
|
// autocomplete
|
|
2371
2331
|
//
|
|
2372
|
-
"
|
|
2332
|
+
".cm-tooltip-autocomplete": {
|
|
2373
2333
|
marginTop: "4px",
|
|
2374
2334
|
marginLeft: "-3px"
|
|
2375
2335
|
},
|
|
2376
|
-
"
|
|
2377
|
-
"
|
|
2378
|
-
"
|
|
2336
|
+
".cm-tooltip-autocomplete ul li": {},
|
|
2337
|
+
".cm-tooltip-autocomplete ul li[aria-selected]": {},
|
|
2338
|
+
".cm-completionIcon": {
|
|
2379
2339
|
display: "none"
|
|
2380
2340
|
},
|
|
2381
|
-
"
|
|
2382
|
-
fontFamily:
|
|
2341
|
+
".cm-completionLabel": {
|
|
2342
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2383
2343
|
},
|
|
2384
|
-
"
|
|
2344
|
+
".cm-completionMatchedText": {
|
|
2385
2345
|
textDecoration: "none"
|
|
2386
2346
|
},
|
|
2387
2347
|
//
|
|
2388
|
-
// widgets
|
|
2389
|
-
//
|
|
2390
|
-
"& .cm-widgetBuffer": {
|
|
2391
|
-
display: "none",
|
|
2392
|
-
height: 0
|
|
2393
|
-
},
|
|
2394
|
-
//
|
|
2395
2348
|
// table
|
|
2396
2349
|
//
|
|
2397
|
-
"
|
|
2398
|
-
fontFamily: `${
|
|
2350
|
+
".cm-table *": {
|
|
2351
|
+
fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
|
|
2399
2352
|
textDecoration: "none !important"
|
|
2400
2353
|
},
|
|
2401
|
-
"
|
|
2354
|
+
".cm-table-head": {
|
|
2402
2355
|
padding: "2px 16px 2px 0px",
|
|
2403
|
-
borderBottom: `1px solid ${
|
|
2356
|
+
borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
|
|
2404
2357
|
fontWeight: 100,
|
|
2405
2358
|
textAlign: "left",
|
|
2406
|
-
color:
|
|
2359
|
+
color: get3(tokens, "extend.colors.neutral.500")
|
|
2407
2360
|
},
|
|
2408
|
-
"
|
|
2361
|
+
".cm-table-cell": {
|
|
2409
2362
|
padding: "2px 16px 2px 0px"
|
|
2410
2363
|
},
|
|
2411
2364
|
//
|
|
2412
2365
|
// image
|
|
2413
2366
|
//
|
|
2414
|
-
"
|
|
2415
|
-
|
|
2367
|
+
".cm-image": {
|
|
2368
|
+
display: "block",
|
|
2369
|
+
height: "0"
|
|
2370
|
+
},
|
|
2371
|
+
".cm-image.cm-loaded-image": {
|
|
2372
|
+
height: "auto",
|
|
2373
|
+
borderTop: "0.5rem solid transparent",
|
|
2374
|
+
borderBottom: "0.5rem solid transparent"
|
|
2416
2375
|
},
|
|
2417
2376
|
//
|
|
2418
2377
|
// font size
|
|
2419
|
-
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2378
|
+
// TODO(thure): This appears to be the best or only way to set selection caret heights,
|
|
2379
|
+
// but it's far more verbose than it needs to be.
|
|
2420
2380
|
//
|
|
2421
|
-
...Object.keys(
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
2430
|
-
height
|
|
2431
|
-
};
|
|
2432
|
-
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
2433
|
-
height
|
|
2434
|
-
};
|
|
2435
|
-
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
2436
|
-
height
|
|
2437
|
-
};
|
|
2438
|
-
return acc;
|
|
2439
|
-
}, {}),
|
|
2381
|
+
// ...Object.keys(get(tokens, 'extend.fontSize', {})).reduce((acc: Record<string, any>, fontSize) => {
|
|
2382
|
+
// const height = get(tokens, ['extend', 'fontSize', fontSize, 1, 'lineHeight']);
|
|
2383
|
+
//
|
|
2384
|
+
// acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = { height };
|
|
2385
|
+
// acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = { height };
|
|
2386
|
+
// acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = { height };
|
|
2387
|
+
// return acc;
|
|
2388
|
+
// }, {}),
|
|
2440
2389
|
/**
|
|
2441
2390
|
* Panels
|
|
2442
2391
|
* TODO(burdon): Needs styling attention (esp. dark mode).
|
|
@@ -2453,51 +2402,53 @@ var defaultTheme = {
|
|
|
2453
2402
|
* </div>
|
|
2454
2403
|
* </div
|
|
2455
2404
|
*/
|
|
2456
|
-
"
|
|
2457
|
-
border: `1px solid ${
|
|
2405
|
+
".cm-panels": {
|
|
2406
|
+
border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
|
|
2458
2407
|
},
|
|
2459
|
-
"
|
|
2460
|
-
background:
|
|
2461
|
-
fontFamily:
|
|
2408
|
+
".cm-panel": {
|
|
2409
|
+
background: get3(tokens, "extend.colors.neutral.50"),
|
|
2410
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2462
2411
|
},
|
|
2463
|
-
"
|
|
2412
|
+
".cm-button": {
|
|
2464
2413
|
margin: "4px",
|
|
2465
|
-
fontFamily:
|
|
2466
|
-
background:
|
|
2414
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(","),
|
|
2415
|
+
background: get3(tokens, "extend.colors.neutral.100"),
|
|
2467
2416
|
backgroundImage: "none",
|
|
2468
2417
|
border: "none",
|
|
2469
2418
|
"&:hover": {
|
|
2470
|
-
background:
|
|
2419
|
+
background: get3(tokens, "extend.colors.neutral.200")
|
|
2471
2420
|
},
|
|
2472
2421
|
"&:active": {
|
|
2473
|
-
background:
|
|
2422
|
+
background: get3(tokens, "extend.colors.neutral.300"),
|
|
2474
2423
|
backgroundImage: "none"
|
|
2475
2424
|
}
|
|
2476
2425
|
},
|
|
2477
|
-
"
|
|
2426
|
+
".cm-panel input[type=checkbox]": {
|
|
2478
2427
|
marginRight: "0.4rem !important"
|
|
2479
2428
|
}
|
|
2480
2429
|
};
|
|
2481
2430
|
var textTheme = {
|
|
2482
|
-
"
|
|
2483
|
-
fontFamily:
|
|
2431
|
+
".cm-scroller": {
|
|
2432
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2484
2433
|
},
|
|
2485
|
-
"
|
|
2486
|
-
fontFamily:
|
|
2434
|
+
".cm-placeholder": {
|
|
2435
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2487
2436
|
}
|
|
2488
2437
|
};
|
|
2489
2438
|
var markdownTheme = {
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2439
|
+
".cm-scroller": {
|
|
2440
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2441
|
+
},
|
|
2442
|
+
".cm-placeholder": {
|
|
2443
|
+
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
2493
2444
|
}
|
|
2494
2445
|
};
|
|
2495
2446
|
var codeTheme = {
|
|
2496
|
-
"
|
|
2497
|
-
fontFamily:
|
|
2447
|
+
".cm-scroller": {
|
|
2448
|
+
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
2498
2449
|
},
|
|
2499
|
-
"
|
|
2500
|
-
fontFamily:
|
|
2450
|
+
".cm-placeholder": {
|
|
2451
|
+
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
2501
2452
|
}
|
|
2502
2453
|
};
|
|
2503
2454
|
|
|
@@ -2506,23 +2457,20 @@ var EditorModes = [
|
|
|
2506
2457
|
"default",
|
|
2507
2458
|
"vim"
|
|
2508
2459
|
];
|
|
2509
|
-
var
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
root,
|
|
2518
|
-
state,
|
|
2519
|
-
view
|
|
2520
|
-
}), [
|
|
2460
|
+
var useComments = (view, comments2) => {
|
|
2461
|
+
useEffect(() => {
|
|
2462
|
+
if (view && comments2 !== void 0) {
|
|
2463
|
+
view.dispatch({
|
|
2464
|
+
effects: setComments.of(comments2)
|
|
2465
|
+
});
|
|
2466
|
+
}
|
|
2467
|
+
}, [
|
|
2521
2468
|
view,
|
|
2522
|
-
|
|
2523
|
-
root
|
|
2469
|
+
comments2
|
|
2524
2470
|
]);
|
|
2525
|
-
|
|
2471
|
+
};
|
|
2472
|
+
var useAwareness = ({ awareness: awareness2, peer }) => {
|
|
2473
|
+
const { themeMode } = useThemeContext();
|
|
2526
2474
|
useEffect(() => {
|
|
2527
2475
|
if (awareness2 && peer) {
|
|
2528
2476
|
awareness2.setLocalStateField("user", {
|
|
@@ -2543,54 +2491,70 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, comments: co
|
|
|
2543
2491
|
peer,
|
|
2544
2492
|
themeMode
|
|
2545
2493
|
]);
|
|
2494
|
+
};
|
|
2495
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, autofocus, readonly, selection, comments: comments2, extensions = [], editorMode, theme, slots = defaultSlots }, forwardedRef) => {
|
|
2496
|
+
const tabsterDOMAttribute = useFocusableGroup({
|
|
2497
|
+
tabBehavior: "limited"
|
|
2498
|
+
});
|
|
2499
|
+
const { themeMode } = useThemeContext();
|
|
2500
|
+
const rootRef = useRef(null);
|
|
2501
|
+
const [view, setView] = useState(null);
|
|
2502
|
+
useImperativeHandle(forwardedRef, () => view, [
|
|
2503
|
+
view
|
|
2504
|
+
]);
|
|
2546
2505
|
useEffect(() => {
|
|
2547
|
-
if (
|
|
2548
|
-
view
|
|
2549
|
-
effects: setCommentRange.of({
|
|
2550
|
-
model,
|
|
2551
|
-
comments: comments2
|
|
2552
|
-
})
|
|
2553
|
-
});
|
|
2506
|
+
if (autofocus) {
|
|
2507
|
+
view?.focus();
|
|
2554
2508
|
}
|
|
2555
2509
|
}, [
|
|
2556
2510
|
view,
|
|
2557
|
-
|
|
2511
|
+
autofocus
|
|
2558
2512
|
]);
|
|
2513
|
+
useAwareness(model);
|
|
2514
|
+
useComments(view, comments2);
|
|
2559
2515
|
useEffect(() => {
|
|
2560
|
-
if (!
|
|
2516
|
+
if (!model || !rootRef.current) {
|
|
2561
2517
|
return;
|
|
2562
2518
|
}
|
|
2563
|
-
const
|
|
2519
|
+
const state = EditorState2.create({
|
|
2564
2520
|
doc: model.text(),
|
|
2521
|
+
// TODO(burdon): Composer should store and set selection when switching documents.
|
|
2522
|
+
selection,
|
|
2565
2523
|
extensions: [
|
|
2566
2524
|
readonly && EditorState2.readOnly.of(readonly),
|
|
2567
|
-
// TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
|
|
2568
|
-
editorMode === "vim" && vim(),
|
|
2569
2525
|
// Theme.
|
|
2570
2526
|
// TODO(burdon): Make theme configurable.
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
//
|
|
2527
|
+
EditorView12.baseTheme(defaultTheme),
|
|
2528
|
+
EditorView12.theme(theme ?? {}),
|
|
2529
|
+
EditorView12.darkTheme.of(themeMode === "dark"),
|
|
2530
|
+
// TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
|
|
2531
|
+
editorMode === "vim" && vim(),
|
|
2532
|
+
// Storage and replication (NOTE: must come before other extensions).
|
|
2575
2533
|
model.extension,
|
|
2576
2534
|
// Custom.
|
|
2577
2535
|
...extensions
|
|
2578
2536
|
].filter(Boolean)
|
|
2579
2537
|
});
|
|
2580
2538
|
view?.destroy();
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2539
|
+
const newView = new EditorView12({
|
|
2540
|
+
parent: rootRef.current,
|
|
2541
|
+
state,
|
|
2542
|
+
// NOTE: Uncomment to spy on all transactions.
|
|
2543
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
2544
|
+
dispatch: (transaction, view2) => {
|
|
2545
|
+
view2.update([
|
|
2546
|
+
transaction
|
|
2547
|
+
]);
|
|
2548
|
+
view2.state.facet(syncFacet)?.reconcile(view2);
|
|
2549
|
+
}
|
|
2587
2550
|
});
|
|
2551
|
+
setView(newView);
|
|
2588
2552
|
return () => {
|
|
2589
|
-
|
|
2590
|
-
|
|
2553
|
+
newView?.destroy();
|
|
2554
|
+
setView(null);
|
|
2591
2555
|
};
|
|
2592
2556
|
}, [
|
|
2593
|
-
|
|
2557
|
+
rootRef,
|
|
2594
2558
|
model,
|
|
2595
2559
|
readonly,
|
|
2596
2560
|
editorMode,
|
|
@@ -2599,12 +2563,10 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, comments: co
|
|
|
2599
2563
|
const handleKeyUp = useCallback((event) => {
|
|
2600
2564
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
2601
2565
|
switch (key) {
|
|
2602
|
-
case "Enter": {
|
|
2603
|
-
view?.contentDOM.focus();
|
|
2604
|
-
break;
|
|
2605
|
-
}
|
|
2606
2566
|
case "Escape": {
|
|
2607
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)
|
|
2567
|
+
if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
2568
|
+
rootRef.current?.focus();
|
|
2569
|
+
}
|
|
2608
2570
|
break;
|
|
2609
2571
|
}
|
|
2610
2572
|
}
|
|
@@ -2614,16 +2576,17 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, comments: co
|
|
|
2614
2576
|
]);
|
|
2615
2577
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2616
2578
|
key: model.id,
|
|
2617
|
-
|
|
2579
|
+
role: "none",
|
|
2618
2580
|
tabIndex: 0,
|
|
2619
|
-
|
|
2581
|
+
onKeyUp: handleKeyUp,
|
|
2582
|
+
...slots.root,
|
|
2620
2583
|
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
2621
|
-
|
|
2584
|
+
ref: rootRef
|
|
2622
2585
|
});
|
|
2623
2586
|
});
|
|
2624
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [],
|
|
2587
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = textTheme, slots, ...props }, forwardedRef) => {
|
|
2625
2588
|
const { themeMode } = useThemeContext();
|
|
2626
|
-
const
|
|
2589
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
|
|
2627
2590
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2628
2591
|
ref: forwardedRef,
|
|
2629
2592
|
readonly,
|
|
@@ -2631,17 +2594,18 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots:
|
|
|
2631
2594
|
basicBundle({
|
|
2632
2595
|
readonly,
|
|
2633
2596
|
themeMode,
|
|
2634
|
-
placeholder:
|
|
2597
|
+
placeholder: placeholder3
|
|
2635
2598
|
}),
|
|
2636
2599
|
...extensions
|
|
2637
2600
|
],
|
|
2638
|
-
|
|
2601
|
+
theme,
|
|
2602
|
+
slots: updatedSlots,
|
|
2639
2603
|
...props
|
|
2640
2604
|
});
|
|
2641
2605
|
});
|
|
2642
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [],
|
|
2606
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, extensions = [], theme = markdownTheme, slots, ...props }, forwardedRef) => {
|
|
2643
2607
|
const { themeMode } = useThemeContext();
|
|
2644
|
-
const
|
|
2608
|
+
const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
|
|
2645
2609
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2646
2610
|
ref: forwardedRef,
|
|
2647
2611
|
readonly,
|
|
@@ -2649,34 +2613,29 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], sl
|
|
|
2649
2613
|
markdownBundle({
|
|
2650
2614
|
readonly,
|
|
2651
2615
|
themeMode,
|
|
2652
|
-
placeholder:
|
|
2616
|
+
placeholder: placeholder3
|
|
2653
2617
|
}),
|
|
2654
2618
|
...extensions
|
|
2655
2619
|
],
|
|
2656
|
-
|
|
2620
|
+
theme,
|
|
2621
|
+
slots: updatedSlots,
|
|
2657
2622
|
...props
|
|
2658
2623
|
});
|
|
2659
2624
|
});
|
|
2660
2625
|
var defaultSlots = {
|
|
2661
2626
|
root: {
|
|
2662
|
-
className: mx3("p-2", inputSurface)
|
|
2627
|
+
className: mx3("w-full p-2 overflow-y-auto", inputSurface)
|
|
2663
2628
|
}
|
|
2664
2629
|
};
|
|
2665
2630
|
var defaultTextSlots = {
|
|
2666
|
-
...defaultSlots
|
|
2667
|
-
editor: {
|
|
2668
|
-
theme: textTheme
|
|
2669
|
-
}
|
|
2631
|
+
...defaultSlots
|
|
2670
2632
|
};
|
|
2671
2633
|
var defaultMarkdownSlots = {
|
|
2672
|
-
...defaultSlots
|
|
2673
|
-
editor: {
|
|
2674
|
-
theme: markdownTheme
|
|
2675
|
-
}
|
|
2634
|
+
...defaultSlots
|
|
2676
2635
|
};
|
|
2677
2636
|
|
|
2678
2637
|
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2679
|
-
import
|
|
2638
|
+
import get4 from "lodash.get";
|
|
2680
2639
|
import { getRawDoc } from "@dxos/echo-schema";
|
|
2681
2640
|
import { isNotNullOrUndefined } from "@dxos/util";
|
|
2682
2641
|
|
|
@@ -2684,7 +2643,7 @@ import { isNotNullOrUndefined } from "@dxos/util";
|
|
|
2684
2643
|
import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
|
|
2685
2644
|
import { Context as Context2 } from "@dxos/context";
|
|
2686
2645
|
import { invariant as invariant3 } from "@dxos/invariant";
|
|
2687
|
-
import { log as
|
|
2646
|
+
import { log as log2 } from "@dxos/log";
|
|
2688
2647
|
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
|
|
2689
2648
|
var DEBOUNCE_INTERVAL = 100;
|
|
2690
2649
|
var SpaceAwarenessProvider = class {
|
|
@@ -2723,7 +2682,7 @@ var SpaceAwarenessProvider = class {
|
|
|
2723
2682
|
void this._space.postMessage(this._channel, {
|
|
2724
2683
|
kind: "query"
|
|
2725
2684
|
}).catch((err) => {
|
|
2726
|
-
|
|
2685
|
+
log2.debug("failed to query awareness", {
|
|
2727
2686
|
err
|
|
2728
2687
|
}, {
|
|
2729
2688
|
F: __dxlog_file4,
|
|
@@ -2785,25 +2744,63 @@ var SpaceAwarenessProvider = class {
|
|
|
2785
2744
|
}
|
|
2786
2745
|
};
|
|
2787
2746
|
|
|
2788
|
-
// packages/ui/react-ui-editor/src/hooks/
|
|
2789
|
-
import { StateField as
|
|
2790
|
-
|
|
2791
|
-
|
|
2747
|
+
// packages/ui/react-ui-editor/src/hooks/defs.ts
|
|
2748
|
+
import { StateField as StateField5 } from "@codemirror/state";
|
|
2749
|
+
var modelState = StateField5.define({
|
|
2750
|
+
create: () => void 0,
|
|
2751
|
+
update: (model) => model
|
|
2752
|
+
});
|
|
2753
|
+
|
|
2754
|
+
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2755
|
+
var createAutomergeModel = ({ space, identity, text }) => {
|
|
2756
|
+
const obj = text;
|
|
2757
|
+
const doc = getRawDoc(obj, [
|
|
2758
|
+
obj.field
|
|
2759
|
+
]);
|
|
2760
|
+
const awarenessProvider = space && new SpaceAwarenessProvider({
|
|
2761
|
+
space,
|
|
2762
|
+
channel: `automerge.awareness.${obj.id}`,
|
|
2763
|
+
info: {
|
|
2764
|
+
displayName: identity?.profile?.displayName ?? "",
|
|
2765
|
+
color: cursorColor.color,
|
|
2766
|
+
lightColor: cursorColor.light
|
|
2767
|
+
},
|
|
2768
|
+
peerId: identity?.identityKey.toHex() ?? "Anonymous"
|
|
2769
|
+
});
|
|
2770
|
+
const model = {
|
|
2771
|
+
id: obj.id,
|
|
2772
|
+
content: doc,
|
|
2773
|
+
text: () => get4(doc.handle.docSync(), doc.path),
|
|
2774
|
+
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
2775
|
+
extension: [
|
|
2776
|
+
modelState.init(() => model),
|
|
2777
|
+
automerge3({
|
|
2778
|
+
handle: doc.handle,
|
|
2779
|
+
path: doc.path
|
|
2780
|
+
}),
|
|
2781
|
+
awarenessProvider && AwarenessProvider.of(awarenessProvider),
|
|
2782
|
+
awareness()
|
|
2783
|
+
].filter(isNotNullOrUndefined),
|
|
2784
|
+
peer: identity ? {
|
|
2785
|
+
id: identity.identityKey.toHex(),
|
|
2786
|
+
name: identity.profile?.displayName
|
|
2787
|
+
} : void 0
|
|
2788
|
+
};
|
|
2789
|
+
return model;
|
|
2790
|
+
};
|
|
2792
2791
|
|
|
2793
2792
|
// packages/ui/react-ui-editor/src/hooks/yjs.ts
|
|
2794
2793
|
import * as decoding from "lib0/decoding";
|
|
2795
2794
|
import * as encoding from "lib0/encoding";
|
|
2796
2795
|
import { Observable } from "lib0/observable";
|
|
2797
2796
|
import * as YP from "y-protocols/awareness";
|
|
2798
|
-
import * as Y2 from "yjs";
|
|
2799
2797
|
import { invariant as invariant4 } from "@dxos/invariant";
|
|
2800
|
-
import { log as
|
|
2801
|
-
import { arrayToString as arrayToString2, stringToArray as stringToArray2 } from "@dxos/util";
|
|
2798
|
+
import { log as log3 } from "@dxos/log";
|
|
2802
2799
|
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
|
|
2803
2800
|
var createYjsModel = ({ identity, space, text }) => {
|
|
2804
2801
|
invariant4(text?.doc && text?.content, void 0, {
|
|
2805
2802
|
F: __dxlog_file5,
|
|
2806
|
-
L:
|
|
2803
|
+
L: 22,
|
|
2807
2804
|
S: void 0,
|
|
2808
2805
|
A: [
|
|
2809
2806
|
"text?.doc && text?.content",
|
|
@@ -2819,34 +2816,6 @@ var createYjsModel = ({ identity, space, text }) => {
|
|
|
2819
2816
|
id: text.doc.guid,
|
|
2820
2817
|
content: text.content,
|
|
2821
2818
|
text: () => text.content.toString(),
|
|
2822
|
-
// https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
|
|
2823
|
-
// TODO(dmaretskyi): Refactor as cursor-converter.
|
|
2824
|
-
getCursorFromRange: (range) => {
|
|
2825
|
-
const from = Y2.encodeRelativePosition(Y2.createRelativePositionFromTypeIndex(text.content, range.from));
|
|
2826
|
-
const to = Y2.encodeRelativePosition(Y2.createRelativePositionFromTypeIndex(text.content, range.to, -1));
|
|
2827
|
-
return [
|
|
2828
|
-
arrayToString2(from),
|
|
2829
|
-
arrayToString2(to)
|
|
2830
|
-
].join(":");
|
|
2831
|
-
},
|
|
2832
|
-
getRangeFromCursor: (cursor) => {
|
|
2833
|
-
invariant4(text.doc, void 0, {
|
|
2834
|
-
F: __dxlog_file5,
|
|
2835
|
-
L: 40,
|
|
2836
|
-
S: void 0,
|
|
2837
|
-
A: [
|
|
2838
|
-
"text.doc",
|
|
2839
|
-
""
|
|
2840
|
-
]
|
|
2841
|
-
});
|
|
2842
|
-
const parts = cursor.split(":");
|
|
2843
|
-
const from = Y2.createAbsolutePositionFromRelativePosition(Y2.decodeRelativePosition(stringToArray2(parts[0])), text.doc);
|
|
2844
|
-
const to = Y2.createAbsolutePositionFromRelativePosition(Y2.decodeRelativePosition(stringToArray2(parts[1])), text.doc);
|
|
2845
|
-
return from && to ? {
|
|
2846
|
-
from: from.index,
|
|
2847
|
-
to: to.index
|
|
2848
|
-
} : void 0;
|
|
2849
|
-
},
|
|
2850
2819
|
extension: [
|
|
2851
2820
|
modelState.init(() => model),
|
|
2852
2821
|
yjs(text.content, provider?.awareness)
|
|
@@ -2889,14 +2858,14 @@ var YAwarenessProvider = class extends Observable {
|
|
|
2889
2858
|
return this._awareness;
|
|
2890
2859
|
}
|
|
2891
2860
|
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
2892
|
-
|
|
2861
|
+
log3("awareness update", {
|
|
2893
2862
|
added,
|
|
2894
2863
|
updated,
|
|
2895
2864
|
removed,
|
|
2896
2865
|
origin
|
|
2897
2866
|
}, {
|
|
2898
2867
|
F: __dxlog_file5,
|
|
2899
|
-
L:
|
|
2868
|
+
L: 101,
|
|
2900
2869
|
S: this,
|
|
2901
2870
|
C: (f, a) => f(...a)
|
|
2902
2871
|
});
|
|
@@ -2907,9 +2876,9 @@ var YAwarenessProvider = class extends Observable {
|
|
|
2907
2876
|
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
2908
2877
|
}
|
|
2909
2878
|
_handleSpaceMessage({ payload }) {
|
|
2910
|
-
|
|
2879
|
+
log3("space message", payload, {
|
|
2911
2880
|
F: __dxlog_file5,
|
|
2912
|
-
L:
|
|
2881
|
+
L: 110,
|
|
2913
2882
|
S: this,
|
|
2914
2883
|
C: (f, a) => f(...a)
|
|
2915
2884
|
});
|
|
@@ -2953,13 +2922,11 @@ var YAwarenessProvider = class extends Observable {
|
|
|
2953
2922
|
};
|
|
2954
2923
|
|
|
2955
2924
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
update: (model) => model
|
|
2959
|
-
});
|
|
2925
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
2926
|
+
import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
2960
2927
|
var useTextModel = (props) => {
|
|
2961
2928
|
const { identity, space, text } = props;
|
|
2962
|
-
const [model, setModel] = useState2(
|
|
2929
|
+
const [model, setModel] = useState2();
|
|
2963
2930
|
useEffect2(() => setModel(createModel(props)), [
|
|
2964
2931
|
identity,
|
|
2965
2932
|
space,
|
|
@@ -2977,47 +2944,10 @@ var createModel = (props) => {
|
|
|
2977
2944
|
return void 0;
|
|
2978
2945
|
}
|
|
2979
2946
|
};
|
|
2980
|
-
|
|
2981
|
-
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2982
|
-
var createAutomergeModel = ({ space, identity, text }) => {
|
|
2983
|
-
const obj = text;
|
|
2984
|
-
const doc = getRawDoc(obj, [
|
|
2985
|
-
obj.field
|
|
2986
|
-
]);
|
|
2987
|
-
const awarenessProvider = space && new SpaceAwarenessProvider({
|
|
2988
|
-
space,
|
|
2989
|
-
channel: `automerge.awareness.${obj.id}`,
|
|
2990
|
-
info: {
|
|
2991
|
-
displayName: identity?.profile?.displayName ?? "",
|
|
2992
|
-
color: cursorColor.color,
|
|
2993
|
-
lightColor: cursorColor.light
|
|
2994
|
-
},
|
|
2995
|
-
peerId: identity?.identityKey.toHex() ?? "Anonymous"
|
|
2996
|
-
});
|
|
2997
|
-
const model = {
|
|
2998
|
-
id: obj.id,
|
|
2999
|
-
content: doc,
|
|
3000
|
-
text: () => get7(doc.handle.docSync(), doc.path),
|
|
3001
|
-
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
3002
|
-
extension: [
|
|
3003
|
-
modelState.init(() => model),
|
|
3004
|
-
automerge3({
|
|
3005
|
-
handle: doc.handle,
|
|
3006
|
-
path: doc.path
|
|
3007
|
-
}),
|
|
3008
|
-
awarenessProvider && AwarenessProvider.of(awarenessProvider),
|
|
3009
|
-
awareness()
|
|
3010
|
-
].filter(isNotNullOrUndefined),
|
|
3011
|
-
peer: identity ? {
|
|
3012
|
-
id: identity.identityKey.toHex(),
|
|
3013
|
-
name: identity.profile?.displayName
|
|
3014
|
-
} : void 0
|
|
3015
|
-
};
|
|
3016
|
-
return model;
|
|
3017
|
-
};
|
|
3018
2947
|
export {
|
|
3019
2948
|
AwarenessProvider,
|
|
3020
2949
|
BaseTextEditor,
|
|
2950
|
+
Cursor,
|
|
3021
2951
|
Doc,
|
|
3022
2952
|
EditorModes,
|
|
3023
2953
|
MarkdownEditor,
|
|
@@ -3033,6 +2963,7 @@ export {
|
|
|
3033
2963
|
awareness,
|
|
3034
2964
|
basicBundle,
|
|
3035
2965
|
blast,
|
|
2966
|
+
callbackWrapper,
|
|
3036
2967
|
code2 as code,
|
|
3037
2968
|
codeTheme,
|
|
3038
2969
|
comments,
|
|
@@ -3043,6 +2974,7 @@ export {
|
|
|
3043
2974
|
defaultSlots,
|
|
3044
2975
|
defaultTextSlots,
|
|
3045
2976
|
defaultTheme,
|
|
2977
|
+
getToken,
|
|
3046
2978
|
hr,
|
|
3047
2979
|
image,
|
|
3048
2980
|
link,
|
|
@@ -3053,16 +2985,19 @@ export {
|
|
|
3053
2985
|
markdownTagsExtensions,
|
|
3054
2986
|
markdownTheme,
|
|
3055
2987
|
mention,
|
|
3056
|
-
mermaid,
|
|
3057
2988
|
modelState,
|
|
3058
|
-
|
|
2989
|
+
outliner,
|
|
2990
|
+
setComments,
|
|
3059
2991
|
setFocus,
|
|
3060
2992
|
setSelection,
|
|
2993
|
+
syncFacet,
|
|
3061
2994
|
table,
|
|
3062
2995
|
tags2 as tags,
|
|
3063
2996
|
tasklist,
|
|
3064
2997
|
textTheme,
|
|
3065
2998
|
typewriter,
|
|
2999
|
+
useAwareness,
|
|
3000
|
+
useComments,
|
|
3066
3001
|
useTextModel,
|
|
3067
3002
|
yjs
|
|
3068
3003
|
};
|