@dxos/react-ui-editor 0.3.11-main.6fccc32 → 0.3.11-main.73ee990
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 +1259 -1060
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +13 -14
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +14 -6
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +1 -1
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -0
- package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
- package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
- 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/defs.d.ts +7 -9
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/index.d.ts +0 -1
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/semaphore.d.ts +7 -12
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness.d.ts +1 -1
- package/dist/types/src/extensions/awareness.d.ts.map +1 -1
- package/dist/types/src/extensions/basic.d.ts +4 -5
- package/dist/types/src/extensions/basic.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +35 -18
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/cursor.d.ts +3 -3
- package/dist/types/src/extensions/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +1 -7
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts +11 -0
- package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
- package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts +8 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner.d.ts +4 -0
- package/dist/types/src/extensions/outliner.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
- package/dist/types/src/hooks/defs.d.ts +25 -0
- package/dist/types/src/hooks/defs.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
- package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextModel.d.ts +2 -27
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +2 -2
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/themes/default.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +2 -0
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +34 -37
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +72 -80
- package/src/components/TextEditor/TextEditor.tsx +96 -70
- package/src/components/TextEditor/comments.stories.tsx +113 -71
- package/src/components/Toolbar/Toolbar.stories.tsx +86 -0
- package/src/components/Toolbar/Toolbar.tsx +115 -0
- package/src/extensions/autocomplete.ts +6 -3
- package/src/extensions/automerge/automerge.ts +39 -46
- package/src/extensions/automerge/defs.ts +10 -31
- package/src/extensions/automerge/index.ts +0 -1
- package/src/extensions/automerge/semaphore.ts +23 -20
- package/src/extensions/automerge/update-automerge.ts +14 -8
- package/src/extensions/awareness.ts +15 -14
- package/src/extensions/basic.ts +22 -17
- package/src/extensions/comments.ts +269 -162
- package/src/extensions/cursor.ts +7 -3
- package/src/extensions/index.ts +1 -7
- package/src/extensions/markdown/bundle.ts +20 -30
- package/src/extensions/markdown/code.ts +142 -0
- package/src/extensions/markdown/formatting.ts +159 -0
- package/src/extensions/markdown/heading.ts +59 -0
- package/src/extensions/markdown/highlight.ts +9 -15
- package/src/extensions/markdown/hr.ts +71 -0
- package/src/extensions/markdown/index.ts +8 -0
- package/src/extensions/{link.ts → markdown/link.ts} +8 -5
- package/src/extensions/markdown/tasklist.ts +110 -0
- package/src/extensions/mention.ts +9 -2
- package/src/extensions/outliner.ts +12 -0
- package/src/hooks/awareness-provider.ts +24 -20
- package/src/hooks/defs.ts +42 -0
- package/src/hooks/index.ts +4 -2
- package/src/hooks/useActionHandler.ts +45 -0
- package/src/hooks/useTextEditor.ts +29 -0
- package/src/hooks/useTextModel.ts +47 -56
- package/src/styles/markdown.ts +9 -10
- package/src/themes/default.ts +1 -11
- package/src/util.ts +26 -3
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -12
- package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
- package/dist/types/src/extensions/code.d.ts.map +0 -1
- package/dist/types/src/extensions/hr.d.ts.map +0 -1
- package/dist/types/src/extensions/image.d.ts.map +0 -1
- package/dist/types/src/extensions/link.d.ts.map +0 -1
- package/dist/types/src/extensions/table.d.ts.map +0 -1
- package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
- package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/index.d.ts +0 -2
- package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
- package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
- package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
- package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge.d.ts +0 -3
- package/dist/types/src/hooks/automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs.d.ts +0 -27
- package/dist/types/src/hooks/yjs.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -3
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
- package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
- package/dist/types/src/testing/proto/index.d.ts +0 -2
- package/dist/types/src/testing/proto/index.d.ts.map +0 -1
- package/dist/types/src/testing/replicator.d.ts +0 -45
- package/dist/types/src/testing/replicator.d.ts.map +0 -1
- package/src/components/TextEditor/codemirror.stories.ts +0 -115
- package/src/components/TextEditor/yjs.stories.tsx +0 -56
- package/src/extensions/code.ts +0 -106
- package/src/extensions/hr.ts +0 -73
- package/src/extensions/tasklist.ts +0 -119
- package/src/extensions/yjs/cursor.ts +0 -22
- package/src/extensions/yjs/index.ts +0 -5
- package/src/extensions/yjs/yjs.test.ts +0 -35
- package/src/extensions/yjs/yjs.ts +0 -16
- package/src/hooks/automerge.ts +0 -52
- package/src/hooks/yjs.ts +0 -156
- package/src/testing/index.ts +0 -6
- package/src/testing/proto/gen/schema.ts +0 -49
- package/src/testing/proto/index.ts +0 -5
- package/src/testing/proto/schema.proto +0 -15
- package/src/testing/replicator.ts +0 -184
- /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
- /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
- /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
- /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
- /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
- /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
- /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
- /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
|
@@ -7,14 +7,14 @@ import { tags as tags2 } from "@lezer/highlight";
|
|
|
7
7
|
|
|
8
8
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
9
9
|
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
10
|
-
import { EditorView as
|
|
10
|
+
import { EditorView as EditorView13 } 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,
|
|
15
|
-
import {
|
|
14
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef, useState } from "react";
|
|
15
|
+
import { log as log2 } from "@dxos/log";
|
|
16
16
|
import { useThemeContext } from "@dxos/react-ui";
|
|
17
|
-
import {
|
|
17
|
+
import { attentionSurface, 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";
|
|
@@ -29,9 +29,11 @@ var autocomplete = ({ onSearch }) => {
|
|
|
29
29
|
// https://codemirror.net/examples/autocompletion
|
|
30
30
|
// https://codemirror.net/docs/ref/#autocomplete.autocompletion
|
|
31
31
|
autocompletion({
|
|
32
|
-
|
|
33
|
-
//
|
|
34
|
-
|
|
32
|
+
activateOnTyping: true,
|
|
33
|
+
// closeOnBlur: false,
|
|
34
|
+
// defaultKeymap: false,
|
|
35
|
+
// TODO(burdon): Styles/fragments.
|
|
36
|
+
tooltipClass: () => "shadow rounded"
|
|
35
37
|
}),
|
|
36
38
|
// TODO(burdon): Option to create new page?
|
|
37
39
|
// TODO(burdon): Optional decoration via addToOptions
|
|
@@ -51,9 +53,10 @@ var autocomplete = ({ onSearch }) => {
|
|
|
51
53
|
};
|
|
52
54
|
|
|
53
55
|
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
54
|
-
import {
|
|
55
|
-
import { ViewPlugin } from "@codemirror/view";
|
|
56
|
-
import { next as
|
|
56
|
+
import { StateField } from "@codemirror/state";
|
|
57
|
+
import { EditorView, ViewPlugin } from "@codemirror/view";
|
|
58
|
+
import { next as _automerge } from "@dxos/automerge/automerge";
|
|
59
|
+
import { invariant } from "@dxos/invariant";
|
|
57
60
|
|
|
58
61
|
// packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
|
|
59
62
|
import get from "lodash.get";
|
|
@@ -94,19 +97,19 @@ var cursorConverter = (handle, path) => ({
|
|
|
94
97
|
// packages/ui/react-ui-editor/src/extensions/automerge/defs.ts
|
|
95
98
|
import { Annotation, StateEffect } from "@codemirror/state";
|
|
96
99
|
var effectType = StateEffect.define({});
|
|
100
|
+
var reconcileAnnotationType = Annotation.define();
|
|
101
|
+
var getPath = (state, field) => state.field(field).path;
|
|
102
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
97
103
|
var updateHeads = (newHeads) => effectType.of({
|
|
98
104
|
newHeads
|
|
99
105
|
});
|
|
100
|
-
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
101
|
-
var getPath = (state, field) => state.field(field).path;
|
|
102
|
-
var reconcileAnnotationType = Annotation.define();
|
|
103
106
|
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
104
107
|
|
|
105
108
|
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
106
|
-
import { next as
|
|
109
|
+
import { next as automerge3 } from "@dxos/automerge/automerge";
|
|
107
110
|
|
|
108
111
|
// packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
|
|
109
|
-
import { next as
|
|
112
|
+
import { next as automerge2 } from "@dxos/automerge/automerge";
|
|
110
113
|
var updateAutomerge = (field, handle, transactions, state) => {
|
|
111
114
|
const { lastHeads, path } = state.field(field);
|
|
112
115
|
let hasChanges = false;
|
|
@@ -119,11 +122,19 @@ var updateAutomerge = (field, handle, transactions, state) => {
|
|
|
119
122
|
return void 0;
|
|
120
123
|
}
|
|
121
124
|
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
125
|
+
const t = [];
|
|
122
126
|
for (const tr of transactions) {
|
|
123
|
-
tr.changes.iterChanges((fromA, toA, _fromB, _toB,
|
|
124
|
-
|
|
127
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
|
|
128
|
+
t.push({
|
|
129
|
+
fromA,
|
|
130
|
+
toA,
|
|
131
|
+
insert
|
|
132
|
+
});
|
|
125
133
|
});
|
|
126
134
|
}
|
|
135
|
+
t.reverse().forEach(({ fromA, toA, insert }) => {
|
|
136
|
+
automerge2.splice(doc, path.slice(), fromA, toA - fromA, insert.toString());
|
|
137
|
+
});
|
|
127
138
|
});
|
|
128
139
|
return newHeads ?? void 0;
|
|
129
140
|
};
|
|
@@ -237,40 +248,42 @@ var charPath = (textPath, candidatePath) => {
|
|
|
237
248
|
|
|
238
249
|
// packages/ui/react-ui-editor/src/extensions/automerge/semaphore.ts
|
|
239
250
|
var PatchSemaphore = class {
|
|
240
|
-
|
|
241
|
-
|
|
251
|
+
// prettier-ignore
|
|
252
|
+
constructor(_handle, _state) {
|
|
253
|
+
this._handle = _handle;
|
|
254
|
+
this._state = _state;
|
|
242
255
|
this._inReconcile = false;
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
changes: inverted,
|
|
259
|
-
annotations: reconcileAnnotationType.of(true)
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
|
|
263
|
-
if (newHeads === null || newHeads === void 0) {
|
|
264
|
-
newHeads = automerge2.getHeads(handle.docSync());
|
|
265
|
-
}
|
|
266
|
-
const diff = automerge2.equals(oldHeads, newHeads) ? [] : automerge2.diff(handle.docSync(), oldHeads, newHeads);
|
|
267
|
-
updateCodeMirror(view, selection, path, diff);
|
|
256
|
+
}
|
|
257
|
+
// NOTE: Cannot destruct view.state.
|
|
258
|
+
reconcile(view) {
|
|
259
|
+
if (this._inReconcile) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
this._inReconcile = true;
|
|
263
|
+
const path = getPath(view.state, this._state);
|
|
264
|
+
const oldHeads = getLastHeads(view.state, this._state);
|
|
265
|
+
let selection = view.state.selection;
|
|
266
|
+
const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
267
|
+
const toInvert = transactions.slice().reverse();
|
|
268
|
+
for (const tx of toInvert) {
|
|
269
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
270
|
+
selection = selection.map(inverted);
|
|
268
271
|
view.dispatch({
|
|
269
|
-
|
|
270
|
-
annotations: reconcileAnnotationType.of(
|
|
272
|
+
changes: inverted,
|
|
273
|
+
annotations: reconcileAnnotationType.of(true)
|
|
271
274
|
});
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
}
|
|
276
|
+
let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
|
|
277
|
+
if (newHeads === null || newHeads === void 0) {
|
|
278
|
+
newHeads = automerge3.getHeads(this._handle.docSync());
|
|
279
|
+
}
|
|
280
|
+
const diff = automerge3.equals(oldHeads, newHeads) ? [] : automerge3.diff(this._handle.docSync(), oldHeads, newHeads);
|
|
281
|
+
updateCodeMirror(view, selection, path, diff);
|
|
282
|
+
view.dispatch({
|
|
283
|
+
effects: updateHeads(newHeads),
|
|
284
|
+
annotations: reconcileAnnotationType.of({})
|
|
285
|
+
});
|
|
286
|
+
this._inReconcile = false;
|
|
274
287
|
}
|
|
275
288
|
};
|
|
276
289
|
|
|
@@ -280,16 +293,17 @@ var defaultCursorConverter = {
|
|
|
280
293
|
toCursor: (position) => position.toString(),
|
|
281
294
|
fromCursor: (cursor) => parseInt(cursor)
|
|
282
295
|
};
|
|
283
|
-
var Cursor = class {
|
|
296
|
+
var Cursor = class _Cursor {
|
|
284
297
|
static {
|
|
285
298
|
this.converter = Facet.define({
|
|
286
299
|
combine: (providers) => providers[0] ?? defaultCursorConverter
|
|
287
300
|
});
|
|
288
301
|
}
|
|
289
302
|
static {
|
|
290
|
-
this.getCursorFromRange = (
|
|
291
|
-
const
|
|
292
|
-
const
|
|
303
|
+
this.getCursorFromRange = (state, range) => {
|
|
304
|
+
const cursorConverter2 = state.facet(_Cursor.converter);
|
|
305
|
+
const from = cursorConverter2.toCursor(range.from);
|
|
306
|
+
const to = cursorConverter2.toCursor(range.to, -1);
|
|
293
307
|
return [
|
|
294
308
|
from,
|
|
295
309
|
to
|
|
@@ -297,10 +311,11 @@ var Cursor = class {
|
|
|
297
311
|
};
|
|
298
312
|
}
|
|
299
313
|
static {
|
|
300
|
-
this.getRangeFromCursor = (
|
|
314
|
+
this.getRangeFromCursor = (state, cursor) => {
|
|
315
|
+
const cursorConverter2 = state.facet(_Cursor.converter);
|
|
301
316
|
const parts = cursor.split(":");
|
|
302
|
-
const from =
|
|
303
|
-
const to =
|
|
317
|
+
const from = cursorConverter2.fromCursor(parts[0]);
|
|
318
|
+
const to = cursorConverter2.fromCursor(parts[1]);
|
|
304
319
|
return from !== void 0 && to !== void 0 ? {
|
|
305
320
|
from,
|
|
306
321
|
to
|
|
@@ -310,21 +325,19 @@ var Cursor = class {
|
|
|
310
325
|
};
|
|
311
326
|
|
|
312
327
|
// packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
|
|
313
|
-
var
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
var automerge3 = ({ handle, path }) => {
|
|
317
|
-
const stateField = StateField.define({
|
|
328
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts";
|
|
329
|
+
var automerge4 = ({ handle, path }) => {
|
|
330
|
+
const state = StateField.define({
|
|
318
331
|
create: () => ({
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
332
|
+
path: path.slice(),
|
|
333
|
+
lastHeads: _automerge.getHeads(handle.docSync()),
|
|
334
|
+
unreconciledTransactions: []
|
|
322
335
|
}),
|
|
323
336
|
update: (value, tr) => {
|
|
324
337
|
const result = {
|
|
338
|
+
path: path.slice(),
|
|
325
339
|
lastHeads: value.lastHeads,
|
|
326
|
-
unreconciledTransactions: value.unreconciledTransactions.slice()
|
|
327
|
-
path: path.slice()
|
|
340
|
+
unreconciledTransactions: value.unreconciledTransactions.slice()
|
|
328
341
|
};
|
|
329
342
|
let clearUnreconciled = false;
|
|
330
343
|
for (const effect of tr.effects) {
|
|
@@ -343,39 +356,44 @@ var automerge3 = ({ handle, path }) => {
|
|
|
343
356
|
return result;
|
|
344
357
|
}
|
|
345
358
|
});
|
|
346
|
-
const semaphore = new PatchSemaphore(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
+
const semaphore = new PatchSemaphore(handle, state);
|
|
360
|
+
return [
|
|
361
|
+
Cursor.converter.of(cursorConverter(handle, path)),
|
|
362
|
+
// Reconcile external updates.
|
|
363
|
+
ViewPlugin.fromClass(class {
|
|
364
|
+
constructor(_view) {
|
|
365
|
+
this._view = _view;
|
|
366
|
+
invariant(this._view, void 0, {
|
|
367
|
+
F: __dxlog_file,
|
|
368
|
+
L: 66,
|
|
369
|
+
S: this,
|
|
370
|
+
A: [
|
|
371
|
+
"this._view",
|
|
372
|
+
""
|
|
373
|
+
]
|
|
359
374
|
});
|
|
375
|
+
handle.addListener("change", this._handleChange.bind(this));
|
|
360
376
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
377
|
+
destroy() {
|
|
378
|
+
handle.removeListener("change", this._handleChange.bind(this));
|
|
379
|
+
}
|
|
380
|
+
_handleChange() {
|
|
381
|
+
semaphore.reconcile(this._view);
|
|
382
|
+
}
|
|
383
|
+
}),
|
|
384
|
+
// Reconcile local updates.
|
|
385
|
+
EditorView.updateListener.of(({ view, changes }) => {
|
|
386
|
+
if (!changes.empty) {
|
|
387
|
+
semaphore.reconcile(view);
|
|
388
|
+
}
|
|
389
|
+
}),
|
|
390
|
+
state
|
|
391
|
+
];
|
|
374
392
|
};
|
|
375
393
|
|
|
376
394
|
// packages/ui/react-ui-editor/src/extensions/awareness.ts
|
|
377
|
-
import { Annotation as Annotation2, Facet as
|
|
378
|
-
import { Decoration, EditorView, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
395
|
+
import { Annotation as Annotation2, Facet as Facet2, RangeSet } from "@codemirror/state";
|
|
396
|
+
import { Decoration, EditorView as EditorView2, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
379
397
|
import { Event } from "@dxos/async";
|
|
380
398
|
import { Context } from "@dxos/context";
|
|
381
399
|
var dummyProvider = {
|
|
@@ -384,11 +402,11 @@ var dummyProvider = {
|
|
|
384
402
|
},
|
|
385
403
|
close: () => {
|
|
386
404
|
},
|
|
405
|
+
getRemoteStates: () => [],
|
|
387
406
|
update: () => {
|
|
388
|
-
}
|
|
389
|
-
getRemoteStates: () => []
|
|
407
|
+
}
|
|
390
408
|
};
|
|
391
|
-
var AwarenessProvider =
|
|
409
|
+
var AwarenessProvider = Facet2.define({
|
|
392
410
|
combine: (providers) => providers[0] ?? dummyProvider
|
|
393
411
|
});
|
|
394
412
|
var RemoteSelectionChangedAnnotation = Annotation2.define();
|
|
@@ -549,7 +567,7 @@ var RemoteCaretWidget = class extends WidgetType {
|
|
|
549
567
|
return true;
|
|
550
568
|
}
|
|
551
569
|
};
|
|
552
|
-
var styles =
|
|
570
|
+
var styles = EditorView2.baseTheme({
|
|
553
571
|
".cm-collab-selection": {},
|
|
554
572
|
".cm-collab-selectionLine": {
|
|
555
573
|
padding: 0,
|
|
@@ -562,40 +580,42 @@ var styles = EditorView.baseTheme({
|
|
|
562
580
|
marginLeft: "-1px",
|
|
563
581
|
marginRight: "-1px",
|
|
564
582
|
boxSizing: "border-box",
|
|
565
|
-
display: "inline"
|
|
583
|
+
display: "inline",
|
|
584
|
+
cursor: "pointer"
|
|
566
585
|
},
|
|
567
586
|
".cm-collab-selectionCaretDot": {
|
|
568
587
|
borderRadius: "50%",
|
|
569
588
|
position: "absolute",
|
|
570
|
-
width: ".
|
|
571
|
-
height: ".
|
|
572
|
-
top: "-.
|
|
573
|
-
left: "-.
|
|
589
|
+
width: ".5em",
|
|
590
|
+
height: ".5em",
|
|
591
|
+
top: "-.25em",
|
|
592
|
+
left: "-.25em",
|
|
574
593
|
backgroundColor: "inherit",
|
|
575
594
|
transition: "transform .3s ease-in-out",
|
|
576
595
|
boxSizing: "border-box"
|
|
577
596
|
},
|
|
578
597
|
".cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot": {
|
|
579
|
-
|
|
580
|
-
|
|
598
|
+
transform: "scale(0)",
|
|
599
|
+
transformOrigin: "center"
|
|
581
600
|
},
|
|
582
601
|
".cm-collab-selectionInfo": {
|
|
583
602
|
position: "absolute",
|
|
584
|
-
|
|
585
|
-
|
|
603
|
+
transform: "translate(-50%, 0)",
|
|
604
|
+
top: "-20px",
|
|
605
|
+
left: 0,
|
|
586
606
|
fontSize: ".75em",
|
|
587
|
-
fontFamily: "serif",
|
|
607
|
+
fontFamily: "sans-serif",
|
|
588
608
|
fontStyle: "normal",
|
|
589
609
|
fontWeight: "normal",
|
|
590
610
|
lineHeight: "normal",
|
|
591
611
|
userSelect: "none",
|
|
592
612
|
color: "white",
|
|
593
|
-
|
|
594
|
-
paddingRight: "2px",
|
|
613
|
+
padding: "2px",
|
|
595
614
|
zIndex: 101,
|
|
596
615
|
transition: "opacity .3s ease-in-out",
|
|
597
616
|
backgroundColor: "inherit",
|
|
598
|
-
|
|
617
|
+
borderRadius: "2px",
|
|
618
|
+
// These should be separate.
|
|
599
619
|
opacity: 0,
|
|
600
620
|
transitionDelay: "0s",
|
|
601
621
|
whiteSpace: "nowrap"
|
|
@@ -608,26 +628,28 @@ var styles = EditorView.baseTheme({
|
|
|
608
628
|
|
|
609
629
|
// packages/ui/react-ui-editor/src/extensions/basic.ts
|
|
610
630
|
import { closeBrackets } from "@codemirror/autocomplete";
|
|
631
|
+
import { history } from "@codemirror/commands";
|
|
611
632
|
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
612
633
|
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
613
|
-
import { drawSelection, EditorView as
|
|
614
|
-
var basicBundle = ({
|
|
615
|
-
|
|
616
|
-
// TODO(burdon): Compare with minimalSetup.
|
|
634
|
+
import { drawSelection, EditorView as EditorView3, highlightActiveLine, placeholder } from "@codemirror/view";
|
|
635
|
+
var basicBundle = ({ themeMode, placeholder: _placeholder, lineWrapping = true }) => [
|
|
636
|
+
lineWrapping ? EditorView3.lineWrapping : [],
|
|
617
637
|
// https://codemirror.net/docs/ref/#codemirror.minimalSetup
|
|
618
638
|
bracketMatching(),
|
|
619
639
|
closeBrackets(),
|
|
620
640
|
drawSelection(),
|
|
621
|
-
|
|
622
|
-
|
|
641
|
+
highlightActiveLine(),
|
|
642
|
+
history(),
|
|
643
|
+
_placeholder ? placeholder(_placeholder) : [],
|
|
644
|
+
// https://github.com/codemirror/theme-one-dark
|
|
623
645
|
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
624
|
-
]
|
|
646
|
+
];
|
|
625
647
|
|
|
626
648
|
// packages/ui/react-ui-editor/src/extensions/blast.ts
|
|
627
|
-
import { EditorView as
|
|
649
|
+
import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
|
|
628
650
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
629
|
-
import { invariant } from "@dxos/invariant";
|
|
630
|
-
var
|
|
651
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
652
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
|
|
631
653
|
var defaultOptions = {
|
|
632
654
|
effect: 2,
|
|
633
655
|
maxParticles: 200,
|
|
@@ -660,10 +682,10 @@ var blast = (options = defaultOptions) => {
|
|
|
660
682
|
const getPoint = (view, pos) => {
|
|
661
683
|
const { left: x = 0, top: y = 0 } = view.coordsForChar(pos) ?? {};
|
|
662
684
|
const element = document.elementFromPoint(x, y);
|
|
663
|
-
const { offsetLeft: left = 0, offsetTop:
|
|
685
|
+
const { offsetLeft: left = 0, offsetTop: top2 = 0 } = view.scrollDOM.parentElement ?? {};
|
|
664
686
|
const point = {
|
|
665
687
|
x: x - left,
|
|
666
|
-
y: y -
|
|
688
|
+
y: y - top2
|
|
667
689
|
};
|
|
668
690
|
return {
|
|
669
691
|
element,
|
|
@@ -672,7 +694,7 @@ var blast = (options = defaultOptions) => {
|
|
|
672
694
|
};
|
|
673
695
|
return [
|
|
674
696
|
// Cursor moved.
|
|
675
|
-
|
|
697
|
+
EditorView4.updateListener.of((update2) => {
|
|
676
698
|
if (blaster?.node !== update2.view.scrollDOM) {
|
|
677
699
|
if (blaster) {
|
|
678
700
|
blaster.destroy();
|
|
@@ -753,8 +775,8 @@ var Blaster = class {
|
|
|
753
775
|
return this._node;
|
|
754
776
|
}
|
|
755
777
|
initialize() {
|
|
756
|
-
|
|
757
|
-
F:
|
|
778
|
+
invariant2(!this._canvas && !this._ctx, void 0, {
|
|
779
|
+
F: __dxlog_file2,
|
|
758
780
|
L: 138,
|
|
759
781
|
S: this,
|
|
760
782
|
A: [
|
|
@@ -790,8 +812,8 @@ var Blaster = class {
|
|
|
790
812
|
}
|
|
791
813
|
}
|
|
792
814
|
start() {
|
|
793
|
-
|
|
794
|
-
F:
|
|
815
|
+
invariant2(this._canvas && this._ctx, void 0, {
|
|
816
|
+
F: __dxlog_file2,
|
|
795
817
|
L: 177,
|
|
796
818
|
S: this,
|
|
797
819
|
A: [
|
|
@@ -933,9 +955,13 @@ var random = (min, max) => {
|
|
|
933
955
|
return min + ~~(Math.random() * (max - min + 1));
|
|
934
956
|
};
|
|
935
957
|
|
|
936
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
937
|
-
import {
|
|
938
|
-
import {
|
|
958
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
959
|
+
import { Facet as Facet3, StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
960
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView5 } from "@codemirror/view";
|
|
961
|
+
import sortBy from "lodash.sortby";
|
|
962
|
+
import { useEffect } from "react";
|
|
963
|
+
import { debounce } from "@dxos/async";
|
|
964
|
+
import { nonNullable } from "@dxos/util";
|
|
939
965
|
|
|
940
966
|
// packages/ui/react-ui-editor/src/styles/cursors.ts
|
|
941
967
|
import * as random2 from "lib0/random";
|
|
@@ -978,34 +1004,15 @@ var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
|
|
|
978
1004
|
// packages/ui/react-ui-editor/src/styles/markdown.ts
|
|
979
1005
|
import { mx } from "@dxos/react-ui-theme";
|
|
980
1006
|
var headings = {
|
|
981
|
-
1:
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
|
|
991
|
-
"-ml-[7px]"
|
|
992
|
-
],
|
|
993
|
-
4: [
|
|
994
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
|
|
995
|
-
"-ml-[6px]"
|
|
996
|
-
],
|
|
997
|
-
5: [
|
|
998
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
|
|
999
|
-
"-ml-[5px]"
|
|
1000
|
-
],
|
|
1001
|
-
6: [
|
|
1002
|
-
"mbs-4 mbe-2 font-medium text-inherit no-underline",
|
|
1003
|
-
"-ml-[4px]"
|
|
1004
|
-
]
|
|
1005
|
-
};
|
|
1006
|
-
var heading = (level, readonly) => {
|
|
1007
|
-
const [style, offset] = headings[level];
|
|
1008
|
-
return mx(style, readonly && offset);
|
|
1007
|
+
1: "mbs-4 mbe-2 font-medium text-inherit no-underline text-4xl",
|
|
1008
|
+
2: "mbs-4 mbe-2 font-medium text-inherit no-underline text-3xl",
|
|
1009
|
+
3: "mbs-4 mbe-2 font-medium text-inherit no-underline text-2xl",
|
|
1010
|
+
4: "mbs-4 mbe-2 font-medium text-inherit no-underline text-xl",
|
|
1011
|
+
5: "mbs-4 mbe-2 font-medium text-inherit no-underline text-lg",
|
|
1012
|
+
6: "mbs-4 mbe-2 font-medium text-inherit no-underline"
|
|
1013
|
+
};
|
|
1014
|
+
var heading = (level) => {
|
|
1015
|
+
return headings[level];
|
|
1009
1016
|
};
|
|
1010
1017
|
var light = "text-neutral-200 dark:text-neutral-800";
|
|
1011
1018
|
var mark = mx("!font-normal !no-underline !text-inherit opacity-40", light);
|
|
@@ -1023,168 +1030,111 @@ import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
|
1023
1030
|
var tokens = tailwindConfig({}).theme;
|
|
1024
1031
|
var getToken = (path, defaultValue = void 0) => get2(tokens, path, defaultValue);
|
|
1025
1032
|
|
|
1026
|
-
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
1027
|
-
var CODE_REGEX = /```[\s\S]*?```/gs;
|
|
1028
|
-
var styles2 = EditorView4.baseTheme({
|
|
1029
|
-
"& .cm-codeblock": {
|
|
1030
|
-
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
1031
|
-
},
|
|
1032
|
-
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
1033
|
-
background: getToken("extend.colors.neutral.50")
|
|
1034
|
-
},
|
|
1035
|
-
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
1036
|
-
background: getToken("extend.colors.neutral.850")
|
|
1037
|
-
},
|
|
1038
|
-
'& [aria-readonly="true"] .cm-codeblock': {
|
|
1039
|
-
display: "block",
|
|
1040
|
-
paddingInline: "4px !important"
|
|
1041
|
-
},
|
|
1042
|
-
'& [aria-readonly="true"] .cm-codeblock-first': {
|
|
1043
|
-
paddingTop: "4px !important",
|
|
1044
|
-
borderTopLeftRadius: "4px",
|
|
1045
|
-
borderTopRightRadius: "4px"
|
|
1046
|
-
},
|
|
1047
|
-
'& [aria-readonly="true"] .cm-codeblock-last': {
|
|
1048
|
-
paddingBottom: "4px !important",
|
|
1049
|
-
borderBottomLeftRadius: "4px",
|
|
1050
|
-
borderBottomRightRadius: "4px"
|
|
1051
|
-
}
|
|
1052
|
-
});
|
|
1053
|
-
var getLineRange = (lines, from, to) => {
|
|
1054
|
-
const start = lines.findIndex((line) => line.from >= from);
|
|
1055
|
-
const end = lines.findIndex((line) => line.to >= to);
|
|
1056
|
-
return [
|
|
1057
|
-
start,
|
|
1058
|
-
end
|
|
1059
|
-
];
|
|
1060
|
-
};
|
|
1061
|
-
var code2 = () => {
|
|
1062
|
-
const getDecorations = (view) => {
|
|
1063
|
-
const decorations = [];
|
|
1064
|
-
if (!view.hasFocus || view.state.readOnly) {
|
|
1065
|
-
const text = view.state.doc.sliceString(0);
|
|
1066
|
-
const matches = text.matchAll(CODE_REGEX);
|
|
1067
|
-
const blocks = view.viewportLineBlocks;
|
|
1068
|
-
for (const match of matches) {
|
|
1069
|
-
const range = getLineRange(blocks, match.index, match.index + match[0].length);
|
|
1070
|
-
for (let i = range[0]; i <= range[1]; i++) {
|
|
1071
|
-
const block = blocks[i];
|
|
1072
|
-
decorations.push(Decoration2.line({
|
|
1073
|
-
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
1074
|
-
}).range(block.from));
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
return Decoration2.set(decorations);
|
|
1079
|
-
};
|
|
1080
|
-
return [
|
|
1081
|
-
ViewPlugin3.fromClass(class {
|
|
1082
|
-
constructor(view) {
|
|
1083
|
-
this.hasFocus = false;
|
|
1084
|
-
this.decorations = getDecorations(view);
|
|
1085
|
-
}
|
|
1086
|
-
update(update2) {
|
|
1087
|
-
if (
|
|
1088
|
-
// TODO(burdon): Generalize for other extensions.
|
|
1089
|
-
this.hasFocus !== update2.view.hasFocus || update2.startState.readOnly !== update2.view.state.readOnly || update2.docChanged || update2.viewportChanged
|
|
1090
|
-
) {
|
|
1091
|
-
this.hasFocus = update2.view.hasFocus;
|
|
1092
|
-
this.decorations = getDecorations(update2.view);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
1095
|
-
}, {
|
|
1096
|
-
decorations: (value) => value.decorations
|
|
1097
|
-
}),
|
|
1098
|
-
styles2
|
|
1099
|
-
];
|
|
1100
|
-
};
|
|
1101
|
-
|
|
1102
|
-
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1103
|
-
import { StateEffect as StateEffect2, StateField as StateField2 } from "@codemirror/state";
|
|
1104
|
-
import { hoverTooltip, keymap as keymap3, Decoration as Decoration3, EditorView as EditorView5 } from "@codemirror/view";
|
|
1105
|
-
import sortBy from "lodash.sortby";
|
|
1106
|
-
import { debounce } from "@dxos/async";
|
|
1107
|
-
import { invariant as invariant2 } from "@dxos/invariant";
|
|
1108
|
-
import { log as log2 } from "@dxos/log";
|
|
1109
|
-
import { nonNullable } from "@dxos/util";
|
|
1110
|
-
|
|
1111
1033
|
// packages/ui/react-ui-editor/src/util.ts
|
|
1112
1034
|
import { log } from "@dxos/log";
|
|
1113
|
-
var
|
|
1035
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util.ts";
|
|
1114
1036
|
var callbackWrapper = (fn) => (...args) => {
|
|
1115
1037
|
try {
|
|
1116
1038
|
return fn(...args);
|
|
1117
|
-
} catch (
|
|
1118
|
-
log.catch(
|
|
1119
|
-
F:
|
|
1120
|
-
L:
|
|
1039
|
+
} catch (err) {
|
|
1040
|
+
log.catch(err, void 0, {
|
|
1041
|
+
F: __dxlog_file3,
|
|
1042
|
+
L: 18,
|
|
1121
1043
|
S: void 0,
|
|
1122
1044
|
C: (f, a) => f(...a)
|
|
1123
1045
|
});
|
|
1124
1046
|
}
|
|
1125
1047
|
};
|
|
1048
|
+
var logChanges = (trs) => {
|
|
1049
|
+
const changes = trs.flatMap((tr) => {
|
|
1050
|
+
if (tr.changes.empty) {
|
|
1051
|
+
return void 0;
|
|
1052
|
+
}
|
|
1053
|
+
const changes2 = [];
|
|
1054
|
+
tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
|
|
1055
|
+
fromA,
|
|
1056
|
+
toA,
|
|
1057
|
+
fromB,
|
|
1058
|
+
toB,
|
|
1059
|
+
inserted: inserted.toString()
|
|
1060
|
+
})));
|
|
1061
|
+
return changes2;
|
|
1062
|
+
}).filter(Boolean);
|
|
1063
|
+
if (changes.length) {
|
|
1064
|
+
console.log("changes", changes);
|
|
1065
|
+
}
|
|
1066
|
+
};
|
|
1126
1067
|
|
|
1127
1068
|
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1128
|
-
var
|
|
1129
|
-
var styles3 = EditorView5.baseTheme({
|
|
1069
|
+
var styles2 = EditorView5.baseTheme({
|
|
1130
1070
|
"& .cm-comment": {
|
|
1131
1071
|
backgroundColor: getToken("extend.colors.yellow.50")
|
|
1132
1072
|
},
|
|
1133
|
-
"& .cm-comment-
|
|
1073
|
+
"& .cm-comment-current": {
|
|
1134
1074
|
backgroundColor: getToken("extend.colors.yellow.100")
|
|
1135
1075
|
}
|
|
1136
1076
|
});
|
|
1137
1077
|
var marks = {
|
|
1138
|
-
highlight:
|
|
1078
|
+
highlight: Decoration2.mark({
|
|
1139
1079
|
class: "cm-comment"
|
|
1140
1080
|
}),
|
|
1141
|
-
highlightActive:
|
|
1142
|
-
class: "cm-comment-
|
|
1081
|
+
highlightActive: Decoration2.mark({
|
|
1082
|
+
class: "cm-comment-current"
|
|
1143
1083
|
})
|
|
1144
1084
|
};
|
|
1145
|
-
var setFocus = (view,
|
|
1146
|
-
const
|
|
1147
|
-
if (!
|
|
1085
|
+
var setFocus = (view, id, center = true) => {
|
|
1086
|
+
const comment = view.state.field(commentsState).comments.find((range2) => range2.comment.id === id);
|
|
1087
|
+
if (!comment?.comment.cursor) {
|
|
1148
1088
|
return;
|
|
1149
1089
|
}
|
|
1150
|
-
view.
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1090
|
+
const range = Cursor.getRangeFromCursor(view.state, comment.comment.cursor);
|
|
1091
|
+
if (range) {
|
|
1092
|
+
view.dispatch({
|
|
1093
|
+
selection: {
|
|
1094
|
+
anchor: range.from
|
|
1095
|
+
},
|
|
1096
|
+
effects: [
|
|
1097
|
+
//
|
|
1098
|
+
EditorView5.scrollIntoView(range.from, center ? {
|
|
1099
|
+
y: "center"
|
|
1100
|
+
} : void 0),
|
|
1101
|
+
setSelection.of({
|
|
1102
|
+
current: id
|
|
1103
|
+
})
|
|
1104
|
+
]
|
|
1105
|
+
});
|
|
1106
|
+
}
|
|
1159
1107
|
};
|
|
1160
|
-
var
|
|
1108
|
+
var setComments = StateEffect2.define();
|
|
1161
1109
|
var setSelection = StateEffect2.define();
|
|
1162
1110
|
var setCommentState = StateEffect2.define();
|
|
1163
|
-
var
|
|
1111
|
+
var commentsState = StateField2.define({
|
|
1164
1112
|
create: () => ({
|
|
1165
|
-
|
|
1113
|
+
comments: [],
|
|
1114
|
+
selection: {}
|
|
1166
1115
|
}),
|
|
1167
1116
|
update: (value, tr) => {
|
|
1168
|
-
const cursorConverter3 = tr.state.facet(Cursor.converter);
|
|
1169
1117
|
for (const effect of tr.effects) {
|
|
1170
1118
|
if (effect.is(setSelection)) {
|
|
1171
1119
|
return {
|
|
1172
1120
|
...value,
|
|
1173
|
-
|
|
1121
|
+
selection: effect.value
|
|
1174
1122
|
};
|
|
1175
1123
|
}
|
|
1176
|
-
if (effect.is(
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
|
|
1124
|
+
if (effect.is(setComments)) {
|
|
1125
|
+
const comments2 = effect.value.map((comment) => {
|
|
1126
|
+
if (!comment.cursor) {
|
|
1127
|
+
return void 0;
|
|
1128
|
+
}
|
|
1129
|
+
const range = Cursor.getRangeFromCursor(tr.state, comment.cursor);
|
|
1180
1130
|
return range && {
|
|
1181
|
-
|
|
1182
|
-
|
|
1131
|
+
comment,
|
|
1132
|
+
range
|
|
1183
1133
|
};
|
|
1184
1134
|
}).filter(nonNullable);
|
|
1185
1135
|
return {
|
|
1186
1136
|
...value,
|
|
1187
|
-
|
|
1137
|
+
comments: comments2
|
|
1188
1138
|
};
|
|
1189
1139
|
}
|
|
1190
1140
|
if (effect.is(setCommentState)) {
|
|
@@ -1195,86 +1145,129 @@ var commentsStateField = StateField2.define({
|
|
|
1195
1145
|
}
|
|
1196
1146
|
});
|
|
1197
1147
|
var highlightDecorations = EditorView5.decorations.compute([
|
|
1198
|
-
|
|
1148
|
+
commentsState
|
|
1199
1149
|
], (state) => {
|
|
1200
|
-
const {
|
|
1201
|
-
const decorations = sortBy(
|
|
1202
|
-
|
|
1150
|
+
const { selection: { current }, comments: comments2 } = state.field(commentsState);
|
|
1151
|
+
const decorations = sortBy(comments2 ?? [], (range) => range.range.from)?.flatMap((comment) => {
|
|
1152
|
+
const range = comment.range;
|
|
1153
|
+
if (!range || range.from === range.to) {
|
|
1154
|
+
console.warn("Invalid range:", range);
|
|
1203
1155
|
return void 0;
|
|
1204
1156
|
}
|
|
1205
|
-
|
|
1206
|
-
from: selection.from,
|
|
1207
|
-
to: selection.to
|
|
1208
|
-
};
|
|
1209
|
-
if (selection.id === active) {
|
|
1157
|
+
if (comment.comment.id === current) {
|
|
1210
1158
|
return marks.highlightActive.range(range.from, range.to);
|
|
1211
1159
|
} else {
|
|
1212
1160
|
return marks.highlight.range(range.from, range.to);
|
|
1213
1161
|
}
|
|
1214
|
-
}).filter(nonNullable)
|
|
1215
|
-
return
|
|
1162
|
+
}).filter(nonNullable);
|
|
1163
|
+
return Decoration2.set(decorations);
|
|
1216
1164
|
});
|
|
1217
|
-
var
|
|
1218
|
-
|
|
1219
|
-
const
|
|
1220
|
-
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1165
|
+
var trackPastedComments = (onUpdate) => {
|
|
1166
|
+
let tracked = null;
|
|
1167
|
+
const handleTrack = (event, view) => {
|
|
1168
|
+
const comments2 = view.state.field(commentsState);
|
|
1169
|
+
const { main } = view.state.selection;
|
|
1170
|
+
const selectedRanges = comments2.comments.filter(({ range }) => range.from >= main.from && range.to <= main.to && range.from < range.to);
|
|
1171
|
+
if (!selectedRanges.length) {
|
|
1172
|
+
tracked = null;
|
|
1173
|
+
} else {
|
|
1174
|
+
tracked = {
|
|
1175
|
+
text: view.state.doc.slice(main.from, main.to),
|
|
1176
|
+
comments: selectedRanges.map(({ comment, range }) => ({
|
|
1177
|
+
id: comment.id,
|
|
1178
|
+
from: range.from - main.from,
|
|
1179
|
+
to: range.to - main.from
|
|
1180
|
+
}))
|
|
1181
|
+
};
|
|
1234
1182
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
changes: {
|
|
1254
|
-
from: head,
|
|
1255
|
-
insert: tag
|
|
1256
|
-
},
|
|
1257
|
-
selection: {
|
|
1258
|
-
anchor: head + tag.length
|
|
1183
|
+
};
|
|
1184
|
+
return [
|
|
1185
|
+
EditorView5.domEventHandlers({
|
|
1186
|
+
cut: handleTrack,
|
|
1187
|
+
copy: handleTrack
|
|
1188
|
+
}),
|
|
1189
|
+
// Handle paste.
|
|
1190
|
+
EditorView5.updateListener.of(({ view, state, transactions }) => {
|
|
1191
|
+
if (tracked) {
|
|
1192
|
+
const paste = transactions.find((tr) => tr.isUserEvent("input.paste"));
|
|
1193
|
+
if (paste) {
|
|
1194
|
+
let found = -1;
|
|
1195
|
+
paste.changes.iterChanges((fromA, toA, fromB, toB, text) => {
|
|
1196
|
+
if (text.eq(tracked.text)) {
|
|
1197
|
+
for (let i = transactions.indexOf(paste) + 1; i < transactions.length; i++) {
|
|
1198
|
+
fromB = transactions[i].changes.mapPos(fromB);
|
|
1199
|
+
}
|
|
1200
|
+
found = fromB;
|
|
1259
1201
|
}
|
|
1260
1202
|
});
|
|
1203
|
+
if (found > -1) {
|
|
1204
|
+
for (const moved of tracked.comments) {
|
|
1205
|
+
const range = {
|
|
1206
|
+
from: found + moved.from,
|
|
1207
|
+
to: found + moved.to
|
|
1208
|
+
};
|
|
1209
|
+
const cursor = Cursor.getCursorFromRange(state, range);
|
|
1210
|
+
onUpdate(moved.id, cursor);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
tracked = null;
|
|
1261
1214
|
}
|
|
1262
|
-
return true;
|
|
1263
1215
|
}
|
|
1264
|
-
}
|
|
1216
|
+
})
|
|
1217
|
+
];
|
|
1218
|
+
};
|
|
1219
|
+
var optionsFacet = Facet3.define({
|
|
1220
|
+
combine: (providers) => providers[0]
|
|
1221
|
+
});
|
|
1222
|
+
var createComment = (view) => {
|
|
1223
|
+
const options = view.state.facet(optionsFacet);
|
|
1224
|
+
const { from, to } = view.state.selection.main;
|
|
1225
|
+
if (from === to) {
|
|
1265
1226
|
return false;
|
|
1266
|
-
}
|
|
1227
|
+
}
|
|
1228
|
+
if (to === view.state.doc.length) {
|
|
1229
|
+
view.dispatch({
|
|
1230
|
+
changes: {
|
|
1231
|
+
from: to,
|
|
1232
|
+
insert: "\n"
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
const cursor = Cursor.getCursorFromRange(view.state, {
|
|
1237
|
+
from,
|
|
1238
|
+
to
|
|
1239
|
+
});
|
|
1240
|
+
if (cursor) {
|
|
1241
|
+
const id = options.onCreate?.(cursor, view.coordsAtPos(from));
|
|
1242
|
+
if (id) {
|
|
1243
|
+
view.dispatch({
|
|
1244
|
+
effects: setSelection.of({
|
|
1245
|
+
current: id
|
|
1246
|
+
}),
|
|
1247
|
+
selection: {
|
|
1248
|
+
anchor: from
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
return true;
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return false;
|
|
1255
|
+
};
|
|
1256
|
+
var comments = (options = {}) => {
|
|
1257
|
+
const { key: shortcut = "meta-'" } = options;
|
|
1258
|
+
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1267
1259
|
return [
|
|
1268
|
-
|
|
1260
|
+
optionsFacet.of(options),
|
|
1261
|
+
commentsState,
|
|
1269
1262
|
highlightDecorations,
|
|
1270
|
-
|
|
1263
|
+
styles2,
|
|
1271
1264
|
//
|
|
1272
1265
|
// Keymap.
|
|
1273
1266
|
//
|
|
1274
1267
|
options.onCreate ? keymap3.of([
|
|
1275
1268
|
{
|
|
1276
1269
|
key: shortcut,
|
|
1277
|
-
run: callbackWrapper(
|
|
1270
|
+
run: callbackWrapper(createComment)
|
|
1278
1271
|
}
|
|
1279
1272
|
]) : [],
|
|
1280
1273
|
//
|
|
@@ -1290,7 +1283,7 @@ var comments = (options = {}) => {
|
|
|
1290
1283
|
above: true,
|
|
1291
1284
|
create: () => {
|
|
1292
1285
|
const el = document.createElement("div");
|
|
1293
|
-
options.onHover
|
|
1286
|
+
options.onHover(el, shortcut);
|
|
1294
1287
|
return {
|
|
1295
1288
|
dom: el,
|
|
1296
1289
|
offset: {
|
|
@@ -1303,364 +1296,115 @@ var comments = (options = {}) => {
|
|
|
1303
1296
|
}
|
|
1304
1297
|
return null;
|
|
1305
1298
|
}, {
|
|
1306
|
-
|
|
1299
|
+
// TODO(burdon): Hide on change triggered immediately?
|
|
1300
|
+
// hideOnChange: true,
|
|
1307
1301
|
hoverTime: 1e3
|
|
1308
1302
|
}) : [],
|
|
1309
1303
|
//
|
|
1310
|
-
//
|
|
1311
|
-
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1304
|
+
// Track deleted ranges and update ranges for decorations.
|
|
1312
1305
|
//
|
|
1313
1306
|
EditorView5.updateListener.of(({ view, state, changes }) => {
|
|
1314
|
-
|
|
1315
|
-
{
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
if (
|
|
1321
|
-
|
|
1322
|
-
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1323
|
-
log2.info("deleted comment", {
|
|
1324
|
-
thread: range.id
|
|
1325
|
-
}, {
|
|
1326
|
-
F: __dxlog_file3,
|
|
1327
|
-
L: 274,
|
|
1328
|
-
S: void 0,
|
|
1329
|
-
C: (f, a) => f(...a)
|
|
1330
|
-
});
|
|
1331
|
-
}
|
|
1332
|
-
}
|
|
1333
|
-
if (from <= range.to) {
|
|
1334
|
-
const newRange = Cursor.getRangeFromCursor(cursorConverter3, range.cursor);
|
|
1335
|
-
Object.assign(range, newRange);
|
|
1336
|
-
mod = true;
|
|
1307
|
+
let mod = false;
|
|
1308
|
+
const { comments: comments2, ...value } = state.field(commentsState);
|
|
1309
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1310
|
+
comments2.forEach(({ comment, range }) => {
|
|
1311
|
+
if (from2 === to2) {
|
|
1312
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1313
|
+
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1314
|
+
options.onDelete?.(comment.id);
|
|
1337
1315
|
}
|
|
1338
|
-
}
|
|
1316
|
+
}
|
|
1317
|
+
if (from <= range.to) {
|
|
1318
|
+
const newRange = Cursor.getRangeFromCursor(view.state, comment.cursor);
|
|
1319
|
+
Object.assign(range, newRange);
|
|
1320
|
+
mod = true;
|
|
1321
|
+
}
|
|
1322
|
+
});
|
|
1323
|
+
});
|
|
1324
|
+
if (mod) {
|
|
1325
|
+
view.dispatch({
|
|
1326
|
+
effects: setCommentState.of({
|
|
1327
|
+
comments: comments2,
|
|
1328
|
+
...value
|
|
1329
|
+
})
|
|
1339
1330
|
});
|
|
1340
|
-
if (mod) {
|
|
1341
|
-
view.dispatch({
|
|
1342
|
-
effects: setCommentState.of({
|
|
1343
|
-
active,
|
|
1344
|
-
ranges
|
|
1345
|
-
})
|
|
1346
|
-
});
|
|
1347
|
-
}
|
|
1348
1331
|
}
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1332
|
+
}),
|
|
1333
|
+
//
|
|
1334
|
+
// Track selection/proximity.
|
|
1335
|
+
//
|
|
1336
|
+
EditorView5.updateListener.of(({ view, state }) => {
|
|
1337
|
+
let min = Infinity;
|
|
1338
|
+
const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
|
|
1339
|
+
const { head } = state.selection.main;
|
|
1340
|
+
const selection = {};
|
|
1341
|
+
comments2.forEach(({ comment, range }) => {
|
|
1342
|
+
if (head >= range.from && head <= range.to) {
|
|
1343
|
+
selection.current = comment.id;
|
|
1344
|
+
selection.closest = void 0;
|
|
1345
|
+
}
|
|
1346
|
+
if (!selection.current) {
|
|
1347
|
+
const d = Math.min(Math.abs(head - range.from), Math.abs(head - range.to));
|
|
1362
1348
|
if (d < min) {
|
|
1363
|
-
|
|
1349
|
+
selection.closest = comment.id;
|
|
1364
1350
|
min = d;
|
|
1365
1351
|
}
|
|
1366
|
-
});
|
|
1367
|
-
if (selected.active !== active || selected.closest !== closest) {
|
|
1368
|
-
view.dispatch({
|
|
1369
|
-
effects: setSelection.of(selected)
|
|
1370
|
-
});
|
|
1371
|
-
handleSelect({
|
|
1372
|
-
...selected,
|
|
1373
|
-
ranges: ranges.map((range) => ({
|
|
1374
|
-
...range,
|
|
1375
|
-
location: view.coordsAtPos(range.from)
|
|
1376
|
-
}))
|
|
1377
|
-
});
|
|
1378
1352
|
}
|
|
1353
|
+
});
|
|
1354
|
+
if (selection.current !== current || selection.closest !== closest) {
|
|
1355
|
+
view.dispatch({
|
|
1356
|
+
effects: setSelection.of(selection)
|
|
1357
|
+
});
|
|
1358
|
+
handleSelect({
|
|
1359
|
+
selection,
|
|
1360
|
+
comments: comments2.map(({ comment, range }) => ({
|
|
1361
|
+
comment,
|
|
1362
|
+
range,
|
|
1363
|
+
location: view.coordsAtPos(range.from)
|
|
1364
|
+
}))
|
|
1365
|
+
});
|
|
1379
1366
|
}
|
|
1380
|
-
})
|
|
1367
|
+
}),
|
|
1368
|
+
options.onUpdate ? trackPastedComments(options.onUpdate) : []
|
|
1381
1369
|
];
|
|
1382
1370
|
};
|
|
1371
|
+
var useComments = (view, comments2 = []) => {
|
|
1372
|
+
useEffect(() => {
|
|
1373
|
+
if (view) {
|
|
1374
|
+
view.dispatch({
|
|
1375
|
+
effects: setComments.of(comments2)
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
}, [
|
|
1379
|
+
view,
|
|
1380
|
+
comments2
|
|
1381
|
+
]);
|
|
1382
|
+
};
|
|
1383
1383
|
|
|
1384
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
1385
|
-
import {
|
|
1386
|
-
var
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
this._pos = _pos;
|
|
1397
|
-
}
|
|
1398
|
-
eq(other) {
|
|
1399
|
-
return this._pos === other._pos;
|
|
1400
|
-
}
|
|
1401
|
-
toDOM(view) {
|
|
1402
|
-
const el = document.createElement("div");
|
|
1403
|
-
el.className = "cm-hr";
|
|
1404
|
-
return el;
|
|
1405
|
-
}
|
|
1406
|
-
};
|
|
1407
|
-
var placeholderMatcher = new MatchDecorator({
|
|
1408
|
-
regexp: /^---$/g,
|
|
1409
|
-
decoration: (match, view, pos) => Decoration4.replace({
|
|
1410
|
-
widget: new HorizontalRuleWidget(pos)
|
|
1411
|
-
})
|
|
1412
|
-
});
|
|
1413
|
-
var hr = () => [
|
|
1414
|
-
styles4,
|
|
1415
|
-
ViewPlugin4.fromClass(class {
|
|
1416
|
-
constructor(view) {
|
|
1417
|
-
this.rules = placeholderMatcher.createDeco(view);
|
|
1418
|
-
}
|
|
1419
|
-
update(update2) {
|
|
1420
|
-
this.rules = placeholderMatcher.updateDeco(update2, this.rules);
|
|
1421
|
-
}
|
|
1422
|
-
}, {
|
|
1423
|
-
decorations: (instance) => instance.rules,
|
|
1424
|
-
// TODO(burdon): Is this really atomic (cursor can move into ---).
|
|
1425
|
-
provide: (plugin) => EditorView6.atomicRanges.of((view) => {
|
|
1426
|
-
return view.plugin(plugin)?.rules || Decoration4.none;
|
|
1427
|
-
})
|
|
1428
|
-
})
|
|
1429
|
-
];
|
|
1430
|
-
|
|
1431
|
-
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
1432
|
-
import { syntaxTree } from "@codemirror/language";
|
|
1433
|
-
import { StateField as StateField3 } from "@codemirror/state";
|
|
1434
|
-
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
1435
|
-
var image = (options = {}) => {
|
|
1436
|
-
return StateField3.define({
|
|
1437
|
-
create: (state) => {
|
|
1438
|
-
return Decoration5.set(buildDecorations(0, state.doc.length, state));
|
|
1439
|
-
},
|
|
1440
|
-
update: (value, tr) => {
|
|
1441
|
-
if (!tr.docChanged && !tr.selection) {
|
|
1442
|
-
return value;
|
|
1443
|
-
}
|
|
1444
|
-
const cursor = tr.state.selection.main.head;
|
|
1445
|
-
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
1446
|
-
let from = Math.min(cursor, oldCursor);
|
|
1447
|
-
let to = Math.max(cursor, oldCursor);
|
|
1448
|
-
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
1449
|
-
from = Math.min(from, fromB);
|
|
1450
|
-
to = Math.max(to, toB);
|
|
1451
|
-
});
|
|
1452
|
-
from = tr.state.doc.lineAt(from).from;
|
|
1453
|
-
to = tr.state.doc.lineAt(to).to;
|
|
1454
|
-
return value.map(tr.changes).update({
|
|
1455
|
-
filterFrom: from,
|
|
1456
|
-
filterTo: to,
|
|
1457
|
-
filter: () => false,
|
|
1458
|
-
add: buildDecorations(from, to, tr.state)
|
|
1459
|
-
});
|
|
1460
|
-
},
|
|
1461
|
-
provide: (field) => EditorView7.decorations.from(field)
|
|
1462
|
-
});
|
|
1463
|
-
};
|
|
1464
|
-
var buildDecorations = (from, to, state) => {
|
|
1465
|
-
const decorations = [];
|
|
1466
|
-
const cursor = state.selection.main.head;
|
|
1467
|
-
syntaxTree(state).iterate({
|
|
1468
|
-
enter: (node) => {
|
|
1469
|
-
if (node.name === "Image") {
|
|
1470
|
-
const urlNode = node.node.getChild("URL");
|
|
1471
|
-
if (urlNode) {
|
|
1472
|
-
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
1473
|
-
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
1474
|
-
decorations.push(Decoration5.replace({
|
|
1475
|
-
block: true,
|
|
1476
|
-
widget: new ImageWidget(url)
|
|
1477
|
-
}).range(hide ? node.from : node.to, node.to));
|
|
1478
|
-
}
|
|
1479
|
-
}
|
|
1480
|
-
},
|
|
1481
|
-
from,
|
|
1482
|
-
to
|
|
1483
|
-
});
|
|
1484
|
-
return decorations;
|
|
1485
|
-
};
|
|
1486
|
-
var ImageWidget = class extends WidgetType3 {
|
|
1487
|
-
constructor(_url) {
|
|
1488
|
-
super();
|
|
1489
|
-
this._url = _url;
|
|
1490
|
-
}
|
|
1491
|
-
eq(other) {
|
|
1492
|
-
return this._url === other._url;
|
|
1493
|
-
}
|
|
1494
|
-
toDOM(view) {
|
|
1495
|
-
const img = document.createElement("img");
|
|
1496
|
-
img.setAttribute("src", this._url);
|
|
1497
|
-
img.setAttribute("class", "cm-image");
|
|
1498
|
-
img.onload = () => img.classList.add("cm-loaded-image");
|
|
1499
|
-
return img;
|
|
1500
|
-
}
|
|
1501
|
-
};
|
|
1502
|
-
|
|
1503
|
-
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
1504
|
-
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1505
|
-
import { RangeSetBuilder } from "@codemirror/state";
|
|
1506
|
-
import { Decoration as Decoration6, WidgetType as WidgetType4, hoverTooltip as hoverTooltip2, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
1507
|
-
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1508
|
-
var link = (options = {}) => {
|
|
1509
|
-
const extensions = [
|
|
1510
|
-
ViewPlugin5.fromClass(class {
|
|
1511
|
-
constructor(view) {
|
|
1512
|
-
this.decorations = buildDecorations2(view, options);
|
|
1513
|
-
}
|
|
1514
|
-
update(update2) {
|
|
1515
|
-
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
1516
|
-
this.decorations = buildDecorations2(update2.view, options);
|
|
1517
|
-
}
|
|
1518
|
-
}
|
|
1519
|
-
}, {
|
|
1520
|
-
decorations: (v) => v.decorations
|
|
1521
|
-
})
|
|
1522
|
-
];
|
|
1523
|
-
if (options.onHover) {
|
|
1524
|
-
extensions.push(
|
|
1525
|
-
// https://codemirror.net/examples/tooltip
|
|
1526
|
-
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1527
|
-
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1528
|
-
hoverTooltip2((view, pos, side) => {
|
|
1529
|
-
const syntax = syntaxTree2(view.state).resolveInner(pos, side);
|
|
1530
|
-
let link2 = null;
|
|
1531
|
-
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
1532
|
-
link2 = node.name === "Link" ? node : null;
|
|
1533
|
-
}
|
|
1534
|
-
const url = link2 && link2.getChild("URL");
|
|
1535
|
-
if (!url || !link2) {
|
|
1536
|
-
return null;
|
|
1537
|
-
}
|
|
1538
|
-
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
1539
|
-
return {
|
|
1540
|
-
pos: link2.from,
|
|
1541
|
-
end: link2.to,
|
|
1542
|
-
above: true,
|
|
1543
|
-
create: () => {
|
|
1544
|
-
const el = document.createElement("div");
|
|
1545
|
-
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1546
|
-
options.onHover(el, urlText);
|
|
1547
|
-
return {
|
|
1548
|
-
dom: el,
|
|
1549
|
-
offset: {
|
|
1550
|
-
x: 0,
|
|
1551
|
-
y: 4
|
|
1552
|
-
}
|
|
1553
|
-
};
|
|
1554
|
-
}
|
|
1555
|
-
};
|
|
1556
|
-
})
|
|
1557
|
-
);
|
|
1558
|
-
}
|
|
1559
|
-
return extensions;
|
|
1560
|
-
};
|
|
1561
|
-
var LinkButton = class extends WidgetType4 {
|
|
1562
|
-
constructor(_url, _onAttach) {
|
|
1563
|
-
super();
|
|
1564
|
-
this._url = _url;
|
|
1565
|
-
this._onAttach = _onAttach;
|
|
1566
|
-
}
|
|
1567
|
-
eq(other) {
|
|
1568
|
-
return this._url === other._url;
|
|
1569
|
-
}
|
|
1570
|
-
toDOM(view) {
|
|
1571
|
-
const el = document.createElement("span");
|
|
1572
|
-
this._onAttach(el, this._url);
|
|
1573
|
-
return el;
|
|
1574
|
-
}
|
|
1575
|
-
};
|
|
1576
|
-
var LinkText = class extends WidgetType4 {
|
|
1577
|
-
constructor(_text, _url) {
|
|
1578
|
-
super();
|
|
1579
|
-
this._text = _text;
|
|
1580
|
-
this._url = _url;
|
|
1581
|
-
}
|
|
1582
|
-
eq(other) {
|
|
1583
|
-
return this._url === other._url;
|
|
1584
|
-
}
|
|
1585
|
-
toDOM(view) {
|
|
1586
|
-
const link2 = document.createElement("a");
|
|
1587
|
-
link2.setAttribute("class", "cm-link");
|
|
1588
|
-
link2.textContent = this._text;
|
|
1589
|
-
if (this._url) {
|
|
1590
|
-
link2.setAttribute("rel", "noreferrer");
|
|
1591
|
-
link2.setAttribute("target", "_blank");
|
|
1592
|
-
link2.href = this._url;
|
|
1593
|
-
} else {
|
|
1594
|
-
link2.onclick = () => {
|
|
1595
|
-
view.dispatch({
|
|
1596
|
-
selection: {
|
|
1597
|
-
anchor: view.posAtDOM(link2)
|
|
1598
|
-
}
|
|
1599
|
-
});
|
|
1600
|
-
};
|
|
1601
|
-
}
|
|
1602
|
-
return link2;
|
|
1603
|
-
}
|
|
1604
|
-
};
|
|
1605
|
-
var buildDecorations2 = (view, options) => {
|
|
1606
|
-
const builder = new RangeSetBuilder();
|
|
1607
|
-
const { state } = view;
|
|
1608
|
-
const cursor = state.selection.main.head;
|
|
1609
|
-
for (const { from, to } of view.visibleRanges) {
|
|
1610
|
-
syntaxTree2(state).iterate({
|
|
1611
|
-
enter: (node) => {
|
|
1612
|
-
if (node.name === "Link") {
|
|
1613
|
-
const marks2 = node.node.getChildren("LinkMark");
|
|
1614
|
-
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1615
|
-
const urlNode = node.node.getChild("URL");
|
|
1616
|
-
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1617
|
-
if (!url) {
|
|
1618
|
-
return false;
|
|
1619
|
-
}
|
|
1620
|
-
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1621
|
-
builder.add(node.from, node.to, Decoration6.replace({
|
|
1622
|
-
widget: new LinkText(text, options.link ? url : void 0)
|
|
1623
|
-
}));
|
|
1624
|
-
}
|
|
1625
|
-
if (options.onRender) {
|
|
1626
|
-
builder.add(node.to, node.to, Decoration6.replace({
|
|
1627
|
-
widget: new LinkButton(url, options.onRender)
|
|
1628
|
-
}));
|
|
1629
|
-
}
|
|
1630
|
-
return false;
|
|
1631
|
-
}
|
|
1632
|
-
},
|
|
1633
|
-
from,
|
|
1634
|
-
to
|
|
1635
|
-
});
|
|
1636
|
-
}
|
|
1637
|
-
return builder.finish();
|
|
1638
|
-
};
|
|
1639
|
-
|
|
1640
|
-
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1641
|
-
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
1642
|
-
var listener = ({ onFocus, onChange }) => {
|
|
1643
|
-
const extensions = [];
|
|
1644
|
-
onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
|
|
1645
|
-
onFocus(focused);
|
|
1646
|
-
return null;
|
|
1647
|
-
}));
|
|
1648
|
-
onChange && extensions.push(EditorView8.updateListener.of((update2) => {
|
|
1649
|
-
onChange(update2.state.doc.toString());
|
|
1650
|
-
}));
|
|
1651
|
-
return extensions;
|
|
1384
|
+
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1385
|
+
import { EditorView as EditorView6 } from "@codemirror/view";
|
|
1386
|
+
var listener = ({ onFocus, onChange }) => {
|
|
1387
|
+
const extensions = [];
|
|
1388
|
+
onFocus && extensions.push(EditorView6.focusChangeEffect.of((_, focused) => {
|
|
1389
|
+
onFocus(focused);
|
|
1390
|
+
return null;
|
|
1391
|
+
}));
|
|
1392
|
+
onChange && extensions.push(EditorView6.updateListener.of((update2) => {
|
|
1393
|
+
onChange(update2.state.doc.toString());
|
|
1394
|
+
}));
|
|
1395
|
+
return extensions;
|
|
1652
1396
|
};
|
|
1653
1397
|
|
|
1654
1398
|
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1655
1399
|
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
1656
|
-
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1400
|
+
import { history as history2, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1657
1401
|
import { markdownLanguage as markdownLanguage3, markdown } from "@codemirror/lang-markdown";
|
|
1658
1402
|
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
1659
1403
|
import { languages } from "@codemirror/language-data";
|
|
1660
1404
|
import { searchKeymap } from "@codemirror/search";
|
|
1661
1405
|
import { EditorState } from "@codemirror/state";
|
|
1662
1406
|
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
1663
|
-
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as
|
|
1407
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView7, highlightActiveLine as highlightActiveLine2, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
1664
1408
|
|
|
1665
1409
|
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1666
1410
|
import { markdownLanguage as markdownLanguage2 } from "@codemirror/lang-markdown";
|
|
@@ -1757,7 +1501,7 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1757
1501
|
markdownTags.QuoteMark,
|
|
1758
1502
|
markdownTags.EmphasisMark
|
|
1759
1503
|
],
|
|
1760
|
-
class:
|
|
1504
|
+
class: mark
|
|
1761
1505
|
},
|
|
1762
1506
|
// E.g., code block language (after ```).
|
|
1763
1507
|
{
|
|
@@ -1765,7 +1509,7 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1765
1509
|
tags.function(tags.variableName),
|
|
1766
1510
|
tags.labelName
|
|
1767
1511
|
],
|
|
1768
|
-
class:
|
|
1512
|
+
class: codeMark
|
|
1769
1513
|
},
|
|
1770
1514
|
{
|
|
1771
1515
|
tag: [
|
|
@@ -1776,27 +1520,27 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1776
1520
|
// Headings.
|
|
1777
1521
|
{
|
|
1778
1522
|
tag: tags.heading1,
|
|
1779
|
-
class: heading(1
|
|
1523
|
+
class: heading(1)
|
|
1780
1524
|
},
|
|
1781
1525
|
{
|
|
1782
1526
|
tag: tags.heading2,
|
|
1783
|
-
class: heading(2
|
|
1527
|
+
class: heading(2)
|
|
1784
1528
|
},
|
|
1785
1529
|
{
|
|
1786
1530
|
tag: tags.heading3,
|
|
1787
|
-
class: heading(3
|
|
1531
|
+
class: heading(3)
|
|
1788
1532
|
},
|
|
1789
1533
|
{
|
|
1790
1534
|
tag: tags.heading4,
|
|
1791
|
-
class: heading(4
|
|
1535
|
+
class: heading(4)
|
|
1792
1536
|
},
|
|
1793
1537
|
{
|
|
1794
1538
|
tag: tags.heading5,
|
|
1795
|
-
class: heading(5
|
|
1539
|
+
class: heading(5)
|
|
1796
1540
|
},
|
|
1797
1541
|
{
|
|
1798
1542
|
tag: tags.heading6,
|
|
1799
|
-
class: heading(6
|
|
1543
|
+
class: heading(6)
|
|
1800
1544
|
},
|
|
1801
1545
|
// Emphasis.
|
|
1802
1546
|
{
|
|
@@ -1822,7 +1566,7 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1822
1566
|
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
1823
1567
|
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
1824
1568
|
// the code, but all other CSS properties will be inherited.
|
|
1825
|
-
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
1569
|
+
// IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
|
|
1826
1570
|
{
|
|
1827
1571
|
tag: [
|
|
1828
1572
|
markdownTags.CodeText,
|
|
@@ -1851,21 +1595,21 @@ var markdownHighlightStyle = (readonly) => {
|
|
|
1851
1595
|
};
|
|
1852
1596
|
|
|
1853
1597
|
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1854
|
-
var markdownBundle = ({
|
|
1598
|
+
var markdownBundle = ({ themeMode, placeholder: _placeholder }) => {
|
|
1855
1599
|
return [
|
|
1856
1600
|
EditorState.allowMultipleSelections.of(true),
|
|
1857
1601
|
EditorState.tabSize.of(2),
|
|
1858
|
-
|
|
1859
|
-
|
|
1602
|
+
EditorView7.lineWrapping,
|
|
1603
|
+
// https://github.com/codemirror/basic-setup
|
|
1860
1604
|
bracketMatching2(),
|
|
1861
1605
|
closeBrackets2(),
|
|
1862
1606
|
crosshairCursor(),
|
|
1863
1607
|
dropCursor(),
|
|
1864
1608
|
drawSelection2(),
|
|
1865
|
-
|
|
1866
|
-
|
|
1609
|
+
highlightActiveLine2(),
|
|
1610
|
+
history2(),
|
|
1867
1611
|
indentOnInput(),
|
|
1868
|
-
_placeholder
|
|
1612
|
+
_placeholder ? placeholder2(_placeholder) : [],
|
|
1869
1613
|
// Main extension.
|
|
1870
1614
|
// https://github.com/codemirror/lang-markdown
|
|
1871
1615
|
// https://codemirror.net/5/mode/markdown/index.html (demo).
|
|
@@ -1887,58 +1631,575 @@ var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
|
1887
1631
|
// https://github.com/codemirror/theme-one-dark
|
|
1888
1632
|
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
|
|
1889
1633
|
// Custom styles.
|
|
1890
|
-
syntaxHighlighting2(markdownHighlightStyle(
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1634
|
+
syntaxHighlighting2(markdownHighlightStyle()),
|
|
1635
|
+
keymap4.of([
|
|
1636
|
+
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
1637
|
+
indentWithTab,
|
|
1638
|
+
// https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
|
|
1639
|
+
...closeBracketsKeymap,
|
|
1640
|
+
// https://codemirror.net/docs/ref/#commands.historyKeymap
|
|
1641
|
+
...historyKeymap,
|
|
1642
|
+
// https://codemirror.net/docs/ref/#search.searchKeymap
|
|
1643
|
+
...searchKeymap,
|
|
1644
|
+
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
1645
|
+
...standardKeymap
|
|
1646
|
+
])
|
|
1647
|
+
];
|
|
1648
|
+
};
|
|
1905
1649
|
|
|
1906
|
-
// packages/ui/react-ui-editor/src/extensions/
|
|
1907
|
-
import {
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1650
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/code.ts
|
|
1651
|
+
import { syntaxTree } from "@codemirror/language";
|
|
1652
|
+
import { RangeSetBuilder } from "@codemirror/state";
|
|
1653
|
+
import { ViewPlugin as ViewPlugin3, EditorView as EditorView8, Decoration as Decoration3, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1654
|
+
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
1655
|
+
var styles3 = EditorView8.baseTheme({
|
|
1656
|
+
"& .cm-code": {
|
|
1657
|
+
paddingInline: "1rem !important",
|
|
1658
|
+
fontFamily: getToken("fontFamily.mono", []).join(",")
|
|
1659
|
+
},
|
|
1660
|
+
"& .cm-codeblock": {
|
|
1661
|
+
display: "inline-block",
|
|
1662
|
+
width: "100%",
|
|
1663
|
+
position: "relative",
|
|
1664
|
+
"&::after": {
|
|
1665
|
+
content: '""',
|
|
1666
|
+
position: "absolute",
|
|
1667
|
+
inset: 0
|
|
1668
|
+
}
|
|
1669
|
+
},
|
|
1670
|
+
"&light .cm-codeblock": {
|
|
1671
|
+
"&::after": {
|
|
1672
|
+
background: getToken("extend.semanticColors.input.light"),
|
|
1673
|
+
mixBlendMode: "darken"
|
|
1674
|
+
}
|
|
1675
|
+
},
|
|
1676
|
+
"&dark .cm-codeblock": {
|
|
1677
|
+
"&::after": {
|
|
1678
|
+
background: getToken("extend.semanticColors.input.dark"),
|
|
1679
|
+
mixBlendMode: "lighten"
|
|
1680
|
+
}
|
|
1681
|
+
},
|
|
1682
|
+
"& .cm-codeblock-first": {
|
|
1683
|
+
"&::after": {
|
|
1684
|
+
borderTopLeftRadius: ".5rem",
|
|
1685
|
+
borderTopRightRadius: ".5rem"
|
|
1686
|
+
}
|
|
1687
|
+
},
|
|
1688
|
+
"& .cm-codeblock-last": {
|
|
1689
|
+
"&::after": {
|
|
1690
|
+
borderBottomLeftRadius: ".5rem",
|
|
1691
|
+
borderBottomRightRadius: ".5rem"
|
|
1692
|
+
}
|
|
1693
|
+
}
|
|
1694
|
+
});
|
|
1695
|
+
var getLineRange = (lines, from, to) => {
|
|
1696
|
+
const start = lines.findIndex((line) => line.from >= from);
|
|
1697
|
+
const end = lines.findIndex((line) => line.to >= to);
|
|
1698
|
+
return [
|
|
1699
|
+
start,
|
|
1700
|
+
end
|
|
1701
|
+
];
|
|
1702
|
+
};
|
|
1703
|
+
var LineWidget = class extends WidgetType2 {
|
|
1704
|
+
constructor(_className) {
|
|
1705
|
+
super();
|
|
1706
|
+
this._className = _className;
|
|
1707
|
+
}
|
|
1708
|
+
toDOM() {
|
|
1709
|
+
const el = document.createElement("span");
|
|
1710
|
+
el.innerHTML = " ";
|
|
1711
|
+
el.className = mx2("cm-code cm-codeblock", this._className);
|
|
1712
|
+
return el;
|
|
1713
|
+
}
|
|
1714
|
+
};
|
|
1715
|
+
var top = new LineWidget("cm-codeblock-first");
|
|
1716
|
+
var bottom = new LineWidget("cm-codeblock-last");
|
|
1717
|
+
var buildDecorations = (view) => {
|
|
1718
|
+
const builder = new RangeSetBuilder();
|
|
1719
|
+
const { state } = view;
|
|
1720
|
+
const blocks = view.viewportLineBlocks;
|
|
1721
|
+
const cursor = state.selection.main.head;
|
|
1722
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1723
|
+
syntaxTree(state).iterate({
|
|
1724
|
+
enter: (node) => {
|
|
1725
|
+
if (node.name === "FencedCode") {
|
|
1726
|
+
const edit = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
|
|
1727
|
+
const range = getLineRange(blocks, node.from, node.to);
|
|
1728
|
+
for (let i = range[0]; i <= range[1]; i++) {
|
|
1729
|
+
const block = blocks[i];
|
|
1730
|
+
if (!edit && (i === range[0] || i === range[1])) {
|
|
1731
|
+
builder.add(block.from, block.to, Decoration3.replace({
|
|
1732
|
+
widget: i === range[0] ? top : bottom
|
|
1733
|
+
}));
|
|
1734
|
+
} else {
|
|
1735
|
+
builder.add(block.from, block.from, Decoration3.line({
|
|
1736
|
+
class: mx2("cm-code cm-codeblock")
|
|
1737
|
+
}));
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
},
|
|
1742
|
+
from,
|
|
1743
|
+
to
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
return builder.finish();
|
|
1747
|
+
};
|
|
1748
|
+
var code2 = () => {
|
|
1749
|
+
return [
|
|
1750
|
+
ViewPlugin3.fromClass(class {
|
|
1751
|
+
constructor(view) {
|
|
1752
|
+
this.decorations = buildDecorations(view);
|
|
1753
|
+
}
|
|
1754
|
+
update(update2) {
|
|
1755
|
+
this.decorations = buildDecorations(update2.view);
|
|
1756
|
+
}
|
|
1757
|
+
}, {
|
|
1758
|
+
decorations: (value) => value.decorations
|
|
1759
|
+
}),
|
|
1760
|
+
styles3
|
|
1761
|
+
];
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/formatting.ts
|
|
1765
|
+
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1766
|
+
import { RangeSetBuilder as RangeSetBuilder2 } from "@codemirror/state";
|
|
1767
|
+
import { Decoration as Decoration4, keymap as keymap5, ViewPlugin as ViewPlugin4 } from "@codemirror/view";
|
|
1768
|
+
var setHeading = (level) => (view) => {
|
|
1769
|
+
const { selection: { ranges }, doc } = view.state;
|
|
1770
|
+
const changes = [];
|
|
1771
|
+
for (const range of ranges) {
|
|
1772
|
+
const { number } = doc.lineAt(range.anchor);
|
|
1773
|
+
const { from } = doc.line(number);
|
|
1774
|
+
const line = doc.sliceString(from, from + 7);
|
|
1775
|
+
const [_, marks2, spaces] = line.match(/(#+)(\s+)/) ?? [];
|
|
1776
|
+
const current = marks2?.length ?? 0;
|
|
1777
|
+
if (level !== current) {
|
|
1778
|
+
changes.push({
|
|
1779
|
+
from,
|
|
1780
|
+
to: from + current + (spaces?.length ?? 0),
|
|
1781
|
+
insert: "#".repeat(level) + (level > 0 ? " " : "")
|
|
1782
|
+
});
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
if (changes.length) {
|
|
1786
|
+
view.dispatch({
|
|
1787
|
+
changes
|
|
1788
|
+
});
|
|
1789
|
+
}
|
|
1790
|
+
return true;
|
|
1791
|
+
};
|
|
1792
|
+
var toggleStyle = (mark2) => (view) => {
|
|
1793
|
+
const { ranges } = view.state.selection;
|
|
1794
|
+
for (const range of ranges) {
|
|
1795
|
+
if (range.from === range.to) {
|
|
1796
|
+
return false;
|
|
1797
|
+
}
|
|
1798
|
+
view.dispatch({
|
|
1799
|
+
changes: [
|
|
1800
|
+
{
|
|
1801
|
+
from: range.from,
|
|
1802
|
+
insert: mark2
|
|
1803
|
+
},
|
|
1804
|
+
{
|
|
1805
|
+
from: range.to,
|
|
1806
|
+
insert: mark2
|
|
1807
|
+
}
|
|
1808
|
+
]
|
|
1809
|
+
});
|
|
1810
|
+
}
|
|
1811
|
+
return true;
|
|
1812
|
+
};
|
|
1813
|
+
var toggleBold = toggleStyle("**");
|
|
1814
|
+
var toggleItalic = toggleStyle("_");
|
|
1815
|
+
var toggleStrikethrough = toggleStyle("~~");
|
|
1816
|
+
var toggleList = (view) => {
|
|
1817
|
+
};
|
|
1818
|
+
var formatting = (options = {}) => {
|
|
1819
|
+
return [
|
|
1820
|
+
keymap5.of([
|
|
1821
|
+
{
|
|
1822
|
+
key: "meta-b",
|
|
1823
|
+
run: toggleBold
|
|
1824
|
+
}
|
|
1825
|
+
]),
|
|
1826
|
+
styling()
|
|
1827
|
+
];
|
|
1828
|
+
};
|
|
1829
|
+
var styling = () => {
|
|
1830
|
+
const buildDecorations7 = (view) => {
|
|
1831
|
+
const builder = new RangeSetBuilder2();
|
|
1832
|
+
const { state } = view;
|
|
1833
|
+
const cursor = state.selection.main.head;
|
|
1834
|
+
const replace = (node, marks2) => {
|
|
1835
|
+
if (cursor <= node.from || cursor >= node.to) {
|
|
1836
|
+
for (const mark2 of marks2) {
|
|
1837
|
+
builder.add(mark2.from, mark2.to, Decoration4.replace({}));
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1842
|
+
syntaxTree2(state).iterate({
|
|
1843
|
+
enter: (node) => {
|
|
1844
|
+
switch (node.name) {
|
|
1845
|
+
case "Emphasis":
|
|
1846
|
+
case "StrongEmphasis": {
|
|
1847
|
+
const marks2 = node.node.getChildren("EmphasisMark");
|
|
1848
|
+
replace(node, marks2);
|
|
1849
|
+
break;
|
|
1850
|
+
}
|
|
1851
|
+
case "Strikethrough": {
|
|
1852
|
+
const marks2 = node.node.getChildren("StrikethroughMark");
|
|
1853
|
+
replace(node, marks2);
|
|
1854
|
+
break;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
},
|
|
1858
|
+
from,
|
|
1859
|
+
to
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
return builder.finish();
|
|
1863
|
+
};
|
|
1864
|
+
return [
|
|
1865
|
+
ViewPlugin4.fromClass(class {
|
|
1866
|
+
constructor(view) {
|
|
1867
|
+
this.decorations = buildDecorations7(view);
|
|
1868
|
+
}
|
|
1869
|
+
update(update2) {
|
|
1870
|
+
this.decorations = buildDecorations7(update2.view);
|
|
1871
|
+
}
|
|
1872
|
+
}, {
|
|
1873
|
+
decorations: (value) => value.decorations
|
|
1874
|
+
})
|
|
1875
|
+
];
|
|
1876
|
+
};
|
|
1877
|
+
|
|
1878
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/heading.ts
|
|
1879
|
+
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1880
|
+
import { RangeSetBuilder as RangeSetBuilder3 } from "@codemirror/state";
|
|
1881
|
+
import { Decoration as Decoration5, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
1882
|
+
var buildDecorations2 = (view) => {
|
|
1883
|
+
const builder = new RangeSetBuilder3();
|
|
1884
|
+
const { state } = view;
|
|
1885
|
+
const cursor = state.selection.main.head;
|
|
1886
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1887
|
+
syntaxTree3(state).iterate({
|
|
1888
|
+
enter: (node) => {
|
|
1889
|
+
switch (node.name) {
|
|
1890
|
+
case "ATXHeading1":
|
|
1891
|
+
case "ATXHeading2":
|
|
1892
|
+
case "ATXHeading3":
|
|
1893
|
+
case "ATXHeading4":
|
|
1894
|
+
case "ATXHeading5":
|
|
1895
|
+
case "ATXHeading6": {
|
|
1896
|
+
const mark2 = node.node.getChild("HeaderMark");
|
|
1897
|
+
if (mark2) {
|
|
1898
|
+
if (!view.hasFocus || view.state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1899
|
+
builder.add(mark2.from, mark2.to + 1, Decoration5.replace({}));
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
},
|
|
1905
|
+
from,
|
|
1906
|
+
to
|
|
1907
|
+
});
|
|
1908
|
+
}
|
|
1909
|
+
return builder.finish();
|
|
1910
|
+
};
|
|
1911
|
+
var heading2 = () => {
|
|
1912
|
+
return [
|
|
1913
|
+
ViewPlugin5.fromClass(class {
|
|
1914
|
+
constructor(view) {
|
|
1915
|
+
this.decorations = buildDecorations2(view);
|
|
1916
|
+
}
|
|
1917
|
+
update(update2) {
|
|
1918
|
+
this.decorations = buildDecorations2(update2.view);
|
|
1919
|
+
}
|
|
1920
|
+
}, {
|
|
1921
|
+
decorations: (value) => value.decorations
|
|
1922
|
+
})
|
|
1923
|
+
];
|
|
1924
|
+
};
|
|
1925
|
+
|
|
1926
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/hr.ts
|
|
1927
|
+
import { syntaxTree as syntaxTree4 } from "@codemirror/language";
|
|
1928
|
+
import { RangeSetBuilder as RangeSetBuilder4 } from "@codemirror/state";
|
|
1929
|
+
import { Decoration as Decoration6, EditorView as EditorView9, ViewPlugin as ViewPlugin6, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
1930
|
+
var styles4 = EditorView9.baseTheme({
|
|
1931
|
+
"& .cm-hr": {
|
|
1932
|
+
// Note that block-level decorations should not have vertical margins,
|
|
1933
|
+
borderTop: `1px solid ${getToken("extend.colors.neutral.200")}`
|
|
1934
|
+
}
|
|
1935
|
+
});
|
|
1936
|
+
var HorizontalRuleWidget = class extends WidgetType3 {
|
|
1937
|
+
toDOM() {
|
|
1938
|
+
const el = document.createElement("div");
|
|
1939
|
+
el.className = "cm-hr";
|
|
1940
|
+
return el;
|
|
1941
|
+
}
|
|
1942
|
+
};
|
|
1943
|
+
var decoration = Decoration6.replace({
|
|
1944
|
+
widget: new HorizontalRuleWidget()
|
|
1945
|
+
});
|
|
1946
|
+
var buildDecorations3 = (view) => {
|
|
1947
|
+
const builder = new RangeSetBuilder4();
|
|
1948
|
+
const { state } = view;
|
|
1949
|
+
const cursor = state.selection.main.head;
|
|
1950
|
+
for (const { from, to } of view.visibleRanges) {
|
|
1951
|
+
syntaxTree4(state).iterate({
|
|
1952
|
+
enter: (node) => {
|
|
1953
|
+
if (node.name === "HorizontalRule") {
|
|
1954
|
+
if (cursor <= node.from || cursor >= node.to) {
|
|
1955
|
+
builder.add(node.from, node.to, decoration);
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
},
|
|
1959
|
+
from,
|
|
1960
|
+
to
|
|
1961
|
+
});
|
|
1962
|
+
}
|
|
1963
|
+
return builder.finish();
|
|
1964
|
+
};
|
|
1965
|
+
var hr = () => {
|
|
1966
|
+
return [
|
|
1967
|
+
ViewPlugin6.fromClass(class {
|
|
1968
|
+
constructor(view) {
|
|
1969
|
+
this.decorations = buildDecorations3(view);
|
|
1970
|
+
}
|
|
1971
|
+
update(update2) {
|
|
1972
|
+
this.decorations = buildDecorations3(update2.view);
|
|
1973
|
+
}
|
|
1974
|
+
}, {
|
|
1975
|
+
decorations: (value) => value.decorations
|
|
1976
|
+
}),
|
|
1977
|
+
styles4
|
|
1978
|
+
];
|
|
1979
|
+
};
|
|
1980
|
+
|
|
1981
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/image.ts
|
|
1982
|
+
import { syntaxTree as syntaxTree5 } from "@codemirror/language";
|
|
1983
|
+
import { StateField as StateField3 } from "@codemirror/state";
|
|
1984
|
+
import { Decoration as Decoration7, EditorView as EditorView10, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
1985
|
+
var image = (options = {}) => {
|
|
1986
|
+
return StateField3.define({
|
|
1987
|
+
create: (state) => {
|
|
1988
|
+
return Decoration7.set(buildDecorations4(0, state.doc.length, state));
|
|
1989
|
+
},
|
|
1990
|
+
update: (value, tr) => {
|
|
1991
|
+
if (!tr.docChanged && !tr.selection) {
|
|
1992
|
+
return value;
|
|
1993
|
+
}
|
|
1994
|
+
const cursor = tr.state.selection.main.head;
|
|
1995
|
+
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
1996
|
+
let from = Math.min(cursor, oldCursor);
|
|
1997
|
+
let to = Math.max(cursor, oldCursor);
|
|
1998
|
+
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
1999
|
+
from = Math.min(from, fromB);
|
|
2000
|
+
to = Math.max(to, toB);
|
|
2001
|
+
});
|
|
2002
|
+
from = tr.state.doc.lineAt(from).from;
|
|
2003
|
+
to = tr.state.doc.lineAt(to).to;
|
|
2004
|
+
return value.map(tr.changes).update({
|
|
2005
|
+
filterFrom: from,
|
|
2006
|
+
filterTo: to,
|
|
2007
|
+
filter: () => false,
|
|
2008
|
+
add: buildDecorations4(from, to, tr.state)
|
|
2009
|
+
});
|
|
2010
|
+
},
|
|
2011
|
+
provide: (field) => EditorView10.decorations.from(field)
|
|
2012
|
+
});
|
|
2013
|
+
};
|
|
2014
|
+
var buildDecorations4 = (from, to, state) => {
|
|
2015
|
+
const decorations = [];
|
|
2016
|
+
const cursor = state.selection.main.head;
|
|
2017
|
+
syntaxTree5(state).iterate({
|
|
2018
|
+
enter: (node) => {
|
|
2019
|
+
if (node.name === "Image") {
|
|
2020
|
+
const urlNode = node.node.getChild("URL");
|
|
2021
|
+
if (urlNode) {
|
|
2022
|
+
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
2023
|
+
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
2024
|
+
decorations.push(Decoration7.replace({
|
|
2025
|
+
block: true,
|
|
2026
|
+
widget: new ImageWidget(url)
|
|
2027
|
+
}).range(hide ? node.from : node.to, node.to));
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
},
|
|
2031
|
+
from,
|
|
2032
|
+
to
|
|
2033
|
+
});
|
|
2034
|
+
return decorations;
|
|
2035
|
+
};
|
|
2036
|
+
var ImageWidget = class extends WidgetType4 {
|
|
2037
|
+
constructor(_url) {
|
|
2038
|
+
super();
|
|
2039
|
+
this._url = _url;
|
|
2040
|
+
}
|
|
2041
|
+
eq(other) {
|
|
2042
|
+
return this._url === other._url;
|
|
2043
|
+
}
|
|
2044
|
+
toDOM(view) {
|
|
2045
|
+
const img = document.createElement("img");
|
|
2046
|
+
img.setAttribute("src", this._url);
|
|
2047
|
+
img.setAttribute("class", "cm-image");
|
|
2048
|
+
img.onload = () => img.classList.add("cm-loaded-image");
|
|
2049
|
+
return img;
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
|
|
2053
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/link.ts
|
|
2054
|
+
import { syntaxTree as syntaxTree6 } from "@codemirror/language";
|
|
2055
|
+
import { RangeSetBuilder as RangeSetBuilder5 } from "@codemirror/state";
|
|
2056
|
+
import { hoverTooltip as hoverTooltip2, Decoration as Decoration8, ViewPlugin as ViewPlugin7, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
2057
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
2058
|
+
var link = (options = {}) => {
|
|
2059
|
+
const extensions = [
|
|
2060
|
+
ViewPlugin7.fromClass(class {
|
|
2061
|
+
constructor(view) {
|
|
2062
|
+
this.decorations = buildDecorations5(view, options);
|
|
2063
|
+
}
|
|
2064
|
+
update(update2) {
|
|
2065
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
2066
|
+
this.decorations = buildDecorations5(update2.view, options);
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}, {
|
|
2070
|
+
decorations: (value) => value.decorations
|
|
2071
|
+
})
|
|
2072
|
+
];
|
|
2073
|
+
if (options.onHover) {
|
|
2074
|
+
extensions.push(
|
|
2075
|
+
// https://codemirror.net/examples/tooltip
|
|
2076
|
+
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
2077
|
+
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
2078
|
+
hoverTooltip2((view, pos, side) => {
|
|
2079
|
+
const syntax = syntaxTree6(view.state).resolveInner(pos, side);
|
|
2080
|
+
let link2 = null;
|
|
2081
|
+
for (let i = 0, node = syntax; !link2 && node && i < 5; node = node.parent, i++) {
|
|
2082
|
+
link2 = node.name === "Link" ? node : null;
|
|
2083
|
+
}
|
|
2084
|
+
const url = link2 && link2.getChild("URL");
|
|
2085
|
+
if (!url || !link2) {
|
|
2086
|
+
return null;
|
|
2087
|
+
}
|
|
2088
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
2089
|
+
return {
|
|
2090
|
+
pos: link2.from,
|
|
2091
|
+
end: link2.to,
|
|
2092
|
+
above: true,
|
|
2093
|
+
create: () => {
|
|
2094
|
+
const el = document.createElement("div");
|
|
2095
|
+
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
2096
|
+
options.onHover(el, urlText);
|
|
2097
|
+
return {
|
|
2098
|
+
dom: el,
|
|
2099
|
+
offset: {
|
|
2100
|
+
x: 0,
|
|
2101
|
+
y: 4
|
|
2102
|
+
}
|
|
2103
|
+
};
|
|
2104
|
+
}
|
|
2105
|
+
};
|
|
2106
|
+
})
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
return extensions;
|
|
2110
|
+
};
|
|
2111
|
+
var LinkButton = class extends WidgetType5 {
|
|
2112
|
+
constructor(_url, _onAttach) {
|
|
2113
|
+
super();
|
|
2114
|
+
this._url = _url;
|
|
2115
|
+
this._onAttach = _onAttach;
|
|
2116
|
+
}
|
|
2117
|
+
eq(other) {
|
|
2118
|
+
return this._url === other._url;
|
|
2119
|
+
}
|
|
2120
|
+
toDOM(view) {
|
|
2121
|
+
const el = document.createElement("span");
|
|
2122
|
+
this._onAttach(el, this._url);
|
|
2123
|
+
return el;
|
|
2124
|
+
}
|
|
2125
|
+
};
|
|
2126
|
+
var LinkText = class extends WidgetType5 {
|
|
2127
|
+
constructor(_text, _url) {
|
|
2128
|
+
super();
|
|
2129
|
+
this._text = _text;
|
|
2130
|
+
this._url = _url;
|
|
2131
|
+
}
|
|
2132
|
+
eq(other) {
|
|
2133
|
+
return this._url === other._url;
|
|
2134
|
+
}
|
|
2135
|
+
toDOM(view) {
|
|
2136
|
+
const link2 = document.createElement("a");
|
|
2137
|
+
link2.setAttribute("class", "cm-link");
|
|
2138
|
+
link2.textContent = this._text;
|
|
2139
|
+
if (this._url) {
|
|
2140
|
+
link2.setAttribute("rel", "noreferrer");
|
|
2141
|
+
link2.setAttribute("target", "_blank");
|
|
2142
|
+
link2.href = this._url;
|
|
2143
|
+
} else {
|
|
2144
|
+
link2.onclick = () => {
|
|
2145
|
+
view.dispatch({
|
|
2146
|
+
selection: {
|
|
2147
|
+
anchor: view.posAtDOM(link2)
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
return link2;
|
|
2153
|
+
}
|
|
2154
|
+
};
|
|
2155
|
+
var buildDecorations5 = (view, options) => {
|
|
2156
|
+
const builder = new RangeSetBuilder5();
|
|
2157
|
+
const { state } = view;
|
|
2158
|
+
const cursor = state.selection.main.head;
|
|
2159
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2160
|
+
syntaxTree6(state).iterate({
|
|
2161
|
+
enter: (node) => {
|
|
2162
|
+
if (node.name === "Link") {
|
|
2163
|
+
const marks2 = node.node.getChildren("LinkMark");
|
|
2164
|
+
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
2165
|
+
const urlNode = node.node.getChild("URL");
|
|
2166
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
2167
|
+
if (!url) {
|
|
2168
|
+
return false;
|
|
2169
|
+
}
|
|
2170
|
+
if (!view.hasFocus || state.readOnly || cursor < node.from || cursor > node.to) {
|
|
2171
|
+
builder.add(node.from, node.to, Decoration8.replace({
|
|
2172
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
2173
|
+
}));
|
|
2174
|
+
}
|
|
2175
|
+
if (options.onRender) {
|
|
2176
|
+
builder.add(node.to, node.to, Decoration8.replace({
|
|
2177
|
+
widget: new LinkButton(url, options.onRender)
|
|
2178
|
+
}));
|
|
2179
|
+
}
|
|
2180
|
+
return false;
|
|
1916
2181
|
}
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
};
|
|
1924
|
-
}
|
|
1925
|
-
]
|
|
1926
|
-
});
|
|
2182
|
+
},
|
|
2183
|
+
from,
|
|
2184
|
+
to
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
return builder.finish();
|
|
1927
2188
|
};
|
|
1928
2189
|
|
|
1929
|
-
// packages/ui/react-ui-editor/src/extensions/table.ts
|
|
1930
|
-
import { syntaxTree as
|
|
1931
|
-
import { RangeSetBuilder as
|
|
1932
|
-
import { Decoration as
|
|
2190
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/table.ts
|
|
2191
|
+
import { syntaxTree as syntaxTree7 } from "@codemirror/language";
|
|
2192
|
+
import { RangeSetBuilder as RangeSetBuilder6, StateField as StateField4 } from "@codemirror/state";
|
|
2193
|
+
import { Decoration as Decoration9, EditorView as EditorView11, WidgetType as WidgetType6 } from "@codemirror/view";
|
|
1933
2194
|
var table = (options = {}) => {
|
|
1934
2195
|
return StateField4.define({
|
|
1935
2196
|
create: (state) => update(state, options),
|
|
1936
2197
|
update: (_, tr) => update(tr.state, options),
|
|
1937
|
-
provide: (field) =>
|
|
2198
|
+
provide: (field) => EditorView11.decorations.from(field)
|
|
1938
2199
|
});
|
|
1939
2200
|
};
|
|
1940
2201
|
var update = (state, options) => {
|
|
1941
|
-
const builder = new
|
|
2202
|
+
const builder = new RangeSetBuilder6();
|
|
1942
2203
|
const cursor = state.selection.main.head;
|
|
1943
2204
|
const tables = [];
|
|
1944
2205
|
const getTable = () => tables[tables.length - 1];
|
|
@@ -1946,7 +2207,7 @@ var update = (state, options) => {
|
|
|
1946
2207
|
const table2 = getTable();
|
|
1947
2208
|
return table2.rows?.[table2.rows.length - 1];
|
|
1948
2209
|
};
|
|
1949
|
-
|
|
2210
|
+
syntaxTree7(state).iterate({
|
|
1950
2211
|
enter: (node) => {
|
|
1951
2212
|
switch (node.name) {
|
|
1952
2213
|
case "Table": {
|
|
@@ -1978,16 +2239,16 @@ var update = (state, options) => {
|
|
|
1978
2239
|
});
|
|
1979
2240
|
tables.forEach((table2) => {
|
|
1980
2241
|
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1981
|
-
hide && builder.add(table2.from, table2.to,
|
|
2242
|
+
hide && builder.add(table2.from, table2.to, Decoration9.replace({
|
|
1982
2243
|
widget: new TableWidget(table2)
|
|
1983
2244
|
}));
|
|
1984
|
-
builder.add(table2.from, table2.to,
|
|
2245
|
+
builder.add(table2.from, table2.to, Decoration9.mark({
|
|
1985
2246
|
class: "cm-table"
|
|
1986
2247
|
}));
|
|
1987
2248
|
});
|
|
1988
2249
|
return builder.finish();
|
|
1989
2250
|
};
|
|
1990
|
-
var TableWidget = class extends
|
|
2251
|
+
var TableWidget = class extends WidgetType6 {
|
|
1991
2252
|
constructor(_table) {
|
|
1992
2253
|
super();
|
|
1993
2254
|
this._table = _table;
|
|
@@ -2024,92 +2285,139 @@ var TableWidget = class extends WidgetType5 {
|
|
|
2024
2285
|
}
|
|
2025
2286
|
};
|
|
2026
2287
|
|
|
2027
|
-
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
2028
|
-
import {
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2288
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/tasklist.ts
|
|
2289
|
+
import { syntaxTree as syntaxTree8 } from "@codemirror/language";
|
|
2290
|
+
import { RangeSetBuilder as RangeSetBuilder7 } from "@codemirror/state";
|
|
2291
|
+
import { EditorView as EditorView12, Decoration as Decoration10, WidgetType as WidgetType7, ViewPlugin as ViewPlugin8 } from "@codemirror/view";
|
|
2292
|
+
var styles5 = EditorView12.baseTheme({
|
|
2293
|
+
"& .cm-task": {
|
|
2294
|
+
color: getToken("extend.colors.blue.500")
|
|
2295
|
+
},
|
|
2296
|
+
"& .cm-task-checkbox": {
|
|
2297
|
+
marginLeft: "4px",
|
|
2298
|
+
marginRight: "4px"
|
|
2033
2299
|
}
|
|
2034
2300
|
});
|
|
2035
|
-
var CheckboxWidget = class extends
|
|
2036
|
-
constructor(_checked
|
|
2301
|
+
var CheckboxWidget = class extends WidgetType7 {
|
|
2302
|
+
constructor(_checked) {
|
|
2037
2303
|
super();
|
|
2038
2304
|
this._checked = _checked;
|
|
2039
|
-
this._indent = _indent;
|
|
2040
|
-
this._onCheck = _onCheck;
|
|
2041
2305
|
}
|
|
2042
2306
|
eq(other) {
|
|
2043
|
-
return this._checked === other._checked
|
|
2307
|
+
return this._checked === other._checked;
|
|
2044
2308
|
}
|
|
2045
2309
|
toDOM(view) {
|
|
2046
|
-
const
|
|
2047
|
-
|
|
2048
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
2049
|
-
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
2050
|
-
const input = wrap.appendChild(document.createElement("input"));
|
|
2310
|
+
const input = document.createElement("input");
|
|
2311
|
+
input.className = "cm-task-checkbox";
|
|
2051
2312
|
input.type = "checkbox";
|
|
2052
2313
|
input.checked = this._checked;
|
|
2053
|
-
if (
|
|
2314
|
+
if (view.state.readOnly) {
|
|
2054
2315
|
input.setAttribute("disabled", "true");
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2316
|
+
} else {
|
|
2317
|
+
input.onmousedown = (event) => {
|
|
2318
|
+
const pos = view.posAtDOM(input);
|
|
2319
|
+
const text = view.state.sliceDoc(pos, pos + 3);
|
|
2320
|
+
if (text === (this._checked ? "[x]" : "[ ]")) {
|
|
2321
|
+
view.dispatch({
|
|
2322
|
+
changes: {
|
|
2323
|
+
from: pos + 1,
|
|
2324
|
+
to: pos + 2,
|
|
2325
|
+
insert: this._checked ? " " : "x"
|
|
2326
|
+
}
|
|
2327
|
+
});
|
|
2328
|
+
event.preventDefault();
|
|
2329
|
+
}
|
|
2060
2330
|
};
|
|
2061
2331
|
}
|
|
2062
|
-
return
|
|
2332
|
+
return input;
|
|
2063
2333
|
}
|
|
2064
2334
|
ignoreEvent() {
|
|
2065
2335
|
return false;
|
|
2066
2336
|
}
|
|
2067
2337
|
};
|
|
2338
|
+
var checkedDecoration = Decoration10.replace({
|
|
2339
|
+
widget: new CheckboxWidget(true)
|
|
2340
|
+
});
|
|
2341
|
+
var uncheckedDecoration = Decoration10.replace({
|
|
2342
|
+
widget: new CheckboxWidget(false)
|
|
2343
|
+
});
|
|
2344
|
+
var buildDecorations6 = (view) => {
|
|
2345
|
+
const builder = new RangeSetBuilder7();
|
|
2346
|
+
const { state } = view;
|
|
2347
|
+
const cursor = state.selection.main.head;
|
|
2348
|
+
for (const { from, to } of view.visibleRanges) {
|
|
2349
|
+
syntaxTree8(state).iterate({
|
|
2350
|
+
enter: (node) => {
|
|
2351
|
+
if (node.name === "TaskMarker") {
|
|
2352
|
+
if (cursor < node.from || cursor > node.to) {
|
|
2353
|
+
const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
|
|
2354
|
+
builder.add(node.from - 2, node.from - 1, Decoration10.mark({
|
|
2355
|
+
class: "cm-task"
|
|
2356
|
+
}));
|
|
2357
|
+
builder.add(node.from, node.to, checked ? checkedDecoration : uncheckedDecoration);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
},
|
|
2361
|
+
from,
|
|
2362
|
+
to
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
return builder.finish();
|
|
2366
|
+
};
|
|
2068
2367
|
var tasklist = (options = {}) => {
|
|
2069
|
-
const taskMatcher = new MatchDecorator2({
|
|
2070
|
-
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
2071
|
-
decoration: (match, view, pos) => {
|
|
2072
|
-
const indent = Math.floor(match[1].length / 2);
|
|
2073
|
-
const checked = match[2] === "x" || match[2] === "X";
|
|
2074
|
-
return Decoration8.replace({
|
|
2075
|
-
widget: new CheckboxWidget(checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
2076
|
-
const idx = pos + match[0].indexOf("[") + 1;
|
|
2077
|
-
view.dispatch({
|
|
2078
|
-
changes: {
|
|
2079
|
-
from: idx,
|
|
2080
|
-
to: idx + 1,
|
|
2081
|
-
insert: checked2 ? "x" : " "
|
|
2082
|
-
},
|
|
2083
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
2084
|
-
selection: {
|
|
2085
|
-
anchor: idx + 3
|
|
2086
|
-
}
|
|
2087
|
-
});
|
|
2088
|
-
})
|
|
2089
|
-
});
|
|
2090
|
-
}
|
|
2091
|
-
});
|
|
2092
|
-
const tasks = ViewPlugin6.fromClass(class {
|
|
2093
|
-
constructor(view) {
|
|
2094
|
-
this.tasks = taskMatcher.createDeco(view);
|
|
2095
|
-
}
|
|
2096
|
-
update(update2) {
|
|
2097
|
-
this.tasks = taskMatcher.updateDeco(update2, this.tasks);
|
|
2098
|
-
}
|
|
2099
|
-
}, {
|
|
2100
|
-
decorations: (value) => value.tasks,
|
|
2101
|
-
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2102
|
-
return view.plugin(plugin)?.tasks || Decoration8.none;
|
|
2103
|
-
})
|
|
2104
|
-
});
|
|
2105
2368
|
return [
|
|
2106
|
-
|
|
2369
|
+
ViewPlugin8.fromClass(class {
|
|
2370
|
+
constructor(view) {
|
|
2371
|
+
this.decorations = buildDecorations6(view);
|
|
2372
|
+
}
|
|
2373
|
+
update(update2) {
|
|
2374
|
+
if (update2.docChanged || update2.viewportChanged || update2.selectionSet) {
|
|
2375
|
+
this.decorations = buildDecorations6(update2.view);
|
|
2376
|
+
}
|
|
2377
|
+
}
|
|
2378
|
+
}, {
|
|
2379
|
+
decorations: (value) => value.decorations
|
|
2380
|
+
}),
|
|
2107
2381
|
styles5
|
|
2108
2382
|
];
|
|
2109
2383
|
};
|
|
2110
2384
|
|
|
2385
|
+
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
2386
|
+
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
2387
|
+
var mention = ({ onSearch }) => {
|
|
2388
|
+
return autocompletion2({
|
|
2389
|
+
// TODO(burdon): Not working.
|
|
2390
|
+
activateOnTyping: true,
|
|
2391
|
+
// activateOnTypingDelay: 100,
|
|
2392
|
+
// selectOnOpen: true,
|
|
2393
|
+
closeOnBlur: false,
|
|
2394
|
+
// defaultKeymap: false,
|
|
2395
|
+
icons: false,
|
|
2396
|
+
override: [
|
|
2397
|
+
(context) => {
|
|
2398
|
+
console.log(context);
|
|
2399
|
+
const match = context.matchBefore(/@(\w+)?/);
|
|
2400
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
2401
|
+
return null;
|
|
2402
|
+
}
|
|
2403
|
+
return {
|
|
2404
|
+
from: match.from,
|
|
2405
|
+
options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
|
|
2406
|
+
label: `@${value}`
|
|
2407
|
+
}))
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
]
|
|
2411
|
+
});
|
|
2412
|
+
};
|
|
2413
|
+
|
|
2414
|
+
// packages/ui/react-ui-editor/src/extensions/outliner.ts
|
|
2415
|
+
var outliner = (options = {}) => {
|
|
2416
|
+
return [];
|
|
2417
|
+
};
|
|
2418
|
+
|
|
2111
2419
|
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
2112
|
-
import { keymap as
|
|
2420
|
+
import { keymap as keymap6 } from "@codemirror/view";
|
|
2113
2421
|
var defaultItems = [
|
|
2114
2422
|
"hello world!",
|
|
2115
2423
|
"this is a test.",
|
|
@@ -2119,7 +2427,7 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
|
2119
2427
|
let t;
|
|
2120
2428
|
let idx = 0;
|
|
2121
2429
|
return [
|
|
2122
|
-
|
|
2430
|
+
keymap6.of([
|
|
2123
2431
|
{
|
|
2124
2432
|
// Reset.
|
|
2125
2433
|
key: "alt-meta-'",
|
|
@@ -2165,27 +2473,6 @@ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
|
2165
2473
|
];
|
|
2166
2474
|
};
|
|
2167
2475
|
|
|
2168
|
-
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2169
|
-
import { yCollab } from "y-codemirror.next";
|
|
2170
|
-
|
|
2171
|
-
// packages/ui/react-ui-editor/src/extensions/yjs/cursor.ts
|
|
2172
|
-
import * as Y from "yjs";
|
|
2173
|
-
import { arrayToString, stringToArray } from "@dxos/util";
|
|
2174
|
-
var cursorConverter2 = (text) => ({
|
|
2175
|
-
toCursor: (index, assoc) => {
|
|
2176
|
-
return arrayToString(Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text, index, assoc)));
|
|
2177
|
-
},
|
|
2178
|
-
fromCursor: (cursor) => {
|
|
2179
|
-
return Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(cursor)), text.doc)?.index ?? 0;
|
|
2180
|
-
}
|
|
2181
|
-
});
|
|
2182
|
-
|
|
2183
|
-
// packages/ui/react-ui-editor/src/extensions/yjs/yjs.ts
|
|
2184
|
-
var yjs = (content, awareness2) => [
|
|
2185
|
-
Cursor.converter.of(cursorConverter2(content)),
|
|
2186
|
-
yCollab(content, awareness2)
|
|
2187
|
-
];
|
|
2188
|
-
|
|
2189
2476
|
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
2190
2477
|
import get3 from "lodash.get";
|
|
2191
2478
|
var defaultTheme = {
|
|
@@ -2252,7 +2539,7 @@ var defaultTheme = {
|
|
|
2252
2539
|
paddingInline: 0
|
|
2253
2540
|
},
|
|
2254
2541
|
".cm-activeLine": {
|
|
2255
|
-
background: "
|
|
2542
|
+
background: "transparent"
|
|
2256
2543
|
},
|
|
2257
2544
|
//
|
|
2258
2545
|
// Selection
|
|
@@ -2315,13 +2602,6 @@ var defaultTheme = {
|
|
|
2315
2602
|
textDecoration: "none"
|
|
2316
2603
|
},
|
|
2317
2604
|
//
|
|
2318
|
-
// widgets
|
|
2319
|
-
//
|
|
2320
|
-
".cm-widgetBuffer": {
|
|
2321
|
-
display: "none",
|
|
2322
|
-
height: 0
|
|
2323
|
-
},
|
|
2324
|
-
//
|
|
2325
2605
|
// table
|
|
2326
2606
|
//
|
|
2327
2607
|
".cm-table *": {
|
|
@@ -2430,11 +2710,12 @@ var codeTheme = {
|
|
|
2430
2710
|
};
|
|
2431
2711
|
|
|
2432
2712
|
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
2713
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx";
|
|
2433
2714
|
var EditorModes = [
|
|
2434
2715
|
"default",
|
|
2435
2716
|
"vim"
|
|
2436
2717
|
];
|
|
2437
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model,
|
|
2718
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, autoFocus, scrollTo, selection, editorMode, theme, slots = defaultSlots, extensions = [], debug }, forwardedRef) => {
|
|
2438
2719
|
const tabsterDOMAttribute = useFocusableGroup({
|
|
2439
2720
|
tabBehavior: "limited"
|
|
2440
2721
|
});
|
|
@@ -2444,76 +2725,68 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
|
|
|
2444
2725
|
useImperativeHandle(forwardedRef, () => view, [
|
|
2445
2726
|
view
|
|
2446
2727
|
]);
|
|
2447
|
-
|
|
2448
|
-
if (
|
|
2449
|
-
view
|
|
2450
|
-
}
|
|
2451
|
-
}, [
|
|
2452
|
-
view,
|
|
2453
|
-
focus
|
|
2454
|
-
]);
|
|
2455
|
-
const { awareness: awareness2, peer } = model;
|
|
2456
|
-
useEffect(() => {
|
|
2457
|
-
if (awareness2 && peer) {
|
|
2458
|
-
awareness2.setLocalStateField("user", {
|
|
2459
|
-
name: peer.name ?? generateName(peer.id),
|
|
2460
|
-
color: getColorForValue({
|
|
2461
|
-
value: peer.id,
|
|
2462
|
-
type: "color"
|
|
2463
|
-
}),
|
|
2464
|
-
colorLight: getColorForValue({
|
|
2465
|
-
value: peer.id,
|
|
2466
|
-
themeMode,
|
|
2467
|
-
type: "highlight"
|
|
2468
|
-
})
|
|
2469
|
-
});
|
|
2470
|
-
}
|
|
2471
|
-
}, [
|
|
2472
|
-
awareness2,
|
|
2473
|
-
peer,
|
|
2474
|
-
themeMode
|
|
2475
|
-
]);
|
|
2476
|
-
useEffect(() => {
|
|
2477
|
-
if (view && comments2 !== void 0) {
|
|
2478
|
-
view.dispatch({
|
|
2479
|
-
effects: setCommentRange.of({
|
|
2480
|
-
model,
|
|
2481
|
-
comments: comments2
|
|
2482
|
-
})
|
|
2483
|
-
});
|
|
2728
|
+
useEffect2(() => {
|
|
2729
|
+
if (autoFocus) {
|
|
2730
|
+
view?.focus();
|
|
2484
2731
|
}
|
|
2485
2732
|
}, [
|
|
2486
2733
|
view,
|
|
2487
|
-
|
|
2734
|
+
autoFocus
|
|
2488
2735
|
]);
|
|
2489
|
-
|
|
2736
|
+
useEffect2(() => {
|
|
2490
2737
|
if (!model || !rootRef.current) {
|
|
2491
2738
|
return;
|
|
2492
2739
|
}
|
|
2493
2740
|
const state = EditorState2.create({
|
|
2494
2741
|
doc: model.text(),
|
|
2495
|
-
// TODO(burdon): Composer should store and set selection when switching documents.
|
|
2496
2742
|
selection,
|
|
2497
2743
|
extensions: [
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2744
|
+
// TODO(burdon): Doesn't catch errors in keymap functions.
|
|
2745
|
+
EditorView13.exceptionSink.of((err) => {
|
|
2746
|
+
log2.catch(err, void 0, {
|
|
2747
|
+
F: __dxlog_file4,
|
|
2748
|
+
L: 123,
|
|
2749
|
+
S: void 0,
|
|
2750
|
+
C: (f, a) => f(...a)
|
|
2751
|
+
});
|
|
2752
|
+
}),
|
|
2501
2753
|
// Theme.
|
|
2502
|
-
// TODO(burdon): Make
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2754
|
+
// TODO(burdon): Make configurable.
|
|
2755
|
+
EditorView13.baseTheme(defaultTheme),
|
|
2756
|
+
EditorView13.theme(theme ?? {}),
|
|
2757
|
+
EditorView13.darkTheme.of(themeMode === "dark"),
|
|
2758
|
+
EditorView13.editorAttributes.of({
|
|
2759
|
+
class: slots.editor?.className ?? ""
|
|
2760
|
+
}),
|
|
2761
|
+
EditorView13.contentAttributes.of({
|
|
2762
|
+
class: slots.content?.className ?? ""
|
|
2763
|
+
}),
|
|
2764
|
+
// State.
|
|
2765
|
+
EditorView13.editable.of(!readonly),
|
|
2766
|
+
EditorState2.readOnly.of(!!readonly),
|
|
2506
2767
|
// Storage and replication.
|
|
2768
|
+
// NOTE: This must come before user extensions.
|
|
2507
2769
|
model.extension,
|
|
2770
|
+
// TODO(burdon): Factor out (requires special handling for Escape/focus).
|
|
2771
|
+
editorMode === "vim" && vim(),
|
|
2508
2772
|
// Custom.
|
|
2509
2773
|
...extensions
|
|
2510
2774
|
].filter(Boolean)
|
|
2511
2775
|
});
|
|
2512
|
-
|
|
2513
|
-
|
|
2776
|
+
const newView = new EditorView13({
|
|
2777
|
+
state,
|
|
2514
2778
|
parent: rootRef.current,
|
|
2515
|
-
|
|
2779
|
+
scrollTo,
|
|
2780
|
+
// NOTE: Uncomment to debug/monitor all transactions.
|
|
2781
|
+
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
|
2782
|
+
dispatchTransactions: (trs, view2) => {
|
|
2783
|
+
if (debug) {
|
|
2784
|
+
logChanges(trs);
|
|
2785
|
+
}
|
|
2786
|
+
view2.update(trs);
|
|
2787
|
+
}
|
|
2516
2788
|
});
|
|
2789
|
+
view?.destroy();
|
|
2517
2790
|
setView(newView);
|
|
2518
2791
|
return () => {
|
|
2519
2792
|
newView?.destroy();
|
|
@@ -2529,8 +2802,14 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
|
|
|
2529
2802
|
const handleKeyUp = useCallback((event) => {
|
|
2530
2803
|
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
2531
2804
|
switch (key) {
|
|
2805
|
+
case "Enter": {
|
|
2806
|
+
view?.focus();
|
|
2807
|
+
break;
|
|
2808
|
+
}
|
|
2532
2809
|
case "Escape": {
|
|
2533
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)
|
|
2810
|
+
if (editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey)) {
|
|
2811
|
+
rootRef.current?.focus();
|
|
2812
|
+
}
|
|
2534
2813
|
break;
|
|
2535
2814
|
}
|
|
2536
2815
|
}
|
|
@@ -2541,14 +2820,14 @@ var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, focus, selection, read
|
|
|
2541
2820
|
return /* @__PURE__ */ React.createElement("div", {
|
|
2542
2821
|
key: model.id,
|
|
2543
2822
|
role: "none",
|
|
2544
|
-
ref: rootRef,
|
|
2545
2823
|
tabIndex: 0,
|
|
2546
2824
|
onKeyUp: handleKeyUp,
|
|
2547
2825
|
...slots.root,
|
|
2548
|
-
...editorMode !== "vim" && tabsterDOMAttribute
|
|
2826
|
+
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
2827
|
+
ref: rootRef
|
|
2549
2828
|
});
|
|
2550
2829
|
});
|
|
2551
|
-
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3,
|
|
2830
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
2552
2831
|
const { themeMode } = useThemeContext();
|
|
2553
2832
|
const updatedSlots = defaultsDeep2({}, slots, defaultTextSlots);
|
|
2554
2833
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
@@ -2556,9 +2835,9 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
|
|
|
2556
2835
|
readonly,
|
|
2557
2836
|
extensions: [
|
|
2558
2837
|
basicBundle({
|
|
2559
|
-
readonly,
|
|
2560
2838
|
themeMode,
|
|
2561
|
-
placeholder: placeholder3
|
|
2839
|
+
placeholder: placeholder3,
|
|
2840
|
+
lineWrapping
|
|
2562
2841
|
}),
|
|
2563
2842
|
...extensions
|
|
2564
2843
|
],
|
|
@@ -2567,7 +2846,7 @@ var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholde
|
|
|
2567
2846
|
...props
|
|
2568
2847
|
});
|
|
2569
2848
|
});
|
|
2570
|
-
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3,
|
|
2849
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeholder3, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
|
|
2571
2850
|
const { themeMode } = useThemeContext();
|
|
2572
2851
|
const updatedSlots = defaultsDeep2({}, slots, defaultMarkdownSlots);
|
|
2573
2852
|
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
@@ -2575,7 +2854,6 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
|
|
|
2575
2854
|
readonly,
|
|
2576
2855
|
extensions: [
|
|
2577
2856
|
markdownBundle({
|
|
2578
|
-
readonly,
|
|
2579
2857
|
themeMode,
|
|
2580
2858
|
placeholder: placeholder3
|
|
2581
2859
|
}),
|
|
@@ -2588,7 +2866,11 @@ var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, placeholder: placeh
|
|
|
2588
2866
|
});
|
|
2589
2867
|
var defaultSlots = {
|
|
2590
2868
|
root: {
|
|
2591
|
-
|
|
2869
|
+
// TODO(burdon): Add focusRing by default/as property?
|
|
2870
|
+
className: mx3("flex flex-col grow overflow-y-auto", attentionSurface)
|
|
2871
|
+
},
|
|
2872
|
+
editor: {
|
|
2873
|
+
className: "h-full p-2"
|
|
2592
2874
|
}
|
|
2593
2875
|
};
|
|
2594
2876
|
var defaultTextSlots = {
|
|
@@ -2598,22 +2880,17 @@ var defaultMarkdownSlots = {
|
|
|
2598
2880
|
...defaultSlots
|
|
2599
2881
|
};
|
|
2600
2882
|
|
|
2601
|
-
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2602
|
-
import get4 from "lodash.get";
|
|
2603
|
-
import { getRawDoc } from "@dxos/echo-schema";
|
|
2604
|
-
import { isNotNullOrUndefined } from "@dxos/util";
|
|
2605
|
-
|
|
2606
2883
|
// packages/ui/react-ui-editor/src/hooks/awareness-provider.ts
|
|
2607
2884
|
import { DeferredTask, Event as Event2, sleep } from "@dxos/async";
|
|
2608
2885
|
import { Context as Context2 } from "@dxos/context";
|
|
2609
2886
|
import { invariant as invariant3 } from "@dxos/invariant";
|
|
2610
2887
|
import { log as log3 } from "@dxos/log";
|
|
2611
|
-
var
|
|
2888
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/awareness-provider.ts";
|
|
2612
2889
|
var DEBOUNCE_INTERVAL = 100;
|
|
2613
2890
|
var SpaceAwarenessProvider = class {
|
|
2614
2891
|
constructor(params) {
|
|
2615
|
-
this.remoteStateChange = new Event2();
|
|
2616
2892
|
this._remoteStates = /* @__PURE__ */ new Map();
|
|
2893
|
+
this.remoteStateChange = new Event2();
|
|
2617
2894
|
this._space = params.space;
|
|
2618
2895
|
this._channel = params.channel;
|
|
2619
2896
|
this._peerId = params.peerId;
|
|
@@ -2630,7 +2907,7 @@ var SpaceAwarenessProvider = class {
|
|
|
2630
2907
|
await sleep(DEBOUNCE_INTERVAL);
|
|
2631
2908
|
}
|
|
2632
2909
|
});
|
|
2633
|
-
|
|
2910
|
+
this._ctx.onDispose(this._space.listen(this._channel, (message) => {
|
|
2634
2911
|
switch (message.payload.kind) {
|
|
2635
2912
|
case "query": {
|
|
2636
2913
|
this._handleQueryMessage();
|
|
@@ -2641,16 +2918,15 @@ var SpaceAwarenessProvider = class {
|
|
|
2641
2918
|
break;
|
|
2642
2919
|
}
|
|
2643
2920
|
}
|
|
2644
|
-
});
|
|
2645
|
-
this._ctx.onDispose(unsubscribe);
|
|
2921
|
+
}));
|
|
2646
2922
|
void this._space.postMessage(this._channel, {
|
|
2647
2923
|
kind: "query"
|
|
2648
2924
|
}).catch((err) => {
|
|
2649
2925
|
log3.debug("failed to query awareness", {
|
|
2650
2926
|
err
|
|
2651
2927
|
}, {
|
|
2652
|
-
F:
|
|
2653
|
-
L:
|
|
2928
|
+
F: __dxlog_file5,
|
|
2929
|
+
L: 89,
|
|
2654
2930
|
S: this,
|
|
2655
2931
|
C: (f, a) => f(...a)
|
|
2656
2932
|
});
|
|
@@ -2661,10 +2937,13 @@ var SpaceAwarenessProvider = class {
|
|
|
2661
2937
|
this._ctx = void 0;
|
|
2662
2938
|
this._postTask = void 0;
|
|
2663
2939
|
}
|
|
2940
|
+
getRemoteStates() {
|
|
2941
|
+
return Array.from(this._remoteStates.values());
|
|
2942
|
+
}
|
|
2664
2943
|
update(position) {
|
|
2665
2944
|
invariant3(this._postTask, void 0, {
|
|
2666
|
-
F:
|
|
2667
|
-
L:
|
|
2945
|
+
F: __dxlog_file5,
|
|
2946
|
+
L: 104,
|
|
2668
2947
|
S: this,
|
|
2669
2948
|
A: [
|
|
2670
2949
|
"this._postTask",
|
|
@@ -2678,13 +2957,10 @@ var SpaceAwarenessProvider = class {
|
|
|
2678
2957
|
};
|
|
2679
2958
|
this._postTask.schedule();
|
|
2680
2959
|
}
|
|
2681
|
-
getRemoteStates() {
|
|
2682
|
-
return Array.from(this._remoteStates.values());
|
|
2683
|
-
}
|
|
2684
2960
|
_handleQueryMessage() {
|
|
2685
2961
|
invariant3(this._postTask, void 0, {
|
|
2686
|
-
F:
|
|
2687
|
-
L:
|
|
2962
|
+
F: __dxlog_file5,
|
|
2963
|
+
L: 115,
|
|
2688
2964
|
S: this,
|
|
2689
2965
|
A: [
|
|
2690
2966
|
"this._postTask",
|
|
@@ -2695,8 +2971,8 @@ var SpaceAwarenessProvider = class {
|
|
|
2695
2971
|
}
|
|
2696
2972
|
_handlePostMessage(message) {
|
|
2697
2973
|
invariant3(message.kind === "post", void 0, {
|
|
2698
|
-
F:
|
|
2699
|
-
L:
|
|
2974
|
+
F: __dxlog_file5,
|
|
2975
|
+
L: 120,
|
|
2700
2976
|
S: this,
|
|
2701
2977
|
A: [
|
|
2702
2978
|
"message.kind === 'post'",
|
|
@@ -2708,171 +2984,86 @@ var SpaceAwarenessProvider = class {
|
|
|
2708
2984
|
}
|
|
2709
2985
|
};
|
|
2710
2986
|
|
|
2711
|
-
// packages/ui/react-ui-editor/src/hooks/
|
|
2987
|
+
// packages/ui/react-ui-editor/src/hooks/defs.ts
|
|
2712
2988
|
import { StateField as StateField5 } from "@codemirror/state";
|
|
2713
|
-
|
|
2714
|
-
|
|
2989
|
+
var modelState = StateField5.define({
|
|
2990
|
+
create: () => void 0,
|
|
2991
|
+
update: (model) => model
|
|
2992
|
+
});
|
|
2715
2993
|
|
|
2716
|
-
// packages/ui/react-ui-editor/src/hooks/
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
import { invariant as invariant4 } from "@dxos/invariant";
|
|
2722
|
-
import { log as log4 } from "@dxos/log";
|
|
2723
|
-
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs.ts";
|
|
2724
|
-
var createYjsModel = ({ identity, space, text }) => {
|
|
2725
|
-
invariant4(text?.doc && text?.content, void 0, {
|
|
2726
|
-
F: __dxlog_file5,
|
|
2727
|
-
L: 21,
|
|
2728
|
-
S: void 0,
|
|
2729
|
-
A: [
|
|
2730
|
-
"text?.doc && text?.content",
|
|
2731
|
-
""
|
|
2732
|
-
]
|
|
2733
|
-
});
|
|
2734
|
-
const provider = space ? new YAwarenessProvider({
|
|
2735
|
-
space,
|
|
2736
|
-
doc: text.doc,
|
|
2737
|
-
channel: `yjs.awareness.${text.id}`
|
|
2738
|
-
}) : void 0;
|
|
2739
|
-
const model = {
|
|
2740
|
-
id: text.doc.guid,
|
|
2741
|
-
content: text.content,
|
|
2742
|
-
text: () => text.content.toString(),
|
|
2743
|
-
extension: [
|
|
2744
|
-
modelState.init(() => model),
|
|
2745
|
-
yjs(text.content, provider?.awareness)
|
|
2746
|
-
],
|
|
2747
|
-
awareness: provider?.awareness,
|
|
2748
|
-
peer: identity ? {
|
|
2749
|
-
id: identity.identityKey.toHex(),
|
|
2750
|
-
name: identity.profile?.displayName
|
|
2751
|
-
} : void 0
|
|
2752
|
-
};
|
|
2753
|
-
return model;
|
|
2754
|
-
};
|
|
2755
|
-
var messageAwareness = 1;
|
|
2756
|
-
var messageQueryAwareness = 3;
|
|
2757
|
-
var YAwarenessProvider = class extends Observable {
|
|
2758
|
-
constructor({ space, doc, channel, awareness: awareness2 }) {
|
|
2759
|
-
super();
|
|
2760
|
-
this._space = space;
|
|
2761
|
-
this._awareness = awareness2 ?? new YP.Awareness(doc);
|
|
2762
|
-
this._channel = channel;
|
|
2763
|
-
this._clientId = doc.clientID;
|
|
2764
|
-
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
2765
|
-
this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
|
|
2766
|
-
if (typeof window !== "undefined") {
|
|
2767
|
-
window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
|
|
2768
|
-
} else if (typeof process !== "undefined") {
|
|
2769
|
-
process.on("exit", this._handleBeforeUnload.bind(this));
|
|
2770
|
-
}
|
|
2771
|
-
const encoderAwarenessQuery = encoding.createEncoder();
|
|
2772
|
-
encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
|
|
2773
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
2774
|
-
const encoderAwarenessState = encoding.createEncoder();
|
|
2775
|
-
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
2776
|
-
encoding.writeVarUint8Array(encoderAwarenessState, YP.encodeAwarenessUpdate(this.awareness, [
|
|
2777
|
-
this._clientId
|
|
2778
|
-
]));
|
|
2779
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
2780
|
-
}
|
|
2781
|
-
get awareness() {
|
|
2782
|
-
return this._awareness;
|
|
2783
|
-
}
|
|
2784
|
-
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
2785
|
-
log4("awareness update", {
|
|
2786
|
-
added,
|
|
2787
|
-
updated,
|
|
2788
|
-
removed,
|
|
2789
|
-
origin
|
|
2790
|
-
}, {
|
|
2791
|
-
F: __dxlog_file5,
|
|
2792
|
-
L: 100,
|
|
2793
|
-
S: this,
|
|
2794
|
-
C: (f, a) => f(...a)
|
|
2795
|
-
});
|
|
2796
|
-
const changedClients = added.concat(updated).concat(removed);
|
|
2797
|
-
const encoderAwareness = encoding.createEncoder();
|
|
2798
|
-
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
2799
|
-
encoding.writeVarUint8Array(encoderAwareness, YP.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
2800
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
2801
|
-
}
|
|
2802
|
-
_handleSpaceMessage({ payload }) {
|
|
2803
|
-
log4("space message", payload, {
|
|
2804
|
-
F: __dxlog_file5,
|
|
2805
|
-
L: 109,
|
|
2806
|
-
S: this,
|
|
2807
|
-
C: (f, a) => f(...a)
|
|
2808
|
-
});
|
|
2809
|
-
const data = new Uint8Array(Array.from(Object.values(payload)));
|
|
2810
|
-
const encoder = this._readMessage(data);
|
|
2811
|
-
if (encoder) {
|
|
2812
|
-
void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
|
|
2994
|
+
// packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
|
|
2995
|
+
var useActionHandler = (view) => {
|
|
2996
|
+
return (action) => {
|
|
2997
|
+
if (!view) {
|
|
2998
|
+
return;
|
|
2813
2999
|
}
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
const encoder = encoding.createEncoder();
|
|
2818
|
-
const messageType = decoding.readVarUint(decoder);
|
|
2819
|
-
let sendReply = false;
|
|
2820
|
-
switch (messageType) {
|
|
2821
|
-
case messageAwareness: {
|
|
2822
|
-
YP.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
3000
|
+
switch (action.type) {
|
|
3001
|
+
case "heading":
|
|
3002
|
+
setHeading(parseInt(action.data))(view);
|
|
2823
3003
|
break;
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
3004
|
+
case "bold":
|
|
3005
|
+
toggleBold(view);
|
|
3006
|
+
break;
|
|
3007
|
+
case "italic":
|
|
3008
|
+
toggleItalic(view);
|
|
3009
|
+
break;
|
|
3010
|
+
case "strikethrough":
|
|
3011
|
+
toggleStrikethrough(view);
|
|
3012
|
+
break;
|
|
3013
|
+
case "list":
|
|
3014
|
+
toggleList(view);
|
|
3015
|
+
break;
|
|
3016
|
+
case "comment":
|
|
3017
|
+
createComment(view);
|
|
2829
3018
|
break;
|
|
2830
|
-
}
|
|
2831
|
-
default: {
|
|
2832
|
-
console.error("Invalid message:", messageType);
|
|
2833
|
-
return encoder;
|
|
2834
|
-
}
|
|
2835
|
-
}
|
|
2836
|
-
if (!sendReply) {
|
|
2837
|
-
return null;
|
|
2838
3019
|
}
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
3020
|
+
setTimeout(() => {
|
|
3021
|
+
view.focus();
|
|
3022
|
+
});
|
|
3023
|
+
};
|
|
3024
|
+
};
|
|
3025
|
+
|
|
3026
|
+
// packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
|
|
3027
|
+
import { useState as useState2 } from "react";
|
|
3028
|
+
var useTextEditor = () => {
|
|
3029
|
+
const [view, setView] = useState2(null);
|
|
3030
|
+
return [
|
|
3031
|
+
(ref) => {
|
|
3032
|
+
setView(ref);
|
|
3033
|
+
},
|
|
3034
|
+
view
|
|
3035
|
+
];
|
|
2846
3036
|
};
|
|
2847
3037
|
|
|
2848
3038
|
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
}
|
|
3039
|
+
import get4 from "lodash.get";
|
|
3040
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
3041
|
+
import { generateName } from "@dxos/display-name";
|
|
3042
|
+
import { getRawDoc } from "@dxos/echo-schema";
|
|
3043
|
+
import { invariant as invariant4 } from "@dxos/invariant";
|
|
3044
|
+
import { isAutomergeObject } from "@dxos/react-client/echo";
|
|
3045
|
+
import { isNotNullOrUndefined } from "@dxos/util";
|
|
3046
|
+
var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
2853
3047
|
var useTextModel = (props) => {
|
|
2854
3048
|
const { identity, space, text } = props;
|
|
2855
|
-
const [model, setModel] =
|
|
2856
|
-
|
|
3049
|
+
const [model, setModel] = useState3();
|
|
3050
|
+
useEffect3(() => setModel(createModel(props)), [
|
|
2857
3051
|
identity,
|
|
2858
3052
|
space,
|
|
2859
3053
|
text
|
|
2860
3054
|
]);
|
|
2861
3055
|
return model;
|
|
2862
3056
|
};
|
|
2863
|
-
var createModel = (
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
};
|
|
2873
|
-
|
|
2874
|
-
// packages/ui/react-ui-editor/src/hooks/automerge.ts
|
|
2875
|
-
var createAutomergeModel = ({ space, identity, text }) => {
|
|
3057
|
+
var createModel = ({ space, identity, text }) => {
|
|
3058
|
+
invariant4(isAutomergeObject(text), void 0, {
|
|
3059
|
+
F: __dxlog_file6,
|
|
3060
|
+
L: 35,
|
|
3061
|
+
S: void 0,
|
|
3062
|
+
A: [
|
|
3063
|
+
"isAutomergeObject(text)",
|
|
3064
|
+
""
|
|
3065
|
+
]
|
|
3066
|
+
});
|
|
2876
3067
|
const obj = text;
|
|
2877
3068
|
const doc = getRawDoc(obj, [
|
|
2878
3069
|
obj.field
|
|
@@ -2881,7 +3072,7 @@ var createAutomergeModel = ({ space, identity, text }) => {
|
|
|
2881
3072
|
space,
|
|
2882
3073
|
channel: `automerge.awareness.${obj.id}`,
|
|
2883
3074
|
info: {
|
|
2884
|
-
displayName: identity
|
|
3075
|
+
displayName: identity ? identity.profile?.displayName ?? generateName(identity.identityKey.toHex()) : void 0,
|
|
2885
3076
|
color: cursorColor.color,
|
|
2886
3077
|
lightColor: cursorColor.light
|
|
2887
3078
|
},
|
|
@@ -2891,10 +3082,9 @@ var createAutomergeModel = ({ space, identity, text }) => {
|
|
|
2891
3082
|
id: obj.id,
|
|
2892
3083
|
content: doc,
|
|
2893
3084
|
text: () => get4(doc.handle.docSync(), doc.path),
|
|
2894
|
-
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
2895
3085
|
extension: [
|
|
2896
3086
|
modelState.init(() => model),
|
|
2897
|
-
|
|
3087
|
+
automerge4({
|
|
2898
3088
|
handle: doc.handle,
|
|
2899
3089
|
path: doc.path
|
|
2900
3090
|
}),
|
|
@@ -2919,11 +3109,10 @@ export {
|
|
|
2919
3109
|
SpaceAwarenessProvider,
|
|
2920
3110
|
TextEditor,
|
|
2921
3111
|
TextKind,
|
|
2922
|
-
YAwarenessProvider,
|
|
2923
3112
|
YText,
|
|
2924
3113
|
YXmlFragment,
|
|
2925
3114
|
autocomplete,
|
|
2926
|
-
|
|
3115
|
+
automerge4 as automerge,
|
|
2927
3116
|
awareness,
|
|
2928
3117
|
basicBundle,
|
|
2929
3118
|
blast,
|
|
@@ -2931,19 +3120,20 @@ export {
|
|
|
2931
3120
|
code2 as code,
|
|
2932
3121
|
codeTheme,
|
|
2933
3122
|
comments,
|
|
2934
|
-
|
|
2935
|
-
createYjsModel,
|
|
2936
|
-
cursorConverter,
|
|
3123
|
+
createComment,
|
|
2937
3124
|
defaultMarkdownSlots,
|
|
2938
3125
|
defaultOptions,
|
|
2939
3126
|
defaultSlots,
|
|
2940
3127
|
defaultTextSlots,
|
|
2941
3128
|
defaultTheme,
|
|
3129
|
+
formatting,
|
|
2942
3130
|
getToken,
|
|
3131
|
+
heading2 as heading,
|
|
2943
3132
|
hr,
|
|
2944
3133
|
image,
|
|
2945
3134
|
link,
|
|
2946
3135
|
listener,
|
|
3136
|
+
logChanges,
|
|
2947
3137
|
markdownBundle,
|
|
2948
3138
|
markdownHighlightStyle,
|
|
2949
3139
|
markdownTags,
|
|
@@ -2951,15 +3141,24 @@ export {
|
|
|
2951
3141
|
markdownTheme,
|
|
2952
3142
|
mention,
|
|
2953
3143
|
modelState,
|
|
2954
|
-
|
|
3144
|
+
outliner,
|
|
3145
|
+
setComments,
|
|
2955
3146
|
setFocus,
|
|
3147
|
+
setHeading,
|
|
2956
3148
|
setSelection,
|
|
2957
3149
|
table,
|
|
2958
3150
|
tags2 as tags,
|
|
2959
3151
|
tasklist,
|
|
2960
3152
|
textTheme,
|
|
3153
|
+
toggleBold,
|
|
3154
|
+
toggleItalic,
|
|
3155
|
+
toggleList,
|
|
3156
|
+
toggleStrikethrough,
|
|
3157
|
+
toggleStyle,
|
|
2961
3158
|
typewriter,
|
|
2962
|
-
|
|
2963
|
-
|
|
3159
|
+
useActionHandler,
|
|
3160
|
+
useComments,
|
|
3161
|
+
useTextEditor,
|
|
3162
|
+
useTextModel
|
|
2964
3163
|
};
|
|
2965
3164
|
//# sourceMappingURL=index.mjs.map
|