@dxos/react-ui-editor 0.3.10 → 0.3.11-main.0e40963
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 +1971 -1744
- 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 +14 -10
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +3 -2
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/index.d.ts +0 -2
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/basic.d.ts.map +1 -0
- package/dist/types/src/extensions/blast.d.ts.map +1 -0
- package/dist/types/src/extensions/code.d.ts.map +1 -0
- package/dist/types/src/extensions/comments.d.ts +48 -0
- package/dist/types/src/extensions/comments.d.ts.map +1 -0
- package/dist/types/src/extensions/hr.d.ts +2 -0
- package/dist/types/src/extensions/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/image.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/index.d.ts +3 -3
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/link.d.ts +1 -0
- package/dist/types/src/extensions/link.d.ts.map +1 -0
- package/dist/types/src/extensions/listener.d.ts +10 -0
- package/dist/types/src/extensions/listener.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -0
- package/dist/types/src/extensions/mermaid.d.ts +3 -0
- package/dist/types/src/extensions/mermaid.d.ts.map +1 -0
- package/dist/types/src/extensions/table.d.ts.map +1 -0
- package/dist/types/src/extensions/tasklist.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions/demo.d.ts → extensions/typewriter.d.ts} +2 -3
- package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
- package/dist/types/src/extensions/util.d.ts +5 -0
- package/dist/types/src/extensions/util.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts +2 -3
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -1
- package/dist/types/src/hooks/automerge/{PatchSemaphore.d.ts → semaphore.d.ts} +1 -1
- package/dist/types/src/hooks/automerge/semaphore.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge/update-automerge.d.ts +6 -0
- package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/hooks/automerge/update-codemirror.d.ts +5 -0
- package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextModel.d.ts +14 -6
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/hooks/yjs/yjs.test.d.ts +2 -0
- package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +3 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/testing/replicator.d.ts.map +1 -1
- package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +5 -13
- package/dist/types/src/themes/default.d.ts.map +1 -0
- package/dist/types/src/themes/index.d.ts.map +1 -0
- package/package.json +24 -20
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +130 -71
- package/src/components/TextEditor/TextEditor.stories.tsx +2 -2
- package/src/components/TextEditor/TextEditor.tsx +29 -25
- package/src/components/TextEditor/index.ts +0 -3
- package/src/{components/TextEditor/extensions → extensions}/code.ts +13 -13
- package/src/extensions/comments.ts +391 -0
- package/src/extensions/hr.ts +74 -0
- package/src/{components/TextEditor/extensions → extensions}/image.ts +3 -1
- package/src/{components/TextEditor/extensions → extensions}/index.ts +3 -3
- package/src/{components/TextEditor/extensions → extensions}/link.ts +66 -10
- package/src/extensions/listener.ts +35 -0
- package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +6 -9
- package/src/extensions/mermaid.ts +11 -0
- package/src/{components/TextEditor/extensions → extensions}/table.ts +14 -1
- package/src/{components/TextEditor/extensions/demo.ts → extensions/typewriter.ts} +2 -3
- package/src/extensions/util.ts +18 -0
- package/src/hooks/automerge/automerge.stories.tsx +2 -3
- package/src/hooks/automerge/plugin.ts +1 -1
- package/src/hooks/automerge/{PatchSemaphore.ts → semaphore.ts} +4 -3
- package/src/hooks/automerge/{translation/codemirror-to-automerge.ts → update-automerge.ts} +3 -3
- package/src/hooks/automerge/{translation/automerge-to-codemirror.ts → update-codemirror.ts} +2 -7
- package/src/hooks/useTextModel.ts +60 -22
- package/src/hooks/yjs/yjs.test.ts +59 -0
- package/src/index.ts +3 -0
- package/src/testing/replicator.ts +0 -1
- package/src/{components/TextEditor/themes → themes}/default.ts +61 -38
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts +0 -18
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts +0 -10
- package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +0 -24
- package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts +0 -9
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +0 -12
- package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +0 -5
- package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +0 -6
- package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/translation/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/translation/index.d.ts.map +0 -1
- package/src/components/TextEditor/extensions/comments.ts +0 -173
- package/src/components/TextEditor/extensions/experimental.tsx +0 -114
- package/src/components/TextEditor/extensions/highlight.ts +0 -128
- package/src/components/TextEditor/extensions/listener.ts +0 -23
- package/src/components/TextEditor/extensions/tooltip.tsx +0 -61
- package/src/hooks/automerge/translation/index.ts +0 -6
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/basic.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/code.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/image.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/bundle.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/index.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/table.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/tasklist.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/basic.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/markdown/index.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/mention.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/tasklist.ts +0 -0
- /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
|
@@ -5,7 +5,18 @@ import { Doc, YText, YXmlFragment } from "@dxos/text-model";
|
|
|
5
5
|
// packages/ui/react-ui-editor/src/components/TextEditor/index.ts
|
|
6
6
|
import { tags as tags2 } from "@lezer/highlight";
|
|
7
7
|
|
|
8
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/
|
|
8
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
9
|
+
import { EditorState as EditorState2 } from "@codemirror/state";
|
|
10
|
+
import { EditorView as EditorView12 } from "@codemirror/view";
|
|
11
|
+
import { useFocusableGroup } from "@fluentui/react-tabster";
|
|
12
|
+
import { vim } from "@replit/codemirror-vim";
|
|
13
|
+
import defaultsDeep2 from "lodash.defaultsdeep";
|
|
14
|
+
import React, { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useState as useState2 } from "react";
|
|
15
|
+
import { generateName } from "@dxos/display-name";
|
|
16
|
+
import { useThemeContext } from "@dxos/react-ui";
|
|
17
|
+
import { getColorForValue, inputSurface, mx as mx3 } from "@dxos/react-ui-theme";
|
|
18
|
+
|
|
19
|
+
// packages/ui/react-ui-editor/src/extensions/autocomplete.ts
|
|
9
20
|
import { autocompletion, completionKeymap } from "@codemirror/autocomplete";
|
|
10
21
|
import { keymap } from "@codemirror/view";
|
|
11
22
|
var autocomplete = ({ onSearch }) => {
|
|
@@ -38,7 +49,7 @@ var autocomplete = ({ onSearch }) => {
|
|
|
38
49
|
];
|
|
39
50
|
};
|
|
40
51
|
|
|
41
|
-
// packages/ui/react-ui-editor/src/
|
|
52
|
+
// packages/ui/react-ui-editor/src/extensions/basic.ts
|
|
42
53
|
import { closeBrackets } from "@codemirror/autocomplete";
|
|
43
54
|
import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@codemirror/language";
|
|
44
55
|
import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
|
|
@@ -55,11 +66,11 @@ var basicBundle = ({ readonly, themeMode, placeholder: _placeholder }) => [
|
|
|
55
66
|
themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)
|
|
56
67
|
].filter(Boolean);
|
|
57
68
|
|
|
58
|
-
// packages/ui/react-ui-editor/src/
|
|
69
|
+
// packages/ui/react-ui-editor/src/extensions/blast.ts
|
|
59
70
|
import { EditorView as EditorView2, keymap as keymap2 } from "@codemirror/view";
|
|
60
71
|
import defaultsDeep from "lodash.defaultsdeep";
|
|
61
72
|
import { invariant } from "@dxos/invariant";
|
|
62
|
-
var __dxlog_file = "/home/
|
|
73
|
+
var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
|
|
63
74
|
var defaultOptions = {
|
|
64
75
|
effect: 2,
|
|
65
76
|
maxParticles: 200,
|
|
@@ -365,7 +376,7 @@ var random = (min, max) => {
|
|
|
365
376
|
return min + ~~(Math.random() * (max - min + 1));
|
|
366
377
|
};
|
|
367
378
|
|
|
368
|
-
// packages/ui/react-ui-editor/src/
|
|
379
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
369
380
|
import { ViewPlugin, EditorView as EditorView3, Decoration } from "@codemirror/view";
|
|
370
381
|
import get from "lodash.get";
|
|
371
382
|
import { mx as mx2 } from "@dxos/react-ui-theme";
|
|
@@ -411,31 +422,33 @@ var code = "font-mono !no-underline text-neutral-700 dark:text-neutral-300";
|
|
|
411
422
|
var codeMark = "font-mono text-primary-500";
|
|
412
423
|
var inlineUrl = mx(code, "px-1");
|
|
413
424
|
var blockquote = mx("pl-1 mr-1 border-is-4 border-primary-500/70 dark:border-primary-500/30", light);
|
|
414
|
-
var horizontalRule = "flex mlb-4 border-b text-neutral-100 dark:text-neutral-900 border-neutral-200 dark:border-neutral-800";
|
|
415
425
|
|
|
416
426
|
// packages/ui/react-ui-editor/src/styles/tokens.ts
|
|
417
427
|
import { tailwindConfig } from "@dxos/react-ui-theme";
|
|
418
428
|
var tokens = tailwindConfig({}).theme;
|
|
419
429
|
|
|
420
|
-
// packages/ui/react-ui-editor/src/
|
|
430
|
+
// packages/ui/react-ui-editor/src/extensions/code.ts
|
|
421
431
|
var CODE_REGEX = /```[\s\S]*?```/gs;
|
|
422
432
|
var styles = EditorView3.baseTheme({
|
|
423
|
-
"& .cm-
|
|
424
|
-
|
|
425
|
-
paddingInline: "4px !important"
|
|
433
|
+
"& .cm-codeblock": {
|
|
434
|
+
fontFamily: get(tokens, "fontFamily.mono", []).join(",")
|
|
426
435
|
},
|
|
427
|
-
|
|
436
|
+
'&light [aria-readonly="true"] .cm-codeblock': {
|
|
428
437
|
background: get(tokens, "extend.colors.neutral.50")
|
|
429
438
|
},
|
|
430
|
-
|
|
439
|
+
'&dark [aria-readonly="true"] .cm-codeblock': {
|
|
431
440
|
background: get(tokens, "extend.colors.neutral.850")
|
|
432
441
|
},
|
|
433
|
-
|
|
442
|
+
'& [aria-readonly="true"] .cm-codeblock': {
|
|
443
|
+
display: "block",
|
|
444
|
+
paddingInline: "4px !important"
|
|
445
|
+
},
|
|
446
|
+
'& [aria-readonly="true"] .cm-codeblock-first': {
|
|
434
447
|
paddingTop: "4px !important",
|
|
435
448
|
borderTopLeftRadius: "4px",
|
|
436
449
|
borderTopRightRadius: "4px"
|
|
437
450
|
},
|
|
438
|
-
|
|
451
|
+
'& [aria-readonly="true"] .cm-codeblock-last': {
|
|
439
452
|
paddingBottom: "4px !important",
|
|
440
453
|
borderBottomLeftRadius: "4px",
|
|
441
454
|
borderBottomRightRadius: "4px"
|
|
@@ -461,7 +474,7 @@ var code2 = () => {
|
|
|
461
474
|
for (let i = range[0]; i <= range[1]; i++) {
|
|
462
475
|
const block = blocks[i];
|
|
463
476
|
decorations.push(Decoration.line({
|
|
464
|
-
class: mx2("cm-
|
|
477
|
+
class: mx2("cm-codeblock", i === range[0] && "cm-codeblock-first", i === range[1] && "cm-codeblock-last")
|
|
465
478
|
}).range(block.from));
|
|
466
479
|
}
|
|
467
480
|
}
|
|
@@ -485,1853 +498,2068 @@ var code2 = () => {
|
|
|
485
498
|
];
|
|
486
499
|
};
|
|
487
500
|
|
|
488
|
-
// packages/ui/react-ui-editor/src/
|
|
489
|
-
import {
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
"this is a test.",
|
|
493
|
-
"this is [DXOS](https://dxos.org)"
|
|
494
|
-
];
|
|
495
|
-
var demo = ({ delay = 75, items = defaultItems } = {}) => {
|
|
496
|
-
let t;
|
|
497
|
-
let idx = 0;
|
|
498
|
-
return [
|
|
499
|
-
keymap3.of([
|
|
500
|
-
{
|
|
501
|
-
// Reset.
|
|
502
|
-
key: "alt-meta-'",
|
|
503
|
-
run: (view) => {
|
|
504
|
-
clearTimeout(t);
|
|
505
|
-
idx = 0;
|
|
506
|
-
return true;
|
|
507
|
-
}
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
// Next prompt.
|
|
511
|
-
// TODO(burdon): Press 1-9 to select prompt?
|
|
512
|
-
key: "shift-meta-'",
|
|
513
|
-
run: (view) => {
|
|
514
|
-
clearTimeout(t);
|
|
515
|
-
const text = items[idx++];
|
|
516
|
-
if (idx === items?.length) {
|
|
517
|
-
idx = 0;
|
|
518
|
-
}
|
|
519
|
-
let i = 0;
|
|
520
|
-
const insert = (d = 0) => {
|
|
521
|
-
t = setTimeout(() => {
|
|
522
|
-
const pos = view.state.selection.main.head;
|
|
523
|
-
view.dispatch({
|
|
524
|
-
changes: {
|
|
525
|
-
from: pos,
|
|
526
|
-
insert: text[i++]
|
|
527
|
-
},
|
|
528
|
-
selection: {
|
|
529
|
-
anchor: pos + 1
|
|
530
|
-
}
|
|
531
|
-
});
|
|
532
|
-
if (i < text.length) {
|
|
533
|
-
insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
|
|
534
|
-
}
|
|
535
|
-
}, d);
|
|
536
|
-
};
|
|
537
|
-
insert();
|
|
538
|
-
return true;
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
])
|
|
542
|
-
];
|
|
543
|
-
};
|
|
544
|
-
|
|
545
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/comments.ts
|
|
546
|
-
import { keymap as keymap4, Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
|
|
501
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
502
|
+
import { StateEffect as StateEffect2, StateField as StateField3 } from "@codemirror/state";
|
|
503
|
+
import { hoverTooltip, keymap as keymap3, Decoration as Decoration2, EditorView as EditorView4, MatchDecorator, ViewPlugin as ViewPlugin3, WidgetType } from "@codemirror/view";
|
|
504
|
+
import sortBy from "lodash.sortby";
|
|
547
505
|
import { debounce } from "@dxos/async";
|
|
548
|
-
import { invariant as
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
});
|
|
561
|
-
var BookmarkWidget = class extends WidgetType {
|
|
562
|
-
constructor(_pos, _id, _handleClick) {
|
|
563
|
-
super();
|
|
564
|
-
this._pos = _pos;
|
|
565
|
-
this._id = _id;
|
|
566
|
-
this._handleClick = _handleClick;
|
|
567
|
-
invariant2(this._id, void 0, {
|
|
506
|
+
import { invariant as invariant3 } from "@dxos/invariant";
|
|
507
|
+
import { log as log3 } from "@dxos/log";
|
|
508
|
+
import { nonNullable } from "@dxos/util";
|
|
509
|
+
|
|
510
|
+
// packages/ui/react-ui-editor/src/extensions/util.ts
|
|
511
|
+
import { log } from "@dxos/log";
|
|
512
|
+
var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/util.ts";
|
|
513
|
+
var callbackWrapper = (fn) => (...args) => {
|
|
514
|
+
try {
|
|
515
|
+
return fn(...args);
|
|
516
|
+
} catch (error) {
|
|
517
|
+
log.catch(error, void 0, {
|
|
568
518
|
F: __dxlog_file2,
|
|
569
|
-
L:
|
|
570
|
-
S:
|
|
571
|
-
|
|
572
|
-
"this._id",
|
|
573
|
-
""
|
|
574
|
-
]
|
|
519
|
+
L: 16,
|
|
520
|
+
S: void 0,
|
|
521
|
+
C: (f, a) => f(...a)
|
|
575
522
|
});
|
|
576
523
|
}
|
|
577
|
-
|
|
578
|
-
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
527
|
+
import { StateField as StateField2 } from "@codemirror/state";
|
|
528
|
+
import get2 from "lodash.get";
|
|
529
|
+
import { useEffect, useState } from "react";
|
|
530
|
+
import { yCollab } from "y-codemirror.next";
|
|
531
|
+
import * as Y from "yjs";
|
|
532
|
+
import { invariant as invariant2 } from "@dxos/invariant";
|
|
533
|
+
import { getRawDoc, isActualAutomergeObject } from "@dxos/react-client/echo";
|
|
534
|
+
import { arrayToString, stringToArray } from "@dxos/util";
|
|
535
|
+
|
|
536
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
|
|
537
|
+
import { Annotation, Facet, StateEffect, StateField } from "@codemirror/state";
|
|
538
|
+
import { ViewPlugin as ViewPlugin2 } from "@codemirror/view";
|
|
539
|
+
import * as automerge2 from "@dxos/automerge/automerge";
|
|
540
|
+
|
|
541
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/semaphore.ts
|
|
542
|
+
import { next as automerge } from "@dxos/automerge/automerge";
|
|
543
|
+
|
|
544
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/update-automerge.ts
|
|
545
|
+
import { next as am } from "@dxos/automerge/automerge";
|
|
546
|
+
var updateAutomerge = (field, handle, transactions, state) => {
|
|
547
|
+
const { lastHeads, path } = state.field(field);
|
|
548
|
+
let hasChanges = false;
|
|
549
|
+
for (const tr of transactions) {
|
|
550
|
+
tr.changes.iterChanges(() => {
|
|
551
|
+
hasChanges = true;
|
|
552
|
+
});
|
|
579
553
|
}
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
span.className = "cm-bookmark";
|
|
583
|
-
span.textContent = "\xA7";
|
|
584
|
-
span.onclick = () => this._handleClick();
|
|
585
|
-
return span;
|
|
554
|
+
if (!hasChanges) {
|
|
555
|
+
return void 0;
|
|
586
556
|
}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
regexp: /\[\^(\w+)\]/g,
|
|
592
|
-
decoration: (match, view, pos) => {
|
|
593
|
-
const id = match[1];
|
|
594
|
-
return Decoration2.replace({
|
|
595
|
-
id,
|
|
596
|
-
widget: new BookmarkWidget(pos, id, () => handleUpdate({
|
|
597
|
-
active: id
|
|
598
|
-
}))
|
|
557
|
+
const newHeads = handle.changeAt(lastHeads, (doc) => {
|
|
558
|
+
for (const tr of transactions) {
|
|
559
|
+
tr.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
|
|
560
|
+
am.splice(doc, path, fromA, toA - fromA, inserted.toString());
|
|
599
561
|
});
|
|
600
562
|
}
|
|
601
563
|
});
|
|
602
|
-
|
|
603
|
-
constructor(view) {
|
|
604
|
-
this.bookmarks = bookmarkMatcher.createDeco(view);
|
|
605
|
-
}
|
|
606
|
-
update(update4) {
|
|
607
|
-
this.bookmarks = bookmarkMatcher.updateDeco(update4, this.bookmarks);
|
|
608
|
-
}
|
|
609
|
-
}, {
|
|
610
|
-
decorations: (instance) => instance.bookmarks,
|
|
611
|
-
provide: (plugin) => EditorView4.atomicRanges.of((view) => {
|
|
612
|
-
return view.plugin(plugin)?.bookmarks || Decoration2.none;
|
|
613
|
-
})
|
|
614
|
-
});
|
|
615
|
-
const handleUpdate = debounce((data) => {
|
|
616
|
-
options.onUpdate?.(data);
|
|
617
|
-
}, 200);
|
|
618
|
-
return [
|
|
619
|
-
keymap4.of([
|
|
620
|
-
{
|
|
621
|
-
key: options?.key ?? "shift-meta-c",
|
|
622
|
-
run: (view) => {
|
|
623
|
-
const { from, to, head } = view.state.selection.main;
|
|
624
|
-
const id = options.onCreate?.(from, to);
|
|
625
|
-
if (id) {
|
|
626
|
-
const tag = `[^${id}]`;
|
|
627
|
-
view.dispatch({
|
|
628
|
-
changes: {
|
|
629
|
-
from: head,
|
|
630
|
-
insert: tag
|
|
631
|
-
},
|
|
632
|
-
selection: {
|
|
633
|
-
anchor: head + tag.length
|
|
634
|
-
}
|
|
635
|
-
});
|
|
636
|
-
return true;
|
|
637
|
-
}
|
|
638
|
-
return false;
|
|
639
|
-
}
|
|
640
|
-
}
|
|
641
|
-
]),
|
|
642
|
-
bookmarks,
|
|
643
|
-
// Monitor cursor movement.
|
|
644
|
-
EditorView4.updateListener.of((update4) => {
|
|
645
|
-
active = void 0;
|
|
646
|
-
if (!options.onUpdate) {
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
const { view, state } = update4;
|
|
650
|
-
const pos = state.selection.main.head;
|
|
651
|
-
const decorations = [];
|
|
652
|
-
const rangeSet = view.plugin(bookmarks)?.bookmarks;
|
|
653
|
-
let closest = Infinity;
|
|
654
|
-
const { from, to } = (
|
|
655
|
-
/* view.visibleRanges?.[0] ?? */
|
|
656
|
-
{
|
|
657
|
-
from: 0,
|
|
658
|
-
to: state.doc.length
|
|
659
|
-
}
|
|
660
|
-
);
|
|
661
|
-
rangeSet?.between(from, to, (from2, to2, value) => {
|
|
662
|
-
decorations.push({
|
|
663
|
-
from: from2,
|
|
664
|
-
to: to2,
|
|
665
|
-
value
|
|
666
|
-
});
|
|
667
|
-
const d = Math.min(Math.abs(pos - from2), Math.abs(pos - to2));
|
|
668
|
-
if (d < closest) {
|
|
669
|
-
active = value.spec.id;
|
|
670
|
-
closest = d;
|
|
671
|
-
}
|
|
672
|
-
});
|
|
673
|
-
if (decorations.length) {
|
|
674
|
-
handleUpdate({
|
|
675
|
-
active,
|
|
676
|
-
items: decorations.map(({ from: from2, value: { spec: { id } } }) => ({
|
|
677
|
-
id,
|
|
678
|
-
pos: from2,
|
|
679
|
-
location: view.coordsAtPos(from2)
|
|
680
|
-
}))
|
|
681
|
-
});
|
|
682
|
-
}
|
|
683
|
-
}),
|
|
684
|
-
styles2
|
|
685
|
-
];
|
|
564
|
+
return newHeads ?? void 0;
|
|
686
565
|
};
|
|
687
566
|
|
|
688
|
-
// packages/ui/react-ui-editor/src/
|
|
689
|
-
import {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
backgroundColor: "lime"
|
|
567
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/update-codemirror.ts
|
|
568
|
+
import { ChangeSet } from "@codemirror/state";
|
|
569
|
+
var updateCodeMirror = (view, selection, target, patches) => {
|
|
570
|
+
for (const patch of patches) {
|
|
571
|
+
const changeSpec = handlePatch(patch, target, view.state);
|
|
572
|
+
if (changeSpec != null) {
|
|
573
|
+
const changeSet = ChangeSet.of(changeSpec, view.state.doc.length, "\n");
|
|
574
|
+
selection = selection.map(changeSet, 1);
|
|
575
|
+
view.dispatch({
|
|
576
|
+
changes: changeSet,
|
|
577
|
+
annotations: reconcileAnnotationType.of({})
|
|
578
|
+
});
|
|
579
|
+
}
|
|
702
580
|
}
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}),
|
|
708
|
-
HighlightActive: Decoration3.mark({
|
|
709
|
-
class: "cm-highlight-active"
|
|
710
|
-
})
|
|
581
|
+
view.dispatch({
|
|
582
|
+
selection,
|
|
583
|
+
annotations: reconcileAnnotationType.of({})
|
|
584
|
+
});
|
|
711
585
|
};
|
|
712
|
-
var
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
586
|
+
var handlePatch = (patch, target, state) => {
|
|
587
|
+
if (patch.action === "insert") {
|
|
588
|
+
return handleInsert(target, patch);
|
|
589
|
+
} else if (patch.action === "splice") {
|
|
590
|
+
return handleSplice(target, patch);
|
|
591
|
+
} else if (patch.action === "del") {
|
|
592
|
+
return handleDel(target, patch);
|
|
593
|
+
} else if (patch.action === "put") {
|
|
594
|
+
return handlePut(target, patch, state);
|
|
595
|
+
} else {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
722
598
|
};
|
|
723
|
-
var
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
if (effect.is(setHighlights)) {
|
|
728
|
-
return effect.value;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
return threads;
|
|
599
|
+
var handleInsert = (target, patch) => {
|
|
600
|
+
const index = charPath(target, patch.path);
|
|
601
|
+
if (index == null) {
|
|
602
|
+
return [];
|
|
732
603
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const range = {
|
|
740
|
-
from: selection.from,
|
|
741
|
-
to: selection.to + 1
|
|
742
|
-
};
|
|
743
|
-
if (selection.to > selection.from) {
|
|
744
|
-
if (selection.active) {
|
|
745
|
-
return marks.HighlightActive.range(range.from, range.to);
|
|
746
|
-
} else {
|
|
747
|
-
return marks.highlight.range(range.from, range.to);
|
|
748
|
-
}
|
|
749
|
-
} else {
|
|
750
|
-
return [];
|
|
604
|
+
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
605
|
+
return [
|
|
606
|
+
{
|
|
607
|
+
from: index,
|
|
608
|
+
to: index,
|
|
609
|
+
insert: text
|
|
751
610
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
if (active !== range.active) {
|
|
764
|
-
mod = true;
|
|
765
|
-
return {
|
|
766
|
-
...range,
|
|
767
|
-
active
|
|
768
|
-
};
|
|
769
|
-
} else {
|
|
770
|
-
return range;
|
|
771
|
-
}
|
|
772
|
-
});
|
|
773
|
-
if (mod) {
|
|
774
|
-
view.dispatch({
|
|
775
|
-
effects: setHighlights.of(newRanges)
|
|
776
|
-
});
|
|
777
|
-
options?.onChange?.(newRanges.find((range) => range.active)?.id);
|
|
611
|
+
];
|
|
612
|
+
};
|
|
613
|
+
var handleSplice = (target, patch) => {
|
|
614
|
+
const index = charPath(target, patch.path);
|
|
615
|
+
if (index == null) {
|
|
616
|
+
return [];
|
|
617
|
+
}
|
|
618
|
+
return [
|
|
619
|
+
{
|
|
620
|
+
from: index,
|
|
621
|
+
insert: patch.value
|
|
778
622
|
}
|
|
779
|
-
|
|
623
|
+
];
|
|
780
624
|
};
|
|
781
|
-
var
|
|
625
|
+
var handleDel = (target, patch) => {
|
|
626
|
+
const index = charPath(target, patch.path);
|
|
627
|
+
if (index == null) {
|
|
628
|
+
return [];
|
|
629
|
+
}
|
|
630
|
+
const length = patch.length || 1;
|
|
782
631
|
return [
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
632
|
+
{
|
|
633
|
+
from: index,
|
|
634
|
+
to: index + length
|
|
635
|
+
}
|
|
787
636
|
];
|
|
788
637
|
};
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
create: (state) => update(state, options),
|
|
797
|
-
update: (_, tr) => update(tr.state, options),
|
|
798
|
-
provide: (field) => EditorView6.decorations.from(field)
|
|
799
|
-
});
|
|
800
|
-
};
|
|
801
|
-
var update = (state, options) => {
|
|
802
|
-
const builder = new RangeSetBuilder();
|
|
803
|
-
const cursor = state.selection.main.head;
|
|
804
|
-
syntaxTree(state).iterate({
|
|
805
|
-
enter: (node) => {
|
|
806
|
-
if (node.name === "Image") {
|
|
807
|
-
const urlNode = node.node.getChild("URL");
|
|
808
|
-
if (urlNode) {
|
|
809
|
-
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
810
|
-
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
811
|
-
builder.add(hide ? node.from : node.to, node.to, Decoration4.replace({
|
|
812
|
-
widget: new ImageWidget(url)
|
|
813
|
-
}));
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
});
|
|
818
|
-
return builder.finish();
|
|
819
|
-
};
|
|
820
|
-
var ImageWidget = class extends WidgetType2 {
|
|
821
|
-
constructor(_url) {
|
|
822
|
-
super();
|
|
823
|
-
this._url = _url;
|
|
824
|
-
}
|
|
825
|
-
eq(other) {
|
|
826
|
-
return this._url === other?._url;
|
|
638
|
+
var handlePut = (target, patch, state) => {
|
|
639
|
+
const index = charPath(target, [
|
|
640
|
+
...patch.path,
|
|
641
|
+
0
|
|
642
|
+
]);
|
|
643
|
+
if (index == null) {
|
|
644
|
+
return [];
|
|
827
645
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
img.setAttribute("class", "cm-image");
|
|
832
|
-
return img;
|
|
646
|
+
const length = state.doc.length;
|
|
647
|
+
if (typeof patch.value !== "string") {
|
|
648
|
+
return [];
|
|
833
649
|
}
|
|
650
|
+
return [
|
|
651
|
+
{
|
|
652
|
+
from: 0,
|
|
653
|
+
to: length,
|
|
654
|
+
insert: patch.value
|
|
655
|
+
}
|
|
656
|
+
];
|
|
834
657
|
};
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField3 } from "@codemirror/state";
|
|
839
|
-
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
840
|
-
var link = (options = {}) => {
|
|
841
|
-
return StateField3.define({
|
|
842
|
-
create: (state) => update2(state, options),
|
|
843
|
-
update: (_, tr) => update2(tr.state, options),
|
|
844
|
-
provide: (field) => EditorView7.decorations.from(field)
|
|
845
|
-
});
|
|
846
|
-
};
|
|
847
|
-
var LinkButton = class extends WidgetType3 {
|
|
848
|
-
constructor(_pos, _url, _onAttach) {
|
|
849
|
-
super();
|
|
850
|
-
this._pos = _pos;
|
|
851
|
-
this._url = _url;
|
|
852
|
-
this._onAttach = _onAttach;
|
|
658
|
+
var charPath = (textPath, candidatePath) => {
|
|
659
|
+
if (candidatePath.length !== textPath.length + 1) {
|
|
660
|
+
return null;
|
|
853
661
|
}
|
|
854
|
-
|
|
855
|
-
|
|
662
|
+
for (let i = 0; i < textPath.length; i++) {
|
|
663
|
+
if (textPath[i] !== candidatePath[i]) {
|
|
664
|
+
return null;
|
|
665
|
+
}
|
|
856
666
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
return el;
|
|
667
|
+
const index = candidatePath[candidatePath.length - 1];
|
|
668
|
+
if (typeof index === "number") {
|
|
669
|
+
return index;
|
|
861
670
|
}
|
|
671
|
+
return null;
|
|
862
672
|
};
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
this.
|
|
868
|
-
this.
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
link2.onclick = () => {
|
|
673
|
+
|
|
674
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/semaphore.ts
|
|
675
|
+
var PatchSemaphore = class {
|
|
676
|
+
constructor(field) {
|
|
677
|
+
this._inReconcile = false;
|
|
678
|
+
this._queue = [];
|
|
679
|
+
this.reconcile = (handle, view) => {
|
|
680
|
+
if (this._inReconcile) {
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
this._inReconcile = true;
|
|
684
|
+
const path = getPath(view.state, this._field);
|
|
685
|
+
const oldHeads = getLastHeads(view.state, this._field);
|
|
686
|
+
let selection = view.state.selection;
|
|
687
|
+
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
688
|
+
const toInvert = transactions.slice().reverse();
|
|
689
|
+
for (const tx of toInvert) {
|
|
690
|
+
const inverted = tx.changes.invert(tx.startState.doc);
|
|
691
|
+
selection = selection.map(inverted);
|
|
883
692
|
view.dispatch({
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
}
|
|
693
|
+
changes: inverted,
|
|
694
|
+
annotations: reconcileAnnotationType.of(true)
|
|
887
695
|
});
|
|
888
|
-
}
|
|
696
|
+
}
|
|
697
|
+
let newHeads = updateAutomerge(this._field, handle, transactions, view.state);
|
|
698
|
+
if (newHeads === null || newHeads === void 0) {
|
|
699
|
+
newHeads = automerge.getHeads(handle.docSync());
|
|
700
|
+
}
|
|
701
|
+
const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
|
|
702
|
+
updateCodeMirror(view, selection, path, diff);
|
|
703
|
+
view.dispatch({
|
|
704
|
+
effects: updateHeads(newHeads),
|
|
705
|
+
annotations: reconcileAnnotationType.of({})
|
|
706
|
+
});
|
|
707
|
+
this._inReconcile = false;
|
|
708
|
+
};
|
|
709
|
+
if (field !== void 0) {
|
|
710
|
+
this._field = field;
|
|
889
711
|
}
|
|
890
|
-
return link2;
|
|
891
712
|
}
|
|
892
713
|
};
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
714
|
+
|
|
715
|
+
// packages/ui/react-ui-editor/src/hooks/automerge/plugin.ts
|
|
716
|
+
var effectType = StateEffect.define({});
|
|
717
|
+
var updateHeads = (newHeads) => effectType.of({
|
|
718
|
+
newHeads
|
|
719
|
+
});
|
|
720
|
+
var getLastHeads = (state, field) => state.field(field).lastHeads;
|
|
721
|
+
var getPath = (state, field) => state.field(field).path;
|
|
722
|
+
var semaphoreFacet = Facet.define({
|
|
723
|
+
combine: (values) => values.at(-1)
|
|
724
|
+
});
|
|
725
|
+
var automergePlugin = (handle, path) => {
|
|
726
|
+
const stateField = StateField.define({
|
|
727
|
+
create: () => ({
|
|
728
|
+
lastHeads: automerge2.getHeads(handle.docSync()),
|
|
729
|
+
unreconciledTransactions: [],
|
|
730
|
+
path: path.slice()
|
|
731
|
+
}),
|
|
732
|
+
update: (value, tr) => {
|
|
733
|
+
const result = {
|
|
734
|
+
lastHeads: value.lastHeads,
|
|
735
|
+
unreconciledTransactions: value.unreconciledTransactions.slice(),
|
|
736
|
+
path: path.slice()
|
|
737
|
+
};
|
|
738
|
+
let clearUnreconciled = false;
|
|
739
|
+
for (const effect of tr.effects) {
|
|
740
|
+
if (effect.is(effectType)) {
|
|
741
|
+
result.lastHeads = effect.value.newHeads;
|
|
742
|
+
clearUnreconciled = true;
|
|
910
743
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
744
|
+
}
|
|
745
|
+
if (clearUnreconciled) {
|
|
746
|
+
result.unreconciledTransactions = [];
|
|
747
|
+
} else {
|
|
748
|
+
if (!isReconcileTx(tr)) {
|
|
749
|
+
result.unreconciledTransactions.push(tr);
|
|
915
750
|
}
|
|
916
|
-
return false;
|
|
917
751
|
}
|
|
752
|
+
return result;
|
|
918
753
|
}
|
|
919
754
|
});
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
755
|
+
const semaphore = new PatchSemaphore(stateField);
|
|
756
|
+
const viewPlugin = ViewPlugin2.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
757
|
+
constructor(view) {
|
|
758
|
+
this._handleChange = () => {
|
|
759
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
760
|
+
};
|
|
761
|
+
this._view = view;
|
|
762
|
+
handle.addListener("change", this._handleChange);
|
|
763
|
+
}
|
|
764
|
+
update(update4) {
|
|
765
|
+
if (update4.transactions.length > 0 && update4.transactions.some((t) => !isReconcileTx(t))) {
|
|
766
|
+
queueMicrotask(() => {
|
|
767
|
+
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
768
|
+
});
|
|
931
769
|
}
|
|
932
|
-
|
|
770
|
+
}
|
|
771
|
+
destroy() {
|
|
772
|
+
handle.addListener("change", this._handleChange);
|
|
933
773
|
}
|
|
934
774
|
});
|
|
775
|
+
return {
|
|
776
|
+
extension: [
|
|
777
|
+
stateField,
|
|
778
|
+
semaphoreFacet.of(semaphore),
|
|
779
|
+
viewPlugin
|
|
780
|
+
]
|
|
781
|
+
};
|
|
935
782
|
};
|
|
783
|
+
var reconcileAnnotationType = Annotation.define();
|
|
784
|
+
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
936
785
|
|
|
937
|
-
// packages/ui/react-ui-editor/src/
|
|
938
|
-
import
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
import { searchKeymap } from "@codemirror/search";
|
|
944
|
-
import { EditorState } from "@codemirror/state";
|
|
945
|
-
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
946
|
-
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView8, highlightActiveLine, keymap as keymap5, placeholder as placeholder2 } from "@codemirror/view";
|
|
947
|
-
|
|
948
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/highlight.ts
|
|
949
|
-
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
950
|
-
import { HighlightStyle } from "@codemirror/language";
|
|
951
|
-
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
952
|
-
import { Table } from "@lezer/markdown";
|
|
953
|
-
import get2 from "lodash.get";
|
|
954
|
-
import { mx as mx3 } from "@dxos/react-ui-theme";
|
|
955
|
-
var markdownTags = {
|
|
956
|
-
Blockquote: Tag.define(),
|
|
957
|
-
CodeMark: Tag.define(),
|
|
958
|
-
CodeText: Tag.define(),
|
|
959
|
-
EmphasisMark: Tag.define(),
|
|
960
|
-
HeaderMark: Tag.define(),
|
|
961
|
-
HorizontalRule: Tag.define(),
|
|
962
|
-
InlineCode: Tag.define(),
|
|
963
|
-
LinkLabel: Tag.define(),
|
|
964
|
-
LinkReference: Tag.define(),
|
|
965
|
-
ListMark: Tag.define(),
|
|
966
|
-
QuoteMark: Tag.define(),
|
|
967
|
-
URL: Tag.define(),
|
|
968
|
-
// Custom.
|
|
969
|
-
TableCell: Tag.define()
|
|
970
|
-
};
|
|
971
|
-
Table.defineNodes?.forEach((node) => {
|
|
972
|
-
switch (node?.name) {
|
|
973
|
-
case "TableCell": {
|
|
974
|
-
node.style = markdownTags.TableCell;
|
|
975
|
-
break;
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
});
|
|
979
|
-
var markdownTagsExtensions = [
|
|
980
|
-
Table,
|
|
981
|
-
{
|
|
982
|
-
props: [
|
|
983
|
-
styleTags(markdownTags)
|
|
984
|
-
]
|
|
985
|
-
}
|
|
986
|
-
];
|
|
987
|
-
var markdownHighlightStyle = (readonly) => {
|
|
988
|
-
return HighlightStyle.define([
|
|
989
|
-
{
|
|
990
|
-
tag: [
|
|
991
|
-
tags.keyword,
|
|
992
|
-
tags.name,
|
|
993
|
-
tags.deleted,
|
|
994
|
-
tags.character,
|
|
995
|
-
tags.propertyName,
|
|
996
|
-
tags.macroName,
|
|
997
|
-
tags.color,
|
|
998
|
-
tags.constant(tags.name),
|
|
999
|
-
tags.standard(tags.name),
|
|
1000
|
-
tags.definition(tags.name),
|
|
1001
|
-
tags.separator,
|
|
1002
|
-
tags.typeName,
|
|
1003
|
-
tags.className,
|
|
1004
|
-
tags.number,
|
|
1005
|
-
tags.changed,
|
|
1006
|
-
tags.annotation,
|
|
1007
|
-
tags.modifier,
|
|
1008
|
-
tags.self,
|
|
1009
|
-
tags.namespace,
|
|
1010
|
-
tags.operator,
|
|
1011
|
-
tags.operatorKeyword,
|
|
1012
|
-
tags.escape,
|
|
1013
|
-
tags.regexp,
|
|
1014
|
-
tags.special(tags.string),
|
|
1015
|
-
tags.meta,
|
|
1016
|
-
tags.comment,
|
|
1017
|
-
tags.atom,
|
|
1018
|
-
tags.bool,
|
|
1019
|
-
tags.special(tags.variableName),
|
|
1020
|
-
tags.processingInstruction,
|
|
1021
|
-
tags.string,
|
|
1022
|
-
tags.inserted,
|
|
1023
|
-
tags.invalid
|
|
1024
|
-
],
|
|
1025
|
-
color: "inherit !important"
|
|
1026
|
-
},
|
|
1027
|
-
// Markdown marks.
|
|
1028
|
-
{
|
|
1029
|
-
tag: [
|
|
1030
|
-
tags.meta,
|
|
1031
|
-
tags.processingInstruction,
|
|
1032
|
-
markdownTags.LinkLabel,
|
|
1033
|
-
markdownTags.LinkReference,
|
|
1034
|
-
markdownTags.ListMark
|
|
1035
|
-
],
|
|
1036
|
-
class: mark
|
|
1037
|
-
},
|
|
1038
|
-
// Markdown marks.
|
|
1039
|
-
{
|
|
1040
|
-
tag: [
|
|
1041
|
-
markdownTags.CodeMark,
|
|
1042
|
-
markdownTags.HeaderMark,
|
|
1043
|
-
markdownTags.QuoteMark,
|
|
1044
|
-
markdownTags.EmphasisMark
|
|
1045
|
-
],
|
|
1046
|
-
class: readonly ? "hidden" : mark
|
|
1047
|
-
},
|
|
1048
|
-
// E.g., code block language (after ```).
|
|
1049
|
-
{
|
|
1050
|
-
tag: [
|
|
1051
|
-
tags.function(tags.variableName),
|
|
1052
|
-
tags.labelName
|
|
1053
|
-
],
|
|
1054
|
-
class: readonly ? "hidden" : codeMark
|
|
1055
|
-
},
|
|
1056
|
-
{
|
|
1057
|
-
tag: [
|
|
1058
|
-
tags.monospace
|
|
1059
|
-
],
|
|
1060
|
-
class: "font-mono"
|
|
1061
|
-
},
|
|
1062
|
-
// Headings.
|
|
1063
|
-
{
|
|
1064
|
-
tag: tags.heading1,
|
|
1065
|
-
class: heading(1, readonly)
|
|
1066
|
-
},
|
|
1067
|
-
{
|
|
1068
|
-
tag: tags.heading2,
|
|
1069
|
-
class: heading(2, readonly)
|
|
1070
|
-
},
|
|
1071
|
-
{
|
|
1072
|
-
tag: tags.heading3,
|
|
1073
|
-
class: heading(3, readonly)
|
|
1074
|
-
},
|
|
1075
|
-
{
|
|
1076
|
-
tag: tags.heading4,
|
|
1077
|
-
class: heading(4, readonly)
|
|
1078
|
-
},
|
|
1079
|
-
{
|
|
1080
|
-
tag: tags.heading5,
|
|
1081
|
-
class: heading(5, readonly)
|
|
1082
|
-
},
|
|
1083
|
-
{
|
|
1084
|
-
tag: tags.heading6,
|
|
1085
|
-
class: heading(6, readonly)
|
|
1086
|
-
},
|
|
1087
|
-
// Emphasis.
|
|
1088
|
-
{
|
|
1089
|
-
tag: tags.emphasis,
|
|
1090
|
-
class: italic
|
|
1091
|
-
},
|
|
1092
|
-
{
|
|
1093
|
-
tag: tags.strong,
|
|
1094
|
-
class: bold
|
|
1095
|
-
},
|
|
1096
|
-
{
|
|
1097
|
-
tag: tags.strikethrough,
|
|
1098
|
-
class: strikethrough
|
|
1099
|
-
},
|
|
1100
|
-
// Naked URLs.
|
|
1101
|
-
{
|
|
1102
|
-
tag: [
|
|
1103
|
-
markdownTags.URL
|
|
1104
|
-
],
|
|
1105
|
-
class: inlineUrl
|
|
1106
|
-
},
|
|
1107
|
-
// NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
1108
|
-
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
1109
|
-
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
1110
|
-
// the code, but all other CSS properties will be inherited.
|
|
1111
|
-
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
1112
|
-
{
|
|
1113
|
-
tag: [
|
|
1114
|
-
markdownTags.CodeText,
|
|
1115
|
-
markdownTags.InlineCode
|
|
1116
|
-
],
|
|
1117
|
-
class: code
|
|
1118
|
-
},
|
|
1119
|
-
{
|
|
1120
|
-
tag: [
|
|
1121
|
-
markdownTags.QuoteMark
|
|
1122
|
-
],
|
|
1123
|
-
class: blockquote
|
|
1124
|
-
},
|
|
1125
|
-
// TODO(burdon): Replace with extension.
|
|
1126
|
-
{
|
|
1127
|
-
tag: [
|
|
1128
|
-
markdownTags.HorizontalRule
|
|
1129
|
-
],
|
|
1130
|
-
class: mx3(horizontalRule, readonly && "-indent-[100rem]")
|
|
1131
|
-
},
|
|
1132
|
-
// TODO(burdon): Only if being edited.
|
|
1133
|
-
{
|
|
1134
|
-
tag: [
|
|
1135
|
-
markdownTags.TableCell
|
|
1136
|
-
],
|
|
1137
|
-
class: "font-mono"
|
|
1138
|
-
}
|
|
1139
|
-
], {
|
|
1140
|
-
scope: markdownLanguage,
|
|
1141
|
-
all: {
|
|
1142
|
-
fontFamily: get2(tokens, "fontFamily.body", []).join(",")
|
|
1143
|
-
}
|
|
1144
|
-
});
|
|
1145
|
-
};
|
|
1146
|
-
|
|
1147
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/markdown/bundle.ts
|
|
1148
|
-
var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
1149
|
-
return [
|
|
1150
|
-
EditorState.allowMultipleSelections.of(true),
|
|
1151
|
-
EditorState.tabSize.of(2),
|
|
1152
|
-
EditorView8.lineWrapping,
|
|
1153
|
-
customKeymap(),
|
|
1154
|
-
bracketMatching2(),
|
|
1155
|
-
closeBrackets2(),
|
|
1156
|
-
crosshairCursor(),
|
|
1157
|
-
dropCursor(),
|
|
1158
|
-
drawSelection2(),
|
|
1159
|
-
highlightActiveLine(),
|
|
1160
|
-
history(),
|
|
1161
|
-
indentOnInput(),
|
|
1162
|
-
_placeholder && placeholder2(_placeholder),
|
|
1163
|
-
// Main extension.
|
|
1164
|
-
// https://github.com/codemirror/lang-markdown
|
|
1165
|
-
// https://codemirror.net/5/mode/markdown/index.html (demo).
|
|
1166
|
-
markdown({
|
|
1167
|
-
// GRM by default (vs strict CommonMark):
|
|
1168
|
-
// Table, TaskList, Strikethrough, and Autolink.
|
|
1169
|
-
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
1170
|
-
// https://github.github.com/gfm
|
|
1171
|
-
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
1172
|
-
base: markdownLanguage2,
|
|
1173
|
-
// Languages for syntax highlighting fenced code blocks.
|
|
1174
|
-
codeLanguages: languages,
|
|
1175
|
-
// Parser extensions.
|
|
1176
|
-
extensions: [
|
|
1177
|
-
// GFM provided by default.
|
|
1178
|
-
markdownTagsExtensions
|
|
1179
|
-
]
|
|
1180
|
-
}),
|
|
1181
|
-
// https://github.com/codemirror/theme-one-dark
|
|
1182
|
-
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
|
|
1183
|
-
// Custom styles.
|
|
1184
|
-
syntaxHighlighting2(markdownHighlightStyle(readonly))
|
|
1185
|
-
].filter(Boolean);
|
|
1186
|
-
};
|
|
1187
|
-
var customKeymap = () => keymap5.of([
|
|
1188
|
-
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
1189
|
-
indentWithTab,
|
|
1190
|
-
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
1191
|
-
...standardKeymap,
|
|
1192
|
-
// https://codemirror.net/docs/ref/#commands.historyKeymap
|
|
1193
|
-
...historyKeymap,
|
|
1194
|
-
// https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
|
|
1195
|
-
...closeBracketsKeymap,
|
|
1196
|
-
// https://codemirror.net/docs/ref/#search.searchKeymap
|
|
1197
|
-
...searchKeymap
|
|
1198
|
-
]);
|
|
1199
|
-
|
|
1200
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/mention.ts
|
|
1201
|
-
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
1202
|
-
var mention = ({ onSearch }) => {
|
|
1203
|
-
return autocompletion2({
|
|
1204
|
-
closeOnBlur: false,
|
|
1205
|
-
override: [
|
|
1206
|
-
(context) => {
|
|
1207
|
-
const match = context.matchBefore(/@(\w+)?/);
|
|
1208
|
-
if (!match || match.from === match.to && !context.explicit) {
|
|
1209
|
-
return null;
|
|
1210
|
-
}
|
|
1211
|
-
console.log(match);
|
|
1212
|
-
return {
|
|
1213
|
-
from: match.from,
|
|
1214
|
-
options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
|
|
1215
|
-
label: `@${value}`
|
|
1216
|
-
}))
|
|
1217
|
-
};
|
|
1218
|
-
}
|
|
1219
|
-
]
|
|
1220
|
-
});
|
|
1221
|
-
};
|
|
1222
|
-
|
|
1223
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/table.ts
|
|
1224
|
-
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1225
|
-
import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField5 } from "@codemirror/state";
|
|
1226
|
-
import { Decoration as Decoration6, EditorView as EditorView9, WidgetType as WidgetType4 } from "@codemirror/view";
|
|
1227
|
-
var table = (options = {}) => {
|
|
1228
|
-
return StateField5.define({
|
|
1229
|
-
create: (state) => update3(state, options),
|
|
1230
|
-
update: (_, tr) => update3(tr.state, options),
|
|
1231
|
-
provide: (field) => EditorView9.decorations.from(field)
|
|
1232
|
-
});
|
|
1233
|
-
};
|
|
1234
|
-
var update3 = (state, options) => {
|
|
1235
|
-
const builder = new RangeSetBuilder3();
|
|
1236
|
-
const cursor = state.selection.main.head;
|
|
1237
|
-
const tables = [];
|
|
1238
|
-
const getTable = () => tables[tables.length - 1];
|
|
1239
|
-
const getRow = () => {
|
|
1240
|
-
const table2 = getTable();
|
|
1241
|
-
return table2.rows?.[table2.rows.length - 1];
|
|
1242
|
-
};
|
|
1243
|
-
syntaxTree3(state).iterate({
|
|
1244
|
-
enter: (node) => {
|
|
1245
|
-
switch (node.name) {
|
|
1246
|
-
case "Table": {
|
|
1247
|
-
tables.push({
|
|
1248
|
-
from: node.from,
|
|
1249
|
-
to: node.to
|
|
1250
|
-
});
|
|
1251
|
-
break;
|
|
1252
|
-
}
|
|
1253
|
-
case "TableHeader": {
|
|
1254
|
-
getTable().header = [];
|
|
1255
|
-
break;
|
|
1256
|
-
}
|
|
1257
|
-
case "TableRow": {
|
|
1258
|
-
(getTable().rows ??= []).push([]);
|
|
1259
|
-
break;
|
|
1260
|
-
}
|
|
1261
|
-
case "TableCell": {
|
|
1262
|
-
const row = getRow();
|
|
1263
|
-
if (row) {
|
|
1264
|
-
row.push(state.sliceDoc(node.from, node.to));
|
|
1265
|
-
} else {
|
|
1266
|
-
getTable().header?.push(state.sliceDoc(node.from, node.to));
|
|
1267
|
-
}
|
|
1268
|
-
break;
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
}
|
|
1272
|
-
});
|
|
1273
|
-
tables.forEach((table2) => {
|
|
1274
|
-
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1275
|
-
hide && builder.add(table2.from, table2.to, Decoration6.replace({
|
|
1276
|
-
widget: new TableWidget(table2)
|
|
1277
|
-
}));
|
|
1278
|
-
builder.add(table2.from, table2.to, Decoration6.mark({
|
|
1279
|
-
class: "cm-table"
|
|
1280
|
-
}));
|
|
1281
|
-
});
|
|
1282
|
-
return builder.finish();
|
|
1283
|
-
};
|
|
1284
|
-
var TableWidget = class extends WidgetType4 {
|
|
1285
|
-
constructor(_table) {
|
|
1286
|
-
super();
|
|
1287
|
-
this._table = _table;
|
|
1288
|
-
}
|
|
1289
|
-
eq(other) {
|
|
1290
|
-
return this._table.from === other?._table?.from;
|
|
1291
|
-
}
|
|
1292
|
-
toDOM(view) {
|
|
1293
|
-
const table2 = document.createElement("table");
|
|
1294
|
-
{
|
|
1295
|
-
const header = table2.appendChild(document.createElement("thead"));
|
|
1296
|
-
const tr = header.appendChild(document.createElement("tr"));
|
|
1297
|
-
this._table.header?.forEach((cell) => {
|
|
1298
|
-
const th = document.createElement("th");
|
|
1299
|
-
th.setAttribute("class", "cm-table-head");
|
|
1300
|
-
tr.appendChild(th).textContent = cell;
|
|
1301
|
-
});
|
|
1302
|
-
}
|
|
1303
|
-
{
|
|
1304
|
-
const body = table2.appendChild(document.createElement("tbody"));
|
|
1305
|
-
this._table.rows?.forEach((row) => {
|
|
1306
|
-
const tr = body.appendChild(document.createElement("tr"));
|
|
1307
|
-
row.forEach((cell) => {
|
|
1308
|
-
const td = document.createElement("td");
|
|
1309
|
-
td.setAttribute("class", "cm-table-cell");
|
|
1310
|
-
tr.appendChild(td).textContent = cell;
|
|
1311
|
-
});
|
|
1312
|
-
});
|
|
1313
|
-
}
|
|
1314
|
-
return table2;
|
|
1315
|
-
}
|
|
1316
|
-
};
|
|
1317
|
-
|
|
1318
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/tasklist.ts
|
|
1319
|
-
import { EditorView as EditorView10, Decoration as Decoration7, WidgetType as WidgetType5, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin3 } from "@codemirror/view";
|
|
1320
|
-
var styles4 = EditorView10.baseTheme({
|
|
1321
|
-
"& .cm-task-item": {
|
|
1322
|
-
paddingLeft: "4px",
|
|
1323
|
-
paddingRight: "8px"
|
|
1324
|
-
}
|
|
1325
|
-
});
|
|
1326
|
-
var CheckboxWidget = class extends WidgetType5 {
|
|
1327
|
-
constructor(_pos, _checked, _indent, _onCheck) {
|
|
1328
|
-
super();
|
|
1329
|
-
this._pos = _pos;
|
|
1330
|
-
this._checked = _checked;
|
|
1331
|
-
this._indent = _indent;
|
|
1332
|
-
this._onCheck = _onCheck;
|
|
1333
|
-
}
|
|
1334
|
-
eq(other) {
|
|
1335
|
-
return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
|
|
1336
|
-
}
|
|
1337
|
-
toDOM(view) {
|
|
1338
|
-
const wrap = document.createElement("span");
|
|
1339
|
-
wrap.className = "cm-task-item";
|
|
1340
|
-
wrap.setAttribute("aria-hidden", "true");
|
|
1341
|
-
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
1342
|
-
const input = wrap.appendChild(document.createElement("input"));
|
|
1343
|
-
input.type = "checkbox";
|
|
1344
|
-
input.checked = this._checked;
|
|
1345
|
-
if (!this._onCheck) {
|
|
1346
|
-
input.setAttribute("disabled", "true");
|
|
1347
|
-
}
|
|
1348
|
-
if (this._onCheck) {
|
|
1349
|
-
input.onchange = (event) => {
|
|
1350
|
-
this._onCheck?.(event.target.checked);
|
|
1351
|
-
return true;
|
|
1352
|
-
};
|
|
1353
|
-
}
|
|
1354
|
-
return wrap;
|
|
1355
|
-
}
|
|
1356
|
-
ignoreEvent() {
|
|
1357
|
-
return false;
|
|
1358
|
-
}
|
|
1359
|
-
};
|
|
1360
|
-
var tasklist = (options = {}) => {
|
|
1361
|
-
const taskMatcher = new MatchDecorator2({
|
|
1362
|
-
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
1363
|
-
decoration: (match, view, pos) => {
|
|
1364
|
-
const indent = Math.floor(match[1].length / 2);
|
|
1365
|
-
const checked = match[2] === "x" || match[2] === "X";
|
|
1366
|
-
return Decoration7.replace({
|
|
1367
|
-
widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
1368
|
-
const idx = pos + match[0].indexOf("[") + 1;
|
|
1369
|
-
view.dispatch({
|
|
1370
|
-
changes: {
|
|
1371
|
-
from: idx,
|
|
1372
|
-
to: idx + 1,
|
|
1373
|
-
insert: checked2 ? "x" : " "
|
|
1374
|
-
},
|
|
1375
|
-
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
1376
|
-
selection: {
|
|
1377
|
-
anchor: idx + 3
|
|
1378
|
-
}
|
|
1379
|
-
});
|
|
1380
|
-
})
|
|
1381
|
-
});
|
|
1382
|
-
}
|
|
1383
|
-
});
|
|
1384
|
-
const tasks = ViewPlugin3.fromClass(class {
|
|
1385
|
-
constructor(view) {
|
|
1386
|
-
this.tasks = taskMatcher.createDeco(view);
|
|
1387
|
-
}
|
|
1388
|
-
update(update4) {
|
|
1389
|
-
this.tasks = taskMatcher.updateDeco(update4, this.tasks);
|
|
1390
|
-
}
|
|
1391
|
-
}, {
|
|
1392
|
-
decorations: (value) => value.tasks,
|
|
1393
|
-
provide: (plugin) => EditorView10.atomicRanges.of((view) => {
|
|
1394
|
-
return view.plugin(plugin)?.tasks || Decoration7.none;
|
|
1395
|
-
})
|
|
1396
|
-
});
|
|
1397
|
-
return [
|
|
1398
|
-
tasks,
|
|
1399
|
-
styles4
|
|
1400
|
-
];
|
|
1401
|
-
};
|
|
1402
|
-
|
|
1403
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/extensions/tooltip.tsx
|
|
1404
|
-
import { hoverTooltip } from "@codemirror/view";
|
|
1405
|
-
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1406
|
-
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1407
|
-
var tooltip = ({ onHover, regexp = markdownLinkRegexp }) => hoverTooltip((view, pos) => {
|
|
1408
|
-
const { from, text } = view.state.doc.lineAt(pos);
|
|
1409
|
-
const p = pos - from;
|
|
1410
|
-
let idx = 0;
|
|
1411
|
-
let match;
|
|
1412
|
-
do {
|
|
1413
|
-
match = text.substring(idx).match(regexp);
|
|
1414
|
-
if (!match) {
|
|
1415
|
-
match = void 0;
|
|
1416
|
-
break;
|
|
1417
|
-
}
|
|
1418
|
-
idx = text.indexOf(match[0]);
|
|
1419
|
-
if (p >= idx && p < idx + match[0].length) {
|
|
1420
|
-
break;
|
|
1421
|
-
}
|
|
1422
|
-
idx += match[0].length;
|
|
1423
|
-
} while (true);
|
|
1424
|
-
if (!match) {
|
|
1425
|
-
return null;
|
|
1426
|
-
}
|
|
1427
|
-
const [, , url] = match ?? [];
|
|
1428
|
-
return {
|
|
1429
|
-
pos: idx + from,
|
|
1430
|
-
end: idx + from + match[0].length,
|
|
1431
|
-
above: true,
|
|
1432
|
-
create: () => {
|
|
1433
|
-
const el = document.createElement("div");
|
|
1434
|
-
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1435
|
-
onHover(el, url);
|
|
1436
|
-
return {
|
|
1437
|
-
dom: el,
|
|
1438
|
-
offset: {
|
|
1439
|
-
x: 0,
|
|
1440
|
-
y: 12
|
|
1441
|
-
}
|
|
1442
|
-
};
|
|
1443
|
-
}
|
|
1444
|
-
};
|
|
1445
|
-
});
|
|
1446
|
-
|
|
1447
|
-
// packages/ui/react-ui-editor/src/components/TextEditor/themes/default.ts
|
|
1448
|
-
import get3 from "lodash.get";
|
|
1449
|
-
var defaultTheme = {
|
|
1450
|
-
//
|
|
1451
|
-
// Main layout:
|
|
1452
|
-
// <div class=".cm-editor .cm-focused">
|
|
1453
|
-
// <div class=".cm-scroller">
|
|
1454
|
-
// <div class=".cm-content" role="textbox" contenteditable="true">
|
|
1455
|
-
// <div class=".cm-line" />
|
|
1456
|
-
// </div>
|
|
1457
|
-
// </div>
|
|
1458
|
-
// </div>
|
|
1459
|
-
//
|
|
1460
|
-
"&": {},
|
|
1461
|
-
"&.cm-focused": {
|
|
1462
|
-
outline: "none"
|
|
1463
|
-
},
|
|
1464
|
-
"& .cm-scroller": {
|
|
1465
|
-
overflow: "visible",
|
|
1466
|
-
fontFamily: get3(tokens, "fontFamily.mono", []).join(",")
|
|
1467
|
-
},
|
|
1468
|
-
"& .cm-content": {
|
|
1469
|
-
padding: 0,
|
|
1470
|
-
// Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
1471
|
-
fontSize: "16px"
|
|
1472
|
-
},
|
|
1473
|
-
"&light .cm-content": {
|
|
1474
|
-
color: get3(tokens, "extend.colors.neutral.900", "black"),
|
|
1475
|
-
caretColor: "black"
|
|
1476
|
-
},
|
|
1477
|
-
"&dark .cm-content": {
|
|
1478
|
-
color: get3(tokens, "extend.colors.neutral.100", "white"),
|
|
1479
|
-
caretColor: "white"
|
|
1480
|
-
},
|
|
1481
|
-
//
|
|
1482
|
-
// Cursor
|
|
1483
|
-
//
|
|
1484
|
-
"&light .cm-cursor, &light .cm-dropCursor": {
|
|
1485
|
-
borderLeft: "2px solid black"
|
|
1486
|
-
},
|
|
1487
|
-
"&dark .cm-cursor, &dark .cm-dropCursor": {
|
|
1488
|
-
borderLeft: "2px solid white"
|
|
1489
|
-
},
|
|
1490
|
-
"& .cm-placeholder": {
|
|
1491
|
-
fontWeight: 100
|
|
1492
|
-
},
|
|
1493
|
-
//
|
|
1494
|
-
// line
|
|
1495
|
-
//
|
|
1496
|
-
"& .cm-line": {
|
|
1497
|
-
paddingInline: 0
|
|
1498
|
-
},
|
|
1499
|
-
"& .cm-line *": {},
|
|
1500
|
-
"& .cm-activeLine": {
|
|
1501
|
-
backgroundColor: "transparent"
|
|
1502
|
-
},
|
|
1503
|
-
//
|
|
1504
|
-
// selection
|
|
1505
|
-
//
|
|
1506
|
-
"&light .cm-selectionBackground, &light.cm-focused .cm-selectionBackground": {
|
|
1507
|
-
background: get3(tokens, "extend.colors.primary.150", "#00ffff")
|
|
1508
|
-
},
|
|
1509
|
-
"&dark .cm-selectionBackground, &dark.cm-focused .cm-selectionBackground": {
|
|
1510
|
-
background: get3(tokens, "extend.colors.primary.850", "#00ffff")
|
|
1511
|
-
},
|
|
1512
|
-
"&light .cm-selectionMatch": {
|
|
1513
|
-
background: get3(tokens, "extend.colors.primary.250", "#00ffff") + "44"
|
|
1514
|
-
},
|
|
1515
|
-
"&dark .cm-selectionMatch": {
|
|
1516
|
-
background: get3(tokens, "extend.colors.primary.600", "#00ffff") + "44"
|
|
1517
|
-
},
|
|
1518
|
-
//
|
|
1519
|
-
// collaboration
|
|
1520
|
-
// TODO(burdon): Review classnames (YJS dependent?)
|
|
1521
|
-
//
|
|
1522
|
-
"&light .cm-ySelection, &light .cm-yLineSelection": {
|
|
1523
|
-
mixBlendMode: "multiply"
|
|
1524
|
-
},
|
|
1525
|
-
"&dark .cm-ySelection, &dark .cm-yLineSelection": {
|
|
1526
|
-
mixBlendMode: "screen"
|
|
1527
|
-
},
|
|
1528
|
-
"& .cm-ySelectionInfo": {
|
|
1529
|
-
padding: "2px 4px",
|
|
1530
|
-
marginBlockStart: "-4px"
|
|
1531
|
-
},
|
|
1532
|
-
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
1533
|
-
paddingBlockStart: ".15em",
|
|
1534
|
-
paddingBlockEnd: ".15em"
|
|
1535
|
-
},
|
|
1536
|
-
"& .cm-ySelectionCaret": {
|
|
1537
|
-
display: "inline-block",
|
|
1538
|
-
insetBlockStart: ".1em",
|
|
1539
|
-
blockSize: "1.4em",
|
|
1540
|
-
verticalAlign: "top"
|
|
1541
|
-
},
|
|
1542
|
-
"& .cm-yLineSelection": {
|
|
1543
|
-
margin: 0
|
|
1544
|
-
},
|
|
1545
|
-
//
|
|
1546
|
-
// link
|
|
1547
|
-
//
|
|
1548
|
-
"& .cm-link": {
|
|
1549
|
-
color: get3(tokens, "extend.colors.primary.500"),
|
|
1550
|
-
textDecorationLine: "underline",
|
|
1551
|
-
textDecorationThickness: "1px",
|
|
1552
|
-
textUnderlineOffset: "2px",
|
|
1553
|
-
borderRadius: ".125rem",
|
|
1554
|
-
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1555
|
-
},
|
|
1556
|
-
//
|
|
1557
|
-
// tooltip
|
|
1558
|
-
//
|
|
1559
|
-
"& .cm-tooltip": {
|
|
1560
|
-
border: "none"
|
|
1561
|
-
},
|
|
1562
|
-
"& .cm-tooltip-below": {},
|
|
1563
|
-
//
|
|
1564
|
-
// autocomplete
|
|
1565
|
-
//
|
|
1566
|
-
"& .cm-tooltip-autocomplete": {
|
|
1567
|
-
marginTop: "4px",
|
|
1568
|
-
marginLeft: "-3px"
|
|
1569
|
-
},
|
|
1570
|
-
"& .cm-tooltip-autocomplete ul li": {},
|
|
1571
|
-
"& .cm-tooltip-autocomplete ul li[aria-selected]": {},
|
|
1572
|
-
"& .cm-completionIcon": {
|
|
1573
|
-
display: "none"
|
|
1574
|
-
},
|
|
1575
|
-
"& .cm-completionLabel": {
|
|
1576
|
-
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
1577
|
-
},
|
|
1578
|
-
"& .cm-completionMatchedText": {
|
|
1579
|
-
textDecoration: "none"
|
|
1580
|
-
},
|
|
1581
|
-
//
|
|
1582
|
-
// widgets
|
|
1583
|
-
//
|
|
1584
|
-
"& .cm-widgetBuffer": {
|
|
1585
|
-
display: "none",
|
|
1586
|
-
height: 0
|
|
1587
|
-
},
|
|
1588
|
-
//
|
|
1589
|
-
// table
|
|
1590
|
-
//
|
|
1591
|
-
"& .cm-table *": {
|
|
1592
|
-
fontFamily: `${get3(tokens, "fontFamily.mono", []).join(",")} !important`,
|
|
1593
|
-
textDecoration: "none !important"
|
|
1594
|
-
},
|
|
1595
|
-
"& .cm-table-head": {
|
|
1596
|
-
padding: "2px 16px 2px 0px",
|
|
1597
|
-
borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.500")}`,
|
|
1598
|
-
fontWeight: 100,
|
|
1599
|
-
textAlign: "left",
|
|
1600
|
-
color: get3(tokens, "extend.colors.neutral.500")
|
|
1601
|
-
},
|
|
1602
|
-
"& .cm-table-cell": {
|
|
1603
|
-
padding: "2px 16px 2px 0px"
|
|
1604
|
-
},
|
|
1605
|
-
//
|
|
1606
|
-
// image
|
|
1607
|
-
//
|
|
1608
|
-
"& .cm-image": {
|
|
1609
|
-
margin: "0.5rem 0"
|
|
1610
|
-
},
|
|
1611
|
-
//
|
|
1612
|
-
// font size
|
|
1613
|
-
// TODO(thure): This appears to be the best or only way to set selection caret heights, but it's far more verbose than it needs to be.
|
|
1614
|
-
//
|
|
1615
|
-
...Object.keys(get3(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
1616
|
-
const height = get3(tokens, [
|
|
1617
|
-
"extend",
|
|
1618
|
-
"fontSize",
|
|
1619
|
-
fontSize,
|
|
1620
|
-
1,
|
|
1621
|
-
"lineHeight"
|
|
1622
|
-
]);
|
|
1623
|
-
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
1624
|
-
height
|
|
1625
|
-
};
|
|
1626
|
-
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
1627
|
-
height
|
|
1628
|
-
};
|
|
1629
|
-
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
1630
|
-
height
|
|
1631
|
-
};
|
|
1632
|
-
return acc;
|
|
1633
|
-
}, {}),
|
|
1634
|
-
/**
|
|
1635
|
-
* Panels
|
|
1636
|
-
* https://github.com/codemirror/search/blob/main/src/search.ts#L745
|
|
1637
|
-
*
|
|
1638
|
-
* Find/replace panel.
|
|
1639
|
-
* <div class="cm-announced">...</div>
|
|
1640
|
-
* <div class="cm-scroller">...</div>
|
|
1641
|
-
* <div class="cm-panels cm-panels-bottom">
|
|
1642
|
-
* <div class="cm-search cm-panel">
|
|
1643
|
-
* <input class="cm-textfield" />
|
|
1644
|
-
* <button class="cm-button">...</button>
|
|
1645
|
-
* <label><input type="checkbox" />...</label>
|
|
1646
|
-
* </div>
|
|
1647
|
-
* </div
|
|
1648
|
-
*/
|
|
1649
|
-
"& .cm-panels": {
|
|
1650
|
-
border: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`,
|
|
1651
|
-
borderBottom: "none"
|
|
786
|
+
// packages/ui/react-ui-editor/src/hooks/yjs/cursors.ts
|
|
787
|
+
import * as random2 from "lib0/random";
|
|
788
|
+
var cursorColors = [
|
|
789
|
+
{
|
|
790
|
+
color: "#30bced",
|
|
791
|
+
light: "#30bced33"
|
|
1652
792
|
},
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
793
|
+
{
|
|
794
|
+
color: "#6eeb83",
|
|
795
|
+
light: "#6eeb8333"
|
|
1656
796
|
},
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
background: get3(tokens, "extend.colors.neutral.100"),
|
|
1661
|
-
border: "none"
|
|
797
|
+
{
|
|
798
|
+
color: "#ffbc42",
|
|
799
|
+
light: "#ffbc4233"
|
|
1662
800
|
},
|
|
1663
|
-
|
|
1664
|
-
|
|
801
|
+
{
|
|
802
|
+
color: "#ecd444",
|
|
803
|
+
light: "#ecd44433"
|
|
1665
804
|
},
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
};
|
|
1670
|
-
var textTheme = {
|
|
1671
|
-
"& .cm-scroller": {
|
|
1672
|
-
fontFamily: get3(tokens, "fontFamily.body", []).join(",")
|
|
805
|
+
{
|
|
806
|
+
color: "#ee6352",
|
|
807
|
+
light: "#ee635233"
|
|
1673
808
|
},
|
|
1674
|
-
|
|
1675
|
-
|
|
809
|
+
{
|
|
810
|
+
color: "#9ac2c9",
|
|
811
|
+
light: "#9ac2c933"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
color: "#8acb88",
|
|
815
|
+
light: "#8acb8833"
|
|
816
|
+
},
|
|
817
|
+
{
|
|
818
|
+
color: "#1be7ff",
|
|
819
|
+
light: "#1be7ff33"
|
|
820
|
+
}
|
|
821
|
+
];
|
|
822
|
+
var cursorColor = cursorColors[random2.uint32() % cursorColors.length];
|
|
823
|
+
|
|
824
|
+
// packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts
|
|
825
|
+
import * as decoding from "lib0/decoding";
|
|
826
|
+
import * as encoding from "lib0/encoding";
|
|
827
|
+
import { Observable } from "lib0/observable";
|
|
828
|
+
import * as awarenessProtocol from "y-protocols/awareness";
|
|
829
|
+
import { log as log2 } from "@dxos/log";
|
|
830
|
+
var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/yjs/space-provider.ts";
|
|
831
|
+
var messageAwareness = 1;
|
|
832
|
+
var messageQueryAwareness = 3;
|
|
833
|
+
var SpaceAwarenessProvider = class extends Observable {
|
|
834
|
+
constructor({ space, doc, channel, awareness }) {
|
|
835
|
+
super();
|
|
836
|
+
this._space = space;
|
|
837
|
+
this._awareness = awareness ?? new awarenessProtocol.Awareness(doc);
|
|
838
|
+
this._channel = channel;
|
|
839
|
+
this._clientId = doc.clientID;
|
|
840
|
+
this._awareness.on("update", this._handleAwarenessUpdate.bind(this));
|
|
841
|
+
this._space.listen(this._channel, this._handleSpaceMessage.bind(this));
|
|
842
|
+
if (typeof window !== "undefined") {
|
|
843
|
+
window.addEventListener("beforeunload", this._handleBeforeUnload.bind(this));
|
|
844
|
+
} else if (typeof process !== "undefined") {
|
|
845
|
+
process.on("exit", this._handleBeforeUnload.bind(this));
|
|
846
|
+
}
|
|
847
|
+
const encoderAwarenessQuery = encoding.createEncoder();
|
|
848
|
+
encoding.writeVarUint(encoderAwarenessQuery, messageQueryAwareness);
|
|
849
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessQuery));
|
|
850
|
+
const encoderAwarenessState = encoding.createEncoder();
|
|
851
|
+
encoding.writeVarUint(encoderAwarenessState, messageAwareness);
|
|
852
|
+
encoding.writeVarUint8Array(encoderAwarenessState, awarenessProtocol.encodeAwarenessUpdate(this.awareness, [
|
|
853
|
+
this._clientId
|
|
854
|
+
]));
|
|
855
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwarenessState));
|
|
856
|
+
}
|
|
857
|
+
get awareness() {
|
|
858
|
+
return this._awareness;
|
|
859
|
+
}
|
|
860
|
+
_handleAwarenessUpdate({ added, updated, removed }, origin) {
|
|
861
|
+
log2("awareness update", {
|
|
862
|
+
added,
|
|
863
|
+
updated,
|
|
864
|
+
removed,
|
|
865
|
+
origin
|
|
866
|
+
}, {
|
|
867
|
+
F: __dxlog_file3,
|
|
868
|
+
L: 67,
|
|
869
|
+
S: this,
|
|
870
|
+
C: (f, a) => f(...a)
|
|
871
|
+
});
|
|
872
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
873
|
+
const encoderAwareness = encoding.createEncoder();
|
|
874
|
+
encoding.writeVarUint(encoderAwareness, messageAwareness);
|
|
875
|
+
encoding.writeVarUint8Array(encoderAwareness, awarenessProtocol.encodeAwarenessUpdate(this._awareness, changedClients));
|
|
876
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoderAwareness));
|
|
877
|
+
}
|
|
878
|
+
_handleSpaceMessage({ payload }) {
|
|
879
|
+
log2("space message", payload, {
|
|
880
|
+
F: __dxlog_file3,
|
|
881
|
+
L: 79,
|
|
882
|
+
S: this,
|
|
883
|
+
C: (f, a) => f(...a)
|
|
884
|
+
});
|
|
885
|
+
const data = new Uint8Array(Array.from(Object.values(payload)));
|
|
886
|
+
const encoder = this._readMessage(data);
|
|
887
|
+
if (encoder) {
|
|
888
|
+
void this._space.postMessage(this._channel, encoding.toUint8Array(encoder));
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
_readMessage(message) {
|
|
892
|
+
const decoder = decoding.createDecoder(message);
|
|
893
|
+
const encoder = encoding.createEncoder();
|
|
894
|
+
const messageType = decoding.readVarUint(decoder);
|
|
895
|
+
let sendReply = false;
|
|
896
|
+
switch (messageType) {
|
|
897
|
+
case messageAwareness: {
|
|
898
|
+
awarenessProtocol.applyAwarenessUpdate(this._awareness, decoding.readVarUint8Array(decoder), this);
|
|
899
|
+
break;
|
|
900
|
+
}
|
|
901
|
+
case messageQueryAwareness: {
|
|
902
|
+
encoding.writeVarUint(encoder, messageAwareness);
|
|
903
|
+
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
904
|
+
sendReply = true;
|
|
905
|
+
break;
|
|
906
|
+
}
|
|
907
|
+
default: {
|
|
908
|
+
console.error("Invalid message:", messageType);
|
|
909
|
+
return encoder;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
if (!sendReply) {
|
|
913
|
+
return null;
|
|
914
|
+
}
|
|
915
|
+
return encoder;
|
|
916
|
+
}
|
|
917
|
+
_handleBeforeUnload() {
|
|
918
|
+
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
919
|
+
this._clientId
|
|
920
|
+
], "window unload");
|
|
1676
921
|
}
|
|
1677
922
|
};
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
923
|
+
|
|
924
|
+
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
925
|
+
var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
926
|
+
var modelState = StateField2.define({
|
|
927
|
+
create: () => void 0,
|
|
928
|
+
update: (model) => model
|
|
929
|
+
});
|
|
930
|
+
var useTextModel = (props) => {
|
|
931
|
+
const { identity, space, text } = props;
|
|
932
|
+
const [model, setModel] = useState(() => createModel(props));
|
|
933
|
+
useEffect(() => setModel(createModel(props)), [
|
|
934
|
+
identity,
|
|
935
|
+
space,
|
|
936
|
+
text
|
|
937
|
+
]);
|
|
938
|
+
return model;
|
|
939
|
+
};
|
|
940
|
+
var createModel = (props) => {
|
|
941
|
+
const { text } = props;
|
|
942
|
+
if (isActualAutomergeObject(text)) {
|
|
943
|
+
return createAutomergeModel(props);
|
|
944
|
+
} else {
|
|
945
|
+
if (!text?.doc) {
|
|
946
|
+
return void 0;
|
|
947
|
+
}
|
|
948
|
+
return createYjsModel(props);
|
|
1682
949
|
}
|
|
1683
950
|
};
|
|
1684
|
-
var
|
|
1685
|
-
|
|
1686
|
-
|
|
951
|
+
var createYjsModel = ({ identity, space, text }) => {
|
|
952
|
+
invariant2(text?.doc && text?.content, void 0, {
|
|
953
|
+
F: __dxlog_file4,
|
|
954
|
+
L: 97,
|
|
955
|
+
S: void 0,
|
|
956
|
+
A: [
|
|
957
|
+
"text?.doc && text?.content",
|
|
958
|
+
""
|
|
959
|
+
]
|
|
960
|
+
});
|
|
961
|
+
const provider = space ? new SpaceAwarenessProvider({
|
|
962
|
+
space,
|
|
963
|
+
doc: text.doc,
|
|
964
|
+
channel: `yjs.awareness.${text.id}`
|
|
965
|
+
}) : void 0;
|
|
966
|
+
const model = {
|
|
967
|
+
id: text.doc.guid,
|
|
968
|
+
content: text.content,
|
|
969
|
+
text: () => text.content.toString(),
|
|
970
|
+
// https://github.com/yjs/yjs?tab=readme-ov-file#relative-positions
|
|
971
|
+
getCursorFromRange: (range) => {
|
|
972
|
+
const from = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content, range.from));
|
|
973
|
+
const to = Y.encodeRelativePosition(Y.createRelativePositionFromTypeIndex(text.content, range.to, -1));
|
|
974
|
+
return [
|
|
975
|
+
arrayToString(from),
|
|
976
|
+
arrayToString(to)
|
|
977
|
+
].join(":");
|
|
978
|
+
},
|
|
979
|
+
getRangeFromCursor: (cursor) => {
|
|
980
|
+
invariant2(text.doc, void 0, {
|
|
981
|
+
F: __dxlog_file4,
|
|
982
|
+
L: 113,
|
|
983
|
+
S: void 0,
|
|
984
|
+
A: [
|
|
985
|
+
"text.doc",
|
|
986
|
+
""
|
|
987
|
+
]
|
|
988
|
+
});
|
|
989
|
+
const parts = cursor.split(":");
|
|
990
|
+
const from = Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(parts[0])), text.doc);
|
|
991
|
+
const to = Y.createAbsolutePositionFromRelativePosition(Y.decodeRelativePosition(stringToArray(parts[1])), text.doc);
|
|
992
|
+
return from && to ? {
|
|
993
|
+
from: from.index,
|
|
994
|
+
to: to.index
|
|
995
|
+
} : void 0;
|
|
996
|
+
},
|
|
997
|
+
extension: [
|
|
998
|
+
yCollab(text.content, provider?.awareness),
|
|
999
|
+
modelState.init(() => model)
|
|
1000
|
+
],
|
|
1001
|
+
awareness: provider?.awareness,
|
|
1002
|
+
peer: identity ? {
|
|
1003
|
+
id: identity.identityKey.toHex(),
|
|
1004
|
+
name: identity.profile?.displayName
|
|
1005
|
+
} : void 0
|
|
1006
|
+
};
|
|
1007
|
+
return model;
|
|
1008
|
+
};
|
|
1009
|
+
var createAutomergeModel = ({ identity, text }) => {
|
|
1010
|
+
const obj = text;
|
|
1011
|
+
const doc = getRawDoc(obj, [
|
|
1012
|
+
obj.field
|
|
1013
|
+
]);
|
|
1014
|
+
const model = {
|
|
1015
|
+
id: obj.id,
|
|
1016
|
+
content: doc,
|
|
1017
|
+
text: () => get2(doc.handle.docSync(), doc.path),
|
|
1018
|
+
// TODO(burdon): https://automerge.org/automerge/api-docs/js/functions/next.getCursor.html
|
|
1019
|
+
extension: [
|
|
1020
|
+
automergePlugin(doc.handle, doc.path),
|
|
1021
|
+
modelState.init(() => model)
|
|
1022
|
+
],
|
|
1023
|
+
peer: identity ? {
|
|
1024
|
+
id: identity.identityKey.toHex(),
|
|
1025
|
+
name: identity.profile?.displayName
|
|
1026
|
+
} : void 0
|
|
1027
|
+
};
|
|
1028
|
+
return model;
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
// packages/ui/react-ui-editor/src/extensions/comments.ts
|
|
1032
|
+
var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/comments.ts";
|
|
1033
|
+
var styles2 = EditorView4.baseTheme({
|
|
1034
|
+
"& .cm-bookmark": {
|
|
1035
|
+
cursor: "pointer",
|
|
1036
|
+
margin: "4px",
|
|
1037
|
+
padding: "4px",
|
|
1038
|
+
backgroundColor: "yellow"
|
|
1687
1039
|
},
|
|
1688
|
-
"& .cm-
|
|
1689
|
-
|
|
1040
|
+
"& .cm-bookmark-selected": {
|
|
1041
|
+
backgroundColor: "orange"
|
|
1042
|
+
},
|
|
1043
|
+
"& .cm-comment": {
|
|
1044
|
+
backgroundColor: "yellow"
|
|
1045
|
+
},
|
|
1046
|
+
"& .cm-comment-active": {
|
|
1047
|
+
backgroundColor: "orange"
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
var marks = {
|
|
1051
|
+
highlight: Decoration2.mark({
|
|
1052
|
+
class: "cm-comment"
|
|
1053
|
+
}),
|
|
1054
|
+
highlightActive: Decoration2.mark({
|
|
1055
|
+
class: "cm-comment-active"
|
|
1056
|
+
})
|
|
1057
|
+
};
|
|
1058
|
+
var setCommentRange = StateEffect2.define();
|
|
1059
|
+
var setSelection = StateEffect2.define();
|
|
1060
|
+
var setCommentState = StateEffect2.define();
|
|
1061
|
+
var commentsStateField = StateField3.define({
|
|
1062
|
+
create: () => ({
|
|
1063
|
+
ranges: []
|
|
1064
|
+
}),
|
|
1065
|
+
update: (value, tr) => {
|
|
1066
|
+
for (const effect of tr.effects) {
|
|
1067
|
+
if (effect.is(setSelection)) {
|
|
1068
|
+
return {
|
|
1069
|
+
...value,
|
|
1070
|
+
...effect.value
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
1073
|
+
if (effect.is(setCommentRange)) {
|
|
1074
|
+
const { model, comments: comments2 } = effect.value;
|
|
1075
|
+
const ranges = comments2.map((comment) => {
|
|
1076
|
+
const range = model.getRangeFromCursor(comment.cursor);
|
|
1077
|
+
return range && {
|
|
1078
|
+
...comment,
|
|
1079
|
+
...range
|
|
1080
|
+
};
|
|
1081
|
+
}).filter(nonNullable);
|
|
1082
|
+
return {
|
|
1083
|
+
...value,
|
|
1084
|
+
ranges
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
if (effect.is(setCommentState)) {
|
|
1088
|
+
return effect.value;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
return value;
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
var highlightDecorations = EditorView4.decorations.compute([
|
|
1095
|
+
commentsStateField
|
|
1096
|
+
], (state) => {
|
|
1097
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1098
|
+
const decorations = sortBy(ranges ?? [], (range) => range.from)?.flatMap((selection) => {
|
|
1099
|
+
if (selection.from === selection.to) {
|
|
1100
|
+
return void 0;
|
|
1101
|
+
}
|
|
1102
|
+
const range = {
|
|
1103
|
+
from: selection.from,
|
|
1104
|
+
to: selection.to
|
|
1105
|
+
};
|
|
1106
|
+
if (selection.id === active) {
|
|
1107
|
+
return marks.highlightActive.range(range.from, range.to);
|
|
1108
|
+
} else {
|
|
1109
|
+
return marks.highlight.range(range.from, range.to);
|
|
1110
|
+
}
|
|
1111
|
+
}).filter(nonNullable) ?? [];
|
|
1112
|
+
return Decoration2.set(decorations);
|
|
1113
|
+
});
|
|
1114
|
+
var BookmarkWidget = class extends WidgetType {
|
|
1115
|
+
constructor(_anchor, _id, _handleClick) {
|
|
1116
|
+
super();
|
|
1117
|
+
this._anchor = _anchor;
|
|
1118
|
+
this._id = _id;
|
|
1119
|
+
this._handleClick = _handleClick;
|
|
1120
|
+
invariant3(this._id, void 0, {
|
|
1121
|
+
F: __dxlog_file5,
|
|
1122
|
+
L: 153,
|
|
1123
|
+
S: this,
|
|
1124
|
+
A: [
|
|
1125
|
+
"this._id",
|
|
1126
|
+
""
|
|
1127
|
+
]
|
|
1128
|
+
});
|
|
1690
1129
|
}
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
import { generateName } from "@dxos/display-name";
|
|
1701
|
-
import { useThemeContext } from "@dxos/react-ui";
|
|
1702
|
-
import { getColorForValue, inputSurface, mx as mx4 } from "@dxos/react-ui-theme";
|
|
1703
|
-
var EditorModes = [
|
|
1704
|
-
"default",
|
|
1705
|
-
"vim"
|
|
1706
|
-
];
|
|
1707
|
-
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
|
|
1708
|
-
const { themeMode } = useThemeContext();
|
|
1709
|
-
const tabsterDOMAttribute = useFocusableGroup({
|
|
1710
|
-
tabBehavior: "limited"
|
|
1711
|
-
});
|
|
1712
|
-
const [root, setRoot] = useState(null);
|
|
1713
|
-
const [state, setState] = useState();
|
|
1714
|
-
const [view, setView] = useState();
|
|
1715
|
-
useImperativeHandle(forwardedRef, () => ({
|
|
1716
|
-
root,
|
|
1717
|
-
state,
|
|
1718
|
-
view
|
|
1719
|
-
}), [
|
|
1720
|
-
view,
|
|
1721
|
-
state,
|
|
1722
|
-
root
|
|
1723
|
-
]);
|
|
1724
|
-
const { awareness, peer } = model;
|
|
1725
|
-
useEffect2(() => {
|
|
1726
|
-
if (awareness && peer) {
|
|
1727
|
-
awareness.setLocalStateField("user", {
|
|
1728
|
-
name: peer.name ?? generateName(peer.id),
|
|
1729
|
-
color: getColorForValue({
|
|
1730
|
-
value: peer.id,
|
|
1731
|
-
type: "color"
|
|
1732
|
-
}),
|
|
1733
|
-
colorLight: getColorForValue({
|
|
1734
|
-
value: peer.id,
|
|
1735
|
-
themeMode,
|
|
1736
|
-
type: "highlight"
|
|
1737
|
-
})
|
|
1738
|
-
});
|
|
1130
|
+
eq(other) {
|
|
1131
|
+
return this._anchor === other._anchor && this._id === other._id;
|
|
1132
|
+
}
|
|
1133
|
+
toDOM() {
|
|
1134
|
+
const span = document.createElement("span");
|
|
1135
|
+
span.className = "cm-bookmark";
|
|
1136
|
+
span.textContent = "\u203B";
|
|
1137
|
+
if (this._handleClick) {
|
|
1138
|
+
span.onclick = () => this._handleClick();
|
|
1739
1139
|
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1140
|
+
return span;
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
var comments = (options = {}) => {
|
|
1144
|
+
const handleSelect = debounce((state) => options.onSelect?.(state), 200);
|
|
1145
|
+
const createCommentThread = (view) => {
|
|
1146
|
+
invariant3(options.onCreate, void 0, {
|
|
1147
|
+
F: __dxlog_file5,
|
|
1148
|
+
L: 207,
|
|
1149
|
+
S: void 0,
|
|
1150
|
+
A: [
|
|
1151
|
+
"options.onCreate",
|
|
1152
|
+
""
|
|
1153
|
+
]
|
|
1154
|
+
});
|
|
1155
|
+
const { head, from, to } = view.state.selection.main;
|
|
1156
|
+
if (from === to) {
|
|
1157
|
+
return false;
|
|
1748
1158
|
}
|
|
1749
|
-
const
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
// TODO(burdon): Factor out VIM mode?
|
|
1754
|
-
editorMode === "vim" && vim(),
|
|
1755
|
-
// Theme.
|
|
1756
|
-
// TODO(burdon): Make optional.
|
|
1757
|
-
EditorView11.baseTheme(defaultTheme),
|
|
1758
|
-
EditorView11.theme(slots?.editor?.theme ?? {}),
|
|
1759
|
-
// TODO(burdon): themeMode doesn't change in storybooks.
|
|
1760
|
-
EditorView11.darkTheme.of(themeMode === "dark"),
|
|
1761
|
-
// Storage and replication.
|
|
1762
|
-
model.extension,
|
|
1763
|
-
// Custom.
|
|
1764
|
-
...extensions
|
|
1765
|
-
].filter(Boolean)
|
|
1159
|
+
const model = view.state.field(modelState);
|
|
1160
|
+
const relPos = model?.getCursorFromRange?.({
|
|
1161
|
+
from,
|
|
1162
|
+
to
|
|
1766
1163
|
});
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
}
|
|
1792
|
-
case "Escape": {
|
|
1793
|
-
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && root?.focus();
|
|
1794
|
-
break;
|
|
1164
|
+
if (relPos) {
|
|
1165
|
+
const id = callbackWrapper(options.onCreate)(relPos);
|
|
1166
|
+
if (id) {
|
|
1167
|
+
view.dispatch({
|
|
1168
|
+
effects: setSelection.of({
|
|
1169
|
+
active: id
|
|
1170
|
+
}),
|
|
1171
|
+
selection: {
|
|
1172
|
+
anchor: from
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
if (options.footnote) {
|
|
1176
|
+
const tag = `[^${id}]`;
|
|
1177
|
+
view.dispatch({
|
|
1178
|
+
changes: {
|
|
1179
|
+
from: head,
|
|
1180
|
+
insert: tag
|
|
1181
|
+
},
|
|
1182
|
+
selection: {
|
|
1183
|
+
anchor: head + tag.length
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
}
|
|
1187
|
+
return true;
|
|
1795
1188
|
}
|
|
1796
1189
|
}
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1190
|
+
return false;
|
|
1191
|
+
};
|
|
1192
|
+
const bookmarksViewPlugin = ViewPlugin3.fromClass(class BookmarkViewPlugin {
|
|
1193
|
+
static {
|
|
1194
|
+
// Match markdown footnotes (option).
|
|
1195
|
+
// https://www.markdownguide.org/extended-syntax/#footnotes
|
|
1196
|
+
this.bookmarkMatcher = new MatchDecorator({
|
|
1197
|
+
regexp: /\[\^(\w+)\]/g,
|
|
1198
|
+
decoration: (match, view, pos) => {
|
|
1199
|
+
const id = match[1];
|
|
1200
|
+
return Decoration2.replace({
|
|
1201
|
+
id,
|
|
1202
|
+
widget: new BookmarkWidget(pos, id)
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
constructor(view) {
|
|
1208
|
+
this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.createDeco(view);
|
|
1209
|
+
}
|
|
1210
|
+
update(update4) {
|
|
1211
|
+
this.bookmarks = BookmarkViewPlugin.bookmarkMatcher.updateDeco(update4, this.bookmarks);
|
|
1212
|
+
}
|
|
1213
|
+
}, {
|
|
1214
|
+
decorations: (instance) => instance.bookmarks,
|
|
1215
|
+
provide: (plugin) => EditorView4.atomicRanges.of((view) => {
|
|
1216
|
+
return view.plugin(plugin)?.bookmarks || Decoration2.none;
|
|
1217
|
+
})
|
|
1808
1218
|
});
|
|
1219
|
+
return [
|
|
1220
|
+
bookmarksViewPlugin,
|
|
1221
|
+
commentsStateField,
|
|
1222
|
+
highlightDecorations,
|
|
1223
|
+
styles2,
|
|
1224
|
+
//
|
|
1225
|
+
// Keymap.
|
|
1226
|
+
//
|
|
1227
|
+
options.onCreate ? keymap3.of([
|
|
1228
|
+
{
|
|
1229
|
+
key: options?.key ?? "meta-'",
|
|
1230
|
+
run: callbackWrapper(createCommentThread)
|
|
1231
|
+
}
|
|
1232
|
+
]) : [],
|
|
1233
|
+
//
|
|
1234
|
+
// Hover tooltip (for key shortcut hints, etc.)
|
|
1235
|
+
// TODO(burdon): Factor out to generic hints extension for current selection/line.
|
|
1236
|
+
//
|
|
1237
|
+
options.onHover ? hoverTooltip((view, pos) => {
|
|
1238
|
+
const selection = view.state.selection.main;
|
|
1239
|
+
if (selection && pos >= selection.from && pos <= selection.to) {
|
|
1240
|
+
return {
|
|
1241
|
+
pos: selection.from,
|
|
1242
|
+
end: selection.to,
|
|
1243
|
+
above: true,
|
|
1244
|
+
create: () => {
|
|
1245
|
+
const el = document.createElement("div");
|
|
1246
|
+
options.onHover?.(el);
|
|
1247
|
+
return {
|
|
1248
|
+
dom: el,
|
|
1249
|
+
offset: {
|
|
1250
|
+
x: 0,
|
|
1251
|
+
y: 8
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
return null;
|
|
1258
|
+
}, {
|
|
1259
|
+
hideOnChange: true,
|
|
1260
|
+
hoverTime: 1e3
|
|
1261
|
+
}) : [],
|
|
1262
|
+
//
|
|
1263
|
+
// Monitor cursor movement and text updates.
|
|
1264
|
+
// TODO(burdon): Is there a better (finer grained) way to do this?
|
|
1265
|
+
//
|
|
1266
|
+
EditorView4.updateListener.of(({ view, state, startState, changes }) => {
|
|
1267
|
+
{
|
|
1268
|
+
let mod = false;
|
|
1269
|
+
const model = state.field(modelState);
|
|
1270
|
+
const { active, ranges } = state.field(commentsStateField);
|
|
1271
|
+
changes.iterChanges((from, to, from2, to2) => {
|
|
1272
|
+
invariant3(model, void 0, {
|
|
1273
|
+
F: __dxlog_file5,
|
|
1274
|
+
L: 336,
|
|
1275
|
+
S: void 0,
|
|
1276
|
+
A: [
|
|
1277
|
+
"model",
|
|
1278
|
+
""
|
|
1279
|
+
]
|
|
1280
|
+
});
|
|
1281
|
+
ranges.forEach((range) => {
|
|
1282
|
+
if (from2 === to2) {
|
|
1283
|
+
const newRange = model.getRangeFromCursor(range.cursor);
|
|
1284
|
+
if (!newRange || newRange.to - newRange.from === 0) {
|
|
1285
|
+
log3.info("deleted comment", {
|
|
1286
|
+
thread: range.id
|
|
1287
|
+
}, {
|
|
1288
|
+
F: __dxlog_file5,
|
|
1289
|
+
L: 342,
|
|
1290
|
+
S: void 0,
|
|
1291
|
+
C: (f, a) => f(...a)
|
|
1292
|
+
});
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
if (from <= range.to) {
|
|
1296
|
+
const newRange = model.getRangeFromCursor(range.cursor);
|
|
1297
|
+
Object.assign(range, newRange);
|
|
1298
|
+
mod = true;
|
|
1299
|
+
}
|
|
1300
|
+
});
|
|
1301
|
+
});
|
|
1302
|
+
if (mod) {
|
|
1303
|
+
view.dispatch({
|
|
1304
|
+
effects: setCommentState.of({
|
|
1305
|
+
active,
|
|
1306
|
+
ranges
|
|
1307
|
+
})
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
{
|
|
1312
|
+
const { head } = state.selection.main;
|
|
1313
|
+
let min = Infinity;
|
|
1314
|
+
const { active, closest, ranges } = state.field(commentsStateField);
|
|
1315
|
+
const selected = {
|
|
1316
|
+
active: void 0,
|
|
1317
|
+
closest: void 0
|
|
1318
|
+
};
|
|
1319
|
+
ranges.forEach((comment) => {
|
|
1320
|
+
const d = Math.min(Math.abs(head - comment.from), Math.abs(head - comment.to));
|
|
1321
|
+
if (head >= comment.from && head <= comment.to) {
|
|
1322
|
+
selected.active = comment.id;
|
|
1323
|
+
}
|
|
1324
|
+
if (d < min) {
|
|
1325
|
+
selected.closest = comment.id;
|
|
1326
|
+
min = d;
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
if (selected.active !== active || selected.closest !== closest) {
|
|
1330
|
+
view.dispatch({
|
|
1331
|
+
effects: setSelection.of(selected)
|
|
1332
|
+
});
|
|
1333
|
+
handleSelect({
|
|
1334
|
+
...selected,
|
|
1335
|
+
ranges: ranges.map((range) => ({
|
|
1336
|
+
...range,
|
|
1337
|
+
location: view.coordsAtPos(range.from)
|
|
1338
|
+
}))
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
})
|
|
1343
|
+
];
|
|
1344
|
+
};
|
|
1345
|
+
|
|
1346
|
+
// packages/ui/react-ui-editor/src/extensions/hr.ts
|
|
1347
|
+
import { Decoration as Decoration3, EditorView as EditorView5, MatchDecorator as MatchDecorator2, ViewPlugin as ViewPlugin4, WidgetType as WidgetType2 } from "@codemirror/view";
|
|
1348
|
+
import get3 from "lodash.get";
|
|
1349
|
+
var styles3 = EditorView5.baseTheme({
|
|
1350
|
+
"& .cm-hr": {
|
|
1351
|
+
// TODO(burdon): ???
|
|
1352
|
+
// Note that block-level decorations should not have vertical margins,
|
|
1353
|
+
borderBottom: `1px solid ${get3(tokens, "extend.colors.neutral.200")}`
|
|
1354
|
+
}
|
|
1809
1355
|
});
|
|
1810
|
-
var
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1356
|
+
var HorizontalRuleWidget = class extends WidgetType2 {
|
|
1357
|
+
constructor(_pos) {
|
|
1358
|
+
super();
|
|
1359
|
+
this._pos = _pos;
|
|
1360
|
+
}
|
|
1361
|
+
eq(other) {
|
|
1362
|
+
return this._pos === other._pos;
|
|
1363
|
+
}
|
|
1364
|
+
toDOM(view) {
|
|
1365
|
+
const el = document.createElement("div");
|
|
1366
|
+
el.className = "cm-hr";
|
|
1367
|
+
return el;
|
|
1368
|
+
}
|
|
1369
|
+
};
|
|
1370
|
+
var placeholderMatcher = new MatchDecorator2({
|
|
1371
|
+
regexp: /^---$/g,
|
|
1372
|
+
decoration: (match, view, pos) => Decoration3.replace({
|
|
1373
|
+
widget: new HorizontalRuleWidget(pos)
|
|
1374
|
+
})
|
|
1827
1375
|
});
|
|
1828
|
-
var
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1376
|
+
var hr = () => [
|
|
1377
|
+
styles3,
|
|
1378
|
+
ViewPlugin4.fromClass(class {
|
|
1379
|
+
constructor(view) {
|
|
1380
|
+
this.rules = placeholderMatcher.createDeco(view);
|
|
1381
|
+
}
|
|
1382
|
+
update(update4) {
|
|
1383
|
+
this.rules = placeholderMatcher.updateDeco(update4, this.rules);
|
|
1384
|
+
}
|
|
1385
|
+
}, {
|
|
1386
|
+
decorations: (instance) => instance.rules,
|
|
1387
|
+
// TODO(burdon): Is this really atomic (cursor can move into ---).
|
|
1388
|
+
provide: (plugin) => EditorView5.atomicRanges.of((view) => {
|
|
1389
|
+
return view.plugin(plugin)?.rules || Decoration3.none;
|
|
1390
|
+
})
|
|
1391
|
+
})
|
|
1392
|
+
];
|
|
1393
|
+
|
|
1394
|
+
// packages/ui/react-ui-editor/src/extensions/image.ts
|
|
1395
|
+
import { syntaxTree } from "@codemirror/language";
|
|
1396
|
+
import { RangeSetBuilder, StateField as StateField4 } from "@codemirror/state";
|
|
1397
|
+
import { Decoration as Decoration4, EditorView as EditorView6, WidgetType as WidgetType3 } from "@codemirror/view";
|
|
1398
|
+
var image = (options = {}) => {
|
|
1399
|
+
return StateField4.define({
|
|
1400
|
+
create: (state) => update(state, options),
|
|
1401
|
+
update: (_, tr) => update(tr.state, options),
|
|
1402
|
+
provide: (field) => EditorView6.decorations.from(field)
|
|
1844
1403
|
});
|
|
1845
|
-
}
|
|
1846
|
-
var
|
|
1847
|
-
|
|
1848
|
-
|
|
1404
|
+
};
|
|
1405
|
+
var update = (state, options) => {
|
|
1406
|
+
const builder = new RangeSetBuilder();
|
|
1407
|
+
const cursor = state.selection.main.head;
|
|
1408
|
+
syntaxTree(state).iterate({
|
|
1409
|
+
enter: (node) => {
|
|
1410
|
+
if (node.name === "Image") {
|
|
1411
|
+
const urlNode = node.node.getChild("URL");
|
|
1412
|
+
if (urlNode) {
|
|
1413
|
+
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
1414
|
+
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
1415
|
+
builder.add(hide ? node.from : node.to, node.to, Decoration4.replace({
|
|
1416
|
+
block: true,
|
|
1417
|
+
widget: new ImageWidget(url)
|
|
1418
|
+
}));
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
return builder.finish();
|
|
1424
|
+
};
|
|
1425
|
+
var ImageWidget = class extends WidgetType3 {
|
|
1426
|
+
constructor(_url) {
|
|
1427
|
+
super();
|
|
1428
|
+
this._url = _url;
|
|
1429
|
+
}
|
|
1430
|
+
// TODO(burdon): Does this need to be unique for each position?
|
|
1431
|
+
eq(other) {
|
|
1432
|
+
return this._url === other._url;
|
|
1433
|
+
}
|
|
1434
|
+
toDOM(view) {
|
|
1435
|
+
const img = document.createElement("img");
|
|
1436
|
+
img.setAttribute("src", this._url);
|
|
1437
|
+
img.setAttribute("class", "cm-image");
|
|
1438
|
+
return img;
|
|
1849
1439
|
}
|
|
1850
1440
|
};
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1441
|
+
|
|
1442
|
+
// packages/ui/react-ui-editor/src/extensions/link.ts
|
|
1443
|
+
import { syntaxTree as syntaxTree2 } from "@codemirror/language";
|
|
1444
|
+
import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField5 } from "@codemirror/state";
|
|
1445
|
+
import { Decoration as Decoration5, EditorView as EditorView7, WidgetType as WidgetType4, hoverTooltip as hoverTooltip2 } from "@codemirror/view";
|
|
1446
|
+
import { tooltipContent } from "@dxos/react-ui-theme";
|
|
1447
|
+
var markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
1448
|
+
var link = (options = {}) => {
|
|
1449
|
+
const extensions = [
|
|
1450
|
+
StateField5.define({
|
|
1451
|
+
create: (state) => update2(state, options),
|
|
1452
|
+
update: (_, tr) => update2(tr.state, options),
|
|
1453
|
+
provide: (field) => EditorView7.decorations.from(field)
|
|
1454
|
+
})
|
|
1455
|
+
];
|
|
1456
|
+
if (options.onHover) {
|
|
1457
|
+
extensions.push(
|
|
1458
|
+
// https://codemirror.net/examples/tooltip
|
|
1459
|
+
// https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
1460
|
+
// https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
1461
|
+
hoverTooltip2((view, pos) => {
|
|
1462
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
1463
|
+
const p = pos - from;
|
|
1464
|
+
let idx = 0;
|
|
1465
|
+
let match;
|
|
1466
|
+
do {
|
|
1467
|
+
match = text.substring(idx).match(markdownLinkRegexp);
|
|
1468
|
+
if (!match) {
|
|
1469
|
+
match = void 0;
|
|
1470
|
+
break;
|
|
1471
|
+
}
|
|
1472
|
+
idx = text.indexOf(match[0]);
|
|
1473
|
+
if (p >= idx && p < idx + match[0].length) {
|
|
1474
|
+
break;
|
|
1475
|
+
}
|
|
1476
|
+
idx += match[0].length;
|
|
1477
|
+
} while (true);
|
|
1478
|
+
if (!match) {
|
|
1479
|
+
return null;
|
|
1480
|
+
}
|
|
1481
|
+
const [, , url] = match ?? [];
|
|
1482
|
+
return {
|
|
1483
|
+
pos: idx + from,
|
|
1484
|
+
end: idx + from + match[0].length,
|
|
1485
|
+
above: true,
|
|
1486
|
+
create: () => {
|
|
1487
|
+
const el = document.createElement("div");
|
|
1488
|
+
el.className = tooltipContent({}, "pli-2 plb-1");
|
|
1489
|
+
options.onHover(el, url);
|
|
1490
|
+
return {
|
|
1491
|
+
dom: el,
|
|
1492
|
+
offset: {
|
|
1493
|
+
x: 0,
|
|
1494
|
+
y: 4
|
|
1495
|
+
}
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
};
|
|
1499
|
+
})
|
|
1500
|
+
);
|
|
1855
1501
|
}
|
|
1502
|
+
return extensions;
|
|
1856
1503
|
};
|
|
1857
|
-
var
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1504
|
+
var LinkButton = class extends WidgetType4 {
|
|
1505
|
+
constructor(_pos, _url, _onAttach) {
|
|
1506
|
+
super();
|
|
1507
|
+
this._pos = _pos;
|
|
1508
|
+
this._url = _url;
|
|
1509
|
+
this._onAttach = _onAttach;
|
|
1510
|
+
}
|
|
1511
|
+
eq(other) {
|
|
1512
|
+
return this._pos === other._pos && this._url === other._url;
|
|
1513
|
+
}
|
|
1514
|
+
toDOM(view) {
|
|
1515
|
+
const el = document.createElement("span");
|
|
1516
|
+
this._onAttach(el, this._url);
|
|
1517
|
+
return el;
|
|
1861
1518
|
}
|
|
1862
1519
|
};
|
|
1520
|
+
var LinkText = class extends WidgetType4 {
|
|
1521
|
+
constructor(_text, _url) {
|
|
1522
|
+
super();
|
|
1523
|
+
this._text = _text;
|
|
1524
|
+
this._url = _url;
|
|
1525
|
+
}
|
|
1526
|
+
eq(other) {
|
|
1527
|
+
return this._url === other._url;
|
|
1528
|
+
}
|
|
1529
|
+
toDOM(view) {
|
|
1530
|
+
const link2 = document.createElement("a");
|
|
1531
|
+
link2.setAttribute("class", "cm-link");
|
|
1532
|
+
link2.textContent = this._text;
|
|
1533
|
+
if (this._url) {
|
|
1534
|
+
link2.setAttribute("rel", "noreferrer");
|
|
1535
|
+
link2.setAttribute("target", "_blank");
|
|
1536
|
+
link2.href = this._url;
|
|
1537
|
+
} else {
|
|
1538
|
+
link2.onclick = () => {
|
|
1539
|
+
view.dispatch({
|
|
1540
|
+
selection: {
|
|
1541
|
+
anchor: view.posAtDOM(link2)
|
|
1542
|
+
}
|
|
1543
|
+
});
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
return link2;
|
|
1547
|
+
}
|
|
1548
|
+
};
|
|
1549
|
+
var update2 = (state, options) => {
|
|
1550
|
+
const builder = new RangeSetBuilder2();
|
|
1551
|
+
const cursor = state.selection.main.head;
|
|
1552
|
+
syntaxTree2(state).iterate({
|
|
1553
|
+
enter: (node) => {
|
|
1554
|
+
if (node.name === "Link") {
|
|
1555
|
+
const marks2 = node.node.getChildren("LinkMark");
|
|
1556
|
+
const text = marks2.length >= 2 ? state.sliceDoc(marks2[0].to, marks2[1].from) : "";
|
|
1557
|
+
const urlNode = node.node.getChild("URL");
|
|
1558
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : "";
|
|
1559
|
+
if (!url) {
|
|
1560
|
+
return false;
|
|
1561
|
+
}
|
|
1562
|
+
if (state.readOnly || cursor < node.from || cursor > node.to) {
|
|
1563
|
+
builder.add(node.from, node.to, Decoration5.replace({
|
|
1564
|
+
widget: new LinkText(text, options.link ? url : void 0)
|
|
1565
|
+
}));
|
|
1566
|
+
}
|
|
1567
|
+
if (options.onRender) {
|
|
1568
|
+
builder.add(node.to, node.to, Decoration5.replace({
|
|
1569
|
+
widget: new LinkButton(node.from, url, options.onRender)
|
|
1570
|
+
}));
|
|
1571
|
+
}
|
|
1572
|
+
return false;
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
return builder.finish();
|
|
1577
|
+
};
|
|
1863
1578
|
|
|
1864
|
-
// packages/ui/react-ui-editor/src/
|
|
1865
|
-
import
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1579
|
+
// packages/ui/react-ui-editor/src/extensions/listener.ts
|
|
1580
|
+
import { EditorView as EditorView8 } from "@codemirror/view";
|
|
1581
|
+
var listener = ({ onFocus, onChange }) => {
|
|
1582
|
+
const extensions = [];
|
|
1583
|
+
onFocus && extensions.push(EditorView8.focusChangeEffect.of((_, focused) => {
|
|
1584
|
+
onFocus(focused);
|
|
1585
|
+
return null;
|
|
1586
|
+
}));
|
|
1587
|
+
onChange && extensions.push(EditorView8.updateListener.of((update4) => {
|
|
1588
|
+
onChange(update4.state.doc.toString());
|
|
1589
|
+
}));
|
|
1590
|
+
return extensions;
|
|
1591
|
+
};
|
|
1875
1592
|
|
|
1876
|
-
// packages/ui/react-ui-editor/src/
|
|
1877
|
-
import {
|
|
1593
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1594
|
+
import { closeBrackets as closeBrackets2, closeBracketsKeymap } from "@codemirror/autocomplete";
|
|
1595
|
+
import { history, historyKeymap, indentWithTab, standardKeymap } from "@codemirror/commands";
|
|
1596
|
+
import { markdownLanguage as markdownLanguage2, markdown } from "@codemirror/lang-markdown";
|
|
1597
|
+
import { bracketMatching as bracketMatching2, defaultHighlightStyle as defaultHighlightStyle2, indentOnInput, syntaxHighlighting as syntaxHighlighting2 } from "@codemirror/language";
|
|
1598
|
+
import { languages } from "@codemirror/language-data";
|
|
1599
|
+
import { searchKeymap } from "@codemirror/search";
|
|
1600
|
+
import { EditorState } from "@codemirror/state";
|
|
1601
|
+
import { oneDarkHighlightStyle as oneDarkHighlightStyle2 } from "@codemirror/theme-one-dark";
|
|
1602
|
+
import { crosshairCursor, drawSelection as drawSelection2, dropCursor, EditorView as EditorView9, highlightActiveLine, keymap as keymap4, placeholder as placeholder2 } from "@codemirror/view";
|
|
1878
1603
|
|
|
1879
|
-
// packages/ui/react-ui-editor/src/
|
|
1880
|
-
import {
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1604
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/highlight.ts
|
|
1605
|
+
import { markdownLanguage } from "@codemirror/lang-markdown";
|
|
1606
|
+
import { HighlightStyle } from "@codemirror/language";
|
|
1607
|
+
import { tags, styleTags, Tag } from "@lezer/highlight";
|
|
1608
|
+
import { Table } from "@lezer/markdown";
|
|
1609
|
+
import get4 from "lodash.get";
|
|
1610
|
+
var markdownTags = {
|
|
1611
|
+
Blockquote: Tag.define(),
|
|
1612
|
+
CodeMark: Tag.define(),
|
|
1613
|
+
CodeText: Tag.define(),
|
|
1614
|
+
EmphasisMark: Tag.define(),
|
|
1615
|
+
HeaderMark: Tag.define(),
|
|
1616
|
+
// HorizontalRule: Tag.define(),
|
|
1617
|
+
InlineCode: Tag.define(),
|
|
1618
|
+
LinkLabel: Tag.define(),
|
|
1619
|
+
LinkReference: Tag.define(),
|
|
1620
|
+
ListMark: Tag.define(),
|
|
1621
|
+
QuoteMark: Tag.define(),
|
|
1622
|
+
URL: Tag.define(),
|
|
1623
|
+
// Custom.
|
|
1624
|
+
TableCell: Tag.define()
|
|
1625
|
+
};
|
|
1626
|
+
Table.defineNodes?.forEach((node) => {
|
|
1627
|
+
switch (node?.name) {
|
|
1628
|
+
case "TableCell": {
|
|
1629
|
+
node.style = markdownTags.TableCell;
|
|
1630
|
+
break;
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
var markdownTagsExtensions = [
|
|
1635
|
+
Table,
|
|
1636
|
+
{
|
|
1637
|
+
props: [
|
|
1638
|
+
styleTags(markdownTags)
|
|
1639
|
+
]
|
|
1640
|
+
}
|
|
1641
|
+
];
|
|
1642
|
+
var markdownHighlightStyle = (readonly) => {
|
|
1643
|
+
return HighlightStyle.define([
|
|
1644
|
+
{
|
|
1645
|
+
tag: [
|
|
1646
|
+
tags.keyword,
|
|
1647
|
+
tags.name,
|
|
1648
|
+
tags.deleted,
|
|
1649
|
+
tags.character,
|
|
1650
|
+
tags.propertyName,
|
|
1651
|
+
tags.macroName,
|
|
1652
|
+
tags.color,
|
|
1653
|
+
tags.constant(tags.name),
|
|
1654
|
+
tags.standard(tags.name),
|
|
1655
|
+
tags.definition(tags.name),
|
|
1656
|
+
tags.separator,
|
|
1657
|
+
tags.typeName,
|
|
1658
|
+
tags.className,
|
|
1659
|
+
tags.number,
|
|
1660
|
+
tags.changed,
|
|
1661
|
+
tags.annotation,
|
|
1662
|
+
tags.modifier,
|
|
1663
|
+
tags.self,
|
|
1664
|
+
tags.namespace,
|
|
1665
|
+
tags.operator,
|
|
1666
|
+
tags.operatorKeyword,
|
|
1667
|
+
tags.escape,
|
|
1668
|
+
tags.regexp,
|
|
1669
|
+
tags.special(tags.string),
|
|
1670
|
+
tags.meta,
|
|
1671
|
+
tags.comment,
|
|
1672
|
+
tags.atom,
|
|
1673
|
+
tags.bool,
|
|
1674
|
+
tags.special(tags.variableName),
|
|
1675
|
+
tags.processingInstruction,
|
|
1676
|
+
tags.string,
|
|
1677
|
+
tags.inserted,
|
|
1678
|
+
tags.invalid
|
|
1679
|
+
],
|
|
1680
|
+
color: "inherit !important"
|
|
1681
|
+
},
|
|
1682
|
+
// Markdown marks.
|
|
1683
|
+
{
|
|
1684
|
+
tag: [
|
|
1685
|
+
tags.meta,
|
|
1686
|
+
tags.processingInstruction,
|
|
1687
|
+
markdownTags.LinkLabel,
|
|
1688
|
+
markdownTags.LinkReference,
|
|
1689
|
+
markdownTags.ListMark
|
|
1690
|
+
],
|
|
1691
|
+
class: mark
|
|
1692
|
+
},
|
|
1693
|
+
// Markdown marks.
|
|
1694
|
+
{
|
|
1695
|
+
tag: [
|
|
1696
|
+
markdownTags.CodeMark,
|
|
1697
|
+
markdownTags.HeaderMark,
|
|
1698
|
+
markdownTags.QuoteMark,
|
|
1699
|
+
markdownTags.EmphasisMark
|
|
1700
|
+
],
|
|
1701
|
+
class: readonly ? "hidden" : mark
|
|
1702
|
+
},
|
|
1703
|
+
// E.g., code block language (after ```).
|
|
1704
|
+
{
|
|
1705
|
+
tag: [
|
|
1706
|
+
tags.function(tags.variableName),
|
|
1707
|
+
tags.labelName
|
|
1708
|
+
],
|
|
1709
|
+
class: readonly ? "hidden" : codeMark
|
|
1710
|
+
},
|
|
1711
|
+
{
|
|
1712
|
+
tag: [
|
|
1713
|
+
tags.monospace
|
|
1714
|
+
],
|
|
1715
|
+
class: "font-mono"
|
|
1716
|
+
},
|
|
1717
|
+
// Headings.
|
|
1718
|
+
{
|
|
1719
|
+
tag: tags.heading1,
|
|
1720
|
+
class: heading(1, readonly)
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
tag: tags.heading2,
|
|
1724
|
+
class: heading(2, readonly)
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
tag: tags.heading3,
|
|
1728
|
+
class: heading(3, readonly)
|
|
1729
|
+
},
|
|
1730
|
+
{
|
|
1731
|
+
tag: tags.heading4,
|
|
1732
|
+
class: heading(4, readonly)
|
|
1733
|
+
},
|
|
1734
|
+
{
|
|
1735
|
+
tag: tags.heading5,
|
|
1736
|
+
class: heading(5, readonly)
|
|
1737
|
+
},
|
|
1738
|
+
{
|
|
1739
|
+
tag: tags.heading6,
|
|
1740
|
+
class: heading(6, readonly)
|
|
1741
|
+
},
|
|
1742
|
+
// Emphasis.
|
|
1743
|
+
{
|
|
1744
|
+
tag: tags.emphasis,
|
|
1745
|
+
class: italic
|
|
1746
|
+
},
|
|
1747
|
+
{
|
|
1748
|
+
tag: tags.strong,
|
|
1749
|
+
class: bold
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
tag: tags.strikethrough,
|
|
1753
|
+
class: strikethrough
|
|
1754
|
+
},
|
|
1755
|
+
// Naked URLs.
|
|
1756
|
+
{
|
|
1757
|
+
tag: [
|
|
1758
|
+
markdownTags.URL
|
|
1759
|
+
],
|
|
1760
|
+
class: inlineUrl
|
|
1761
|
+
},
|
|
1762
|
+
// NOTE: The `markdown` extension configures extensions for `lezer` to parse markdown tokens (incl. below).
|
|
1763
|
+
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
1764
|
+
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
1765
|
+
// the code, but all other CSS properties will be inherited.
|
|
1766
|
+
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
1767
|
+
{
|
|
1768
|
+
tag: [
|
|
1769
|
+
markdownTags.CodeText,
|
|
1770
|
+
markdownTags.InlineCode
|
|
1771
|
+
],
|
|
1772
|
+
class: code
|
|
1773
|
+
},
|
|
1774
|
+
{
|
|
1775
|
+
tag: [
|
|
1776
|
+
markdownTags.QuoteMark
|
|
1777
|
+
],
|
|
1778
|
+
class: blockquote
|
|
1779
|
+
},
|
|
1780
|
+
// TODO(burdon): Replace with extension.
|
|
1781
|
+
// {
|
|
1782
|
+
// tag: [markdownTags.HorizontalRule],
|
|
1783
|
+
// class: mx(horizontalRule, readonly && '-indent-[100rem]'),
|
|
1784
|
+
// },
|
|
1785
|
+
// TODO(burdon): Only if being edited.
|
|
1786
|
+
{
|
|
1787
|
+
tag: [
|
|
1788
|
+
markdownTags.TableCell
|
|
1789
|
+
],
|
|
1790
|
+
class: "font-mono"
|
|
1791
|
+
}
|
|
1792
|
+
], {
|
|
1793
|
+
scope: markdownLanguage,
|
|
1794
|
+
all: {
|
|
1795
|
+
fontFamily: get4(tokens, "fontFamily.body", []).join(",")
|
|
1891
1796
|
}
|
|
1892
|
-
}
|
|
1893
|
-
view.dispatch({
|
|
1894
|
-
selection,
|
|
1895
|
-
annotations: reconcileAnnotationType.of({})
|
|
1896
1797
|
});
|
|
1897
1798
|
};
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
} else if (patch.action === "splice") {
|
|
1902
|
-
return handleSplice(target, patch);
|
|
1903
|
-
} else if (patch.action === "del") {
|
|
1904
|
-
return handleDel(target, patch);
|
|
1905
|
-
} else if (patch.action === "put") {
|
|
1906
|
-
return handlePut(target, patch, state);
|
|
1907
|
-
} else {
|
|
1908
|
-
return null;
|
|
1909
|
-
}
|
|
1910
|
-
};
|
|
1911
|
-
var handleInsert = (target, patch) => {
|
|
1912
|
-
const index = charPath(target, patch.path);
|
|
1913
|
-
if (index == null) {
|
|
1914
|
-
return [];
|
|
1915
|
-
}
|
|
1916
|
-
const text = patch.values.map((v) => v ? v.toString() : "").join("");
|
|
1799
|
+
|
|
1800
|
+
// packages/ui/react-ui-editor/src/extensions/markdown/bundle.ts
|
|
1801
|
+
var markdownBundle = ({ readonly, themeMode, placeholder: _placeholder }) => {
|
|
1917
1802
|
return [
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1803
|
+
EditorState.allowMultipleSelections.of(true),
|
|
1804
|
+
EditorState.tabSize.of(2),
|
|
1805
|
+
EditorView9.lineWrapping,
|
|
1806
|
+
customKeymap(),
|
|
1807
|
+
bracketMatching2(),
|
|
1808
|
+
closeBrackets2(),
|
|
1809
|
+
crosshairCursor(),
|
|
1810
|
+
dropCursor(),
|
|
1811
|
+
drawSelection2(),
|
|
1812
|
+
highlightActiveLine(),
|
|
1813
|
+
history(),
|
|
1814
|
+
indentOnInput(),
|
|
1815
|
+
_placeholder && placeholder2(_placeholder),
|
|
1816
|
+
// Main extension.
|
|
1817
|
+
// https://github.com/codemirror/lang-markdown
|
|
1818
|
+
// https://codemirror.net/5/mode/markdown/index.html (demo).
|
|
1819
|
+
markdown({
|
|
1820
|
+
// GRM by default (vs strict CommonMark):
|
|
1821
|
+
// Table, TaskList, Strikethrough, and Autolink.
|
|
1822
|
+
// NOTE: This extends the parser; it doesn't affect rendering.
|
|
1823
|
+
// https://github.github.com/gfm
|
|
1824
|
+
// https://github.com/lezer-parser/markdown?tab=readme-ov-file#github-flavored-markdown
|
|
1825
|
+
base: markdownLanguage2,
|
|
1826
|
+
// Languages for syntax highlighting fenced code blocks.
|
|
1827
|
+
codeLanguages: languages,
|
|
1828
|
+
// Parser extensions.
|
|
1829
|
+
extensions: [
|
|
1830
|
+
// GFM provided by default.
|
|
1831
|
+
markdownTagsExtensions
|
|
1832
|
+
]
|
|
1833
|
+
}),
|
|
1834
|
+
// https://github.com/codemirror/theme-one-dark
|
|
1835
|
+
themeMode === "dark" ? syntaxHighlighting2(oneDarkHighlightStyle2) : syntaxHighlighting2(defaultHighlightStyle2),
|
|
1836
|
+
// Custom styles.
|
|
1837
|
+
syntaxHighlighting2(markdownHighlightStyle(readonly))
|
|
1838
|
+
].filter(Boolean);
|
|
1924
1839
|
};
|
|
1925
|
-
var
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1840
|
+
var customKeymap = () => keymap4.of([
|
|
1841
|
+
// https://codemirror.net/docs/ref/#commands.indentWithTab
|
|
1842
|
+
indentWithTab,
|
|
1843
|
+
// https://codemirror.net/docs/ref/#commands.standardKeymap
|
|
1844
|
+
...standardKeymap,
|
|
1845
|
+
// https://codemirror.net/docs/ref/#commands.historyKeymap
|
|
1846
|
+
...historyKeymap,
|
|
1847
|
+
// https://codemirror.net/docs/ref/#autocomplete.closeBracketsKeymap
|
|
1848
|
+
...closeBracketsKeymap,
|
|
1849
|
+
// https://codemirror.net/docs/ref/#search.searchKeymap
|
|
1850
|
+
...searchKeymap
|
|
1851
|
+
]);
|
|
1852
|
+
|
|
1853
|
+
// packages/ui/react-ui-editor/src/extensions/mention.ts
|
|
1854
|
+
import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
|
|
1855
|
+
var mention = ({ onSearch }) => {
|
|
1856
|
+
return autocompletion2({
|
|
1857
|
+
closeOnBlur: false,
|
|
1858
|
+
override: [
|
|
1859
|
+
(context) => {
|
|
1860
|
+
const match = context.matchBefore(/@(\w+)?/);
|
|
1861
|
+
if (!match || match.from === match.to && !context.explicit) {
|
|
1862
|
+
return null;
|
|
1863
|
+
}
|
|
1864
|
+
console.log(match);
|
|
1865
|
+
return {
|
|
1866
|
+
from: match.from,
|
|
1867
|
+
options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
|
|
1868
|
+
label: `@${value}`
|
|
1869
|
+
}))
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
]
|
|
1873
|
+
});
|
|
1936
1874
|
};
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
}
|
|
1942
|
-
const length = patch.length || 1;
|
|
1943
|
-
return [
|
|
1944
|
-
{
|
|
1945
|
-
from: index,
|
|
1946
|
-
to: index + length
|
|
1947
|
-
}
|
|
1948
|
-
];
|
|
1875
|
+
|
|
1876
|
+
// packages/ui/react-ui-editor/src/extensions/mermaid.ts
|
|
1877
|
+
var mermaid = () => {
|
|
1878
|
+
return [];
|
|
1949
1879
|
};
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
}
|
|
1962
|
-
return [
|
|
1963
|
-
{
|
|
1964
|
-
from: 0,
|
|
1965
|
-
to: length,
|
|
1966
|
-
insert: patch.value
|
|
1967
|
-
}
|
|
1968
|
-
];
|
|
1880
|
+
|
|
1881
|
+
// packages/ui/react-ui-editor/src/extensions/table.ts
|
|
1882
|
+
import { syntaxTree as syntaxTree3 } from "@codemirror/language";
|
|
1883
|
+
import { RangeSetBuilder as RangeSetBuilder3, StateField as StateField6 } from "@codemirror/state";
|
|
1884
|
+
import { Decoration as Decoration6, EditorView as EditorView10, WidgetType as WidgetType5 } from "@codemirror/view";
|
|
1885
|
+
var table = (options = {}) => {
|
|
1886
|
+
return StateField6.define({
|
|
1887
|
+
create: (state) => update3(state, options),
|
|
1888
|
+
update: (_, tr) => update3(tr.state, options),
|
|
1889
|
+
provide: (field) => EditorView10.decorations.from(field)
|
|
1890
|
+
});
|
|
1969
1891
|
};
|
|
1970
|
-
var
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1892
|
+
var update3 = (state, options) => {
|
|
1893
|
+
const builder = new RangeSetBuilder3();
|
|
1894
|
+
const cursor = state.selection.main.head;
|
|
1895
|
+
const tables = [];
|
|
1896
|
+
const getTable = () => tables[tables.length - 1];
|
|
1897
|
+
const getRow = () => {
|
|
1898
|
+
const table2 = getTable();
|
|
1899
|
+
return table2.rows?.[table2.rows.length - 1];
|
|
1900
|
+
};
|
|
1901
|
+
syntaxTree3(state).iterate({
|
|
1902
|
+
enter: (node) => {
|
|
1903
|
+
switch (node.name) {
|
|
1904
|
+
case "Table": {
|
|
1905
|
+
tables.push({
|
|
1906
|
+
from: node.from,
|
|
1907
|
+
to: node.to
|
|
1908
|
+
});
|
|
1909
|
+
break;
|
|
1910
|
+
}
|
|
1911
|
+
case "TableHeader": {
|
|
1912
|
+
getTable().header = [];
|
|
1913
|
+
break;
|
|
1914
|
+
}
|
|
1915
|
+
case "TableRow": {
|
|
1916
|
+
(getTable().rows ??= []).push([]);
|
|
1917
|
+
break;
|
|
1918
|
+
}
|
|
1919
|
+
case "TableCell": {
|
|
1920
|
+
const row = getRow();
|
|
1921
|
+
if (row) {
|
|
1922
|
+
row.push(state.sliceDoc(node.from, node.to));
|
|
1923
|
+
} else {
|
|
1924
|
+
getTable().header?.push(state.sliceDoc(node.from, node.to));
|
|
1925
|
+
}
|
|
1926
|
+
break;
|
|
1927
|
+
}
|
|
1928
|
+
}
|
|
1977
1929
|
}
|
|
1978
|
-
}
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1930
|
+
});
|
|
1931
|
+
tables.forEach((table2) => {
|
|
1932
|
+
const hide = state.readOnly || cursor < table2.from || cursor > table2.to;
|
|
1933
|
+
hide && builder.add(table2.from, table2.to, Decoration6.replace({
|
|
1934
|
+
block: true,
|
|
1935
|
+
widget: new TableWidget(table2)
|
|
1936
|
+
}));
|
|
1937
|
+
builder.add(table2.from, table2.to, Decoration6.mark({
|
|
1938
|
+
class: "cm-table"
|
|
1939
|
+
}));
|
|
1940
|
+
});
|
|
1941
|
+
return builder.finish();
|
|
1984
1942
|
};
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
const { lastHeads, path } = state.field(field);
|
|
1990
|
-
let hasChanges = false;
|
|
1991
|
-
for (const tr of transactions) {
|
|
1992
|
-
tr.changes.iterChanges(() => {
|
|
1993
|
-
hasChanges = true;
|
|
1994
|
-
});
|
|
1943
|
+
var TableWidget = class extends WidgetType5 {
|
|
1944
|
+
constructor(_table) {
|
|
1945
|
+
super();
|
|
1946
|
+
this._table = _table;
|
|
1995
1947
|
}
|
|
1996
|
-
|
|
1997
|
-
return
|
|
1948
|
+
eq(other) {
|
|
1949
|
+
return this._table.from === other?._table?.from;
|
|
1998
1950
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
1951
|
+
toDOM(view) {
|
|
1952
|
+
const table2 = document.createElement("table");
|
|
1953
|
+
{
|
|
1954
|
+
const header = table2.appendChild(document.createElement("thead"));
|
|
1955
|
+
const tr = header.appendChild(document.createElement("tr"));
|
|
1956
|
+
this._table.header?.forEach((cell) => {
|
|
1957
|
+
const th = document.createElement("th");
|
|
1958
|
+
th.setAttribute("class", "cm-table-head");
|
|
1959
|
+
tr.appendChild(th).textContent = cell;
|
|
2003
1960
|
});
|
|
2004
1961
|
}
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
this._queue = [];
|
|
2014
|
-
this.reconcile = (handle, view) => {
|
|
2015
|
-
if (this._inReconcile) {
|
|
2016
|
-
return;
|
|
2017
|
-
}
|
|
2018
|
-
this._inReconcile = true;
|
|
2019
|
-
const path = getPath(view.state, this._field);
|
|
2020
|
-
const oldHeads = getLastHeads(view.state, this._field);
|
|
2021
|
-
let selection = view.state.selection;
|
|
2022
|
-
const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
|
|
2023
|
-
const toInvert = transactions.slice().reverse();
|
|
2024
|
-
for (const tx of toInvert) {
|
|
2025
|
-
const inverted = tx.changes.invert(tx.startState.doc);
|
|
2026
|
-
selection = selection.map(inverted);
|
|
2027
|
-
view.dispatch({
|
|
2028
|
-
changes: inverted,
|
|
2029
|
-
annotations: reconcileAnnotationType.of(true)
|
|
1962
|
+
{
|
|
1963
|
+
const body = table2.appendChild(document.createElement("tbody"));
|
|
1964
|
+
this._table.rows?.forEach((row) => {
|
|
1965
|
+
const tr = body.appendChild(document.createElement("tr"));
|
|
1966
|
+
row.forEach((cell) => {
|
|
1967
|
+
const td = document.createElement("td");
|
|
1968
|
+
td.setAttribute("class", "cm-table-cell");
|
|
1969
|
+
tr.appendChild(td).textContent = cell;
|
|
2030
1970
|
});
|
|
2031
|
-
}
|
|
2032
|
-
let newHeads = codemirrorToAutomerge(this._field, handle, transactions, view.state);
|
|
2033
|
-
if (newHeads === null || newHeads === void 0) {
|
|
2034
|
-
newHeads = automerge.getHeads(handle.docSync());
|
|
2035
|
-
}
|
|
2036
|
-
const diff = automerge.equals(oldHeads, newHeads) ? [] : automerge.diff(handle.docSync(), oldHeads, newHeads);
|
|
2037
|
-
automergeToCodemirror(view, selection, path, diff);
|
|
2038
|
-
view.dispatch({
|
|
2039
|
-
effects: updateHeads(newHeads),
|
|
2040
|
-
annotations: reconcileAnnotationType.of({})
|
|
2041
1971
|
});
|
|
2042
|
-
this._inReconcile = false;
|
|
2043
|
-
};
|
|
2044
|
-
if (field !== void 0) {
|
|
2045
|
-
this._field = field;
|
|
2046
1972
|
}
|
|
1973
|
+
return table2;
|
|
2047
1974
|
}
|
|
2048
1975
|
};
|
|
2049
1976
|
|
|
2050
|
-
// packages/ui/react-ui-editor/src/
|
|
2051
|
-
|
|
2052
|
-
var
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
var semaphoreFacet = Facet.define({
|
|
2058
|
-
combine: (values) => values.at(-1)
|
|
1977
|
+
// packages/ui/react-ui-editor/src/extensions/tasklist.ts
|
|
1978
|
+
import { EditorView as EditorView11, Decoration as Decoration7, WidgetType as WidgetType6, MatchDecorator as MatchDecorator3, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
|
|
1979
|
+
var styles4 = EditorView11.baseTheme({
|
|
1980
|
+
"& .cm-task-item": {
|
|
1981
|
+
paddingLeft: "4px",
|
|
1982
|
+
paddingRight: "8px"
|
|
1983
|
+
}
|
|
2059
1984
|
});
|
|
2060
|
-
var
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
1985
|
+
var CheckboxWidget = class extends WidgetType6 {
|
|
1986
|
+
constructor(_pos, _checked, _indent, _onCheck) {
|
|
1987
|
+
super();
|
|
1988
|
+
this._pos = _pos;
|
|
1989
|
+
this._checked = _checked;
|
|
1990
|
+
this._indent = _indent;
|
|
1991
|
+
this._onCheck = _onCheck;
|
|
1992
|
+
}
|
|
1993
|
+
eq(other) {
|
|
1994
|
+
return this._pos === other._pos && this._checked === other._checked && this._indent === other._indent;
|
|
1995
|
+
}
|
|
1996
|
+
toDOM(view) {
|
|
1997
|
+
const wrap = document.createElement("span");
|
|
1998
|
+
wrap.className = "cm-task-item";
|
|
1999
|
+
wrap.setAttribute("aria-hidden", "true");
|
|
2000
|
+
wrap.style.setProperty("margin-left", this._indent * 24 + "px");
|
|
2001
|
+
const input = wrap.appendChild(document.createElement("input"));
|
|
2002
|
+
input.type = "checkbox";
|
|
2003
|
+
input.checked = this._checked;
|
|
2004
|
+
if (!this._onCheck) {
|
|
2005
|
+
input.setAttribute("disabled", "true");
|
|
2006
|
+
}
|
|
2007
|
+
if (this._onCheck) {
|
|
2008
|
+
input.onchange = (event) => {
|
|
2009
|
+
this._onCheck?.(event.target.checked);
|
|
2010
|
+
return true;
|
|
2072
2011
|
};
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2012
|
+
}
|
|
2013
|
+
return wrap;
|
|
2014
|
+
}
|
|
2015
|
+
ignoreEvent() {
|
|
2016
|
+
return false;
|
|
2017
|
+
}
|
|
2018
|
+
};
|
|
2019
|
+
var tasklist = (options = {}) => {
|
|
2020
|
+
const taskMatcher = new MatchDecorator3({
|
|
2021
|
+
regexp: /^(\s*)- \[([ xX])\]\s/g,
|
|
2022
|
+
decoration: (match, view, pos) => {
|
|
2023
|
+
const indent = Math.floor(match[1].length / 2);
|
|
2024
|
+
const checked = match[2] === "x" || match[2] === "X";
|
|
2025
|
+
return Decoration7.replace({
|
|
2026
|
+
widget: new CheckboxWidget(pos, checked, indent, view.state.readOnly ? void 0 : (checked2) => {
|
|
2027
|
+
const idx = pos + match[0].indexOf("[") + 1;
|
|
2028
|
+
view.dispatch({
|
|
2029
|
+
changes: {
|
|
2030
|
+
from: idx,
|
|
2031
|
+
to: idx + 1,
|
|
2032
|
+
insert: checked2 ? "x" : " "
|
|
2033
|
+
},
|
|
2034
|
+
// TODO(burdon): Restore cursor position? More useful to move to end of line (can indent).
|
|
2035
|
+
selection: {
|
|
2036
|
+
anchor: idx + 3
|
|
2037
|
+
}
|
|
2038
|
+
});
|
|
2039
|
+
})
|
|
2040
|
+
});
|
|
2088
2041
|
}
|
|
2089
2042
|
});
|
|
2090
|
-
const
|
|
2091
|
-
const viewPlugin = ViewPlugin4.fromClass(class AutomergeCodemirrorViewPlugin {
|
|
2043
|
+
const tasks = ViewPlugin5.fromClass(class {
|
|
2092
2044
|
constructor(view) {
|
|
2093
|
-
this.
|
|
2094
|
-
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
2095
|
-
};
|
|
2096
|
-
this._view = view;
|
|
2097
|
-
handle.addListener("change", this._handleChange);
|
|
2045
|
+
this.tasks = taskMatcher.createDeco(view);
|
|
2098
2046
|
}
|
|
2099
2047
|
update(update4) {
|
|
2100
|
-
|
|
2101
|
-
queueMicrotask(() => {
|
|
2102
|
-
this._view.state.facet(semaphoreFacet).reconcile(handle, this._view);
|
|
2103
|
-
});
|
|
2104
|
-
}
|
|
2105
|
-
}
|
|
2106
|
-
destroy() {
|
|
2107
|
-
handle.addListener("change", this._handleChange);
|
|
2048
|
+
this.tasks = taskMatcher.updateDeco(update4, this.tasks);
|
|
2108
2049
|
}
|
|
2050
|
+
}, {
|
|
2051
|
+
decorations: (value) => value.tasks,
|
|
2052
|
+
provide: (plugin) => EditorView11.atomicRanges.of((view) => {
|
|
2053
|
+
return view.plugin(plugin)?.tasks || Decoration7.none;
|
|
2054
|
+
})
|
|
2109
2055
|
});
|
|
2110
|
-
return
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2056
|
+
return [
|
|
2057
|
+
tasks,
|
|
2058
|
+
styles4
|
|
2059
|
+
];
|
|
2060
|
+
};
|
|
2061
|
+
|
|
2062
|
+
// packages/ui/react-ui-editor/src/extensions/typewriter.ts
|
|
2063
|
+
import { keymap as keymap5 } from "@codemirror/view";
|
|
2064
|
+
var defaultItems = [
|
|
2065
|
+
"hello world!",
|
|
2066
|
+
"this is a test.",
|
|
2067
|
+
"this is [DXOS](https://dxos.org)"
|
|
2068
|
+
];
|
|
2069
|
+
var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
|
|
2070
|
+
let t;
|
|
2071
|
+
let idx = 0;
|
|
2072
|
+
return [
|
|
2073
|
+
keymap5.of([
|
|
2074
|
+
{
|
|
2075
|
+
// Reset.
|
|
2076
|
+
key: "alt-meta-'",
|
|
2077
|
+
run: (view) => {
|
|
2078
|
+
clearTimeout(t);
|
|
2079
|
+
idx = 0;
|
|
2080
|
+
return true;
|
|
2081
|
+
}
|
|
2082
|
+
},
|
|
2083
|
+
{
|
|
2084
|
+
// Next prompt.
|
|
2085
|
+
// TODO(burdon): Press 1-9 to select prompt?
|
|
2086
|
+
key: "shift-meta-'",
|
|
2087
|
+
run: (view) => {
|
|
2088
|
+
clearTimeout(t);
|
|
2089
|
+
const text = items[idx++];
|
|
2090
|
+
if (idx === items?.length) {
|
|
2091
|
+
idx = 0;
|
|
2092
|
+
}
|
|
2093
|
+
let i = 0;
|
|
2094
|
+
const insert = (d = 0) => {
|
|
2095
|
+
t = setTimeout(() => {
|
|
2096
|
+
const pos = view.state.selection.main.head;
|
|
2097
|
+
view.dispatch({
|
|
2098
|
+
changes: {
|
|
2099
|
+
from: pos,
|
|
2100
|
+
insert: text[i++]
|
|
2101
|
+
},
|
|
2102
|
+
selection: {
|
|
2103
|
+
anchor: pos + 1
|
|
2104
|
+
}
|
|
2105
|
+
});
|
|
2106
|
+
if (i < text.length) {
|
|
2107
|
+
insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
|
|
2108
|
+
}
|
|
2109
|
+
}, d);
|
|
2110
|
+
};
|
|
2111
|
+
insert();
|
|
2112
|
+
return true;
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
])
|
|
2116
|
+
];
|
|
2117
2117
|
};
|
|
2118
|
-
var reconcileAnnotationType = Annotation.define();
|
|
2119
|
-
var isReconcileTx = (tr) => !!tr.annotation(reconcileAnnotationType);
|
|
2120
2118
|
|
|
2121
|
-
// packages/ui/react-ui-editor/src/
|
|
2122
|
-
import
|
|
2123
|
-
var
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2119
|
+
// packages/ui/react-ui-editor/src/themes/default.ts
|
|
2120
|
+
import get5 from "lodash.get";
|
|
2121
|
+
var defaultTheme = {
|
|
2122
|
+
//
|
|
2123
|
+
// Main layout:
|
|
2124
|
+
// https://codemirror.net/examples/styling
|
|
2125
|
+
//
|
|
2126
|
+
// <div class="cm-editor [cm-focused] [generated classes]">
|
|
2127
|
+
// <div class="cm-scroller">
|
|
2128
|
+
// <div class="cm-gutters">
|
|
2129
|
+
// <div class="cm-gutter [...]">
|
|
2130
|
+
// <div class="cm-gutterElement">...</div>
|
|
2131
|
+
// </div>
|
|
2132
|
+
// </div>
|
|
2133
|
+
// <div class="cm-content" role="textbox" contenteditable="true">
|
|
2134
|
+
// <div class="cm-line"></div>
|
|
2135
|
+
// </div>
|
|
2136
|
+
// <div class="cm-selectionLayer">
|
|
2137
|
+
// <div class="cm-selectionBackground"></div>
|
|
2138
|
+
// </div>
|
|
2139
|
+
// <div class="cm-cursorLayer">
|
|
2140
|
+
// <div class="cm-cursor"></div>
|
|
2141
|
+
// </div>
|
|
2142
|
+
// </div>
|
|
2143
|
+
// </div>
|
|
2144
|
+
//
|
|
2145
|
+
"&": {},
|
|
2146
|
+
"&.cm-focused": {
|
|
2147
|
+
outline: "none"
|
|
2127
2148
|
},
|
|
2128
|
-
{
|
|
2129
|
-
|
|
2130
|
-
|
|
2149
|
+
"& .cm-scroller": {
|
|
2150
|
+
overflow: "visible",
|
|
2151
|
+
fontFamily: get5(tokens, "fontFamily.mono", []).join(",")
|
|
2131
2152
|
},
|
|
2132
|
-
{
|
|
2133
|
-
|
|
2134
|
-
|
|
2153
|
+
"& .cm-content": {
|
|
2154
|
+
padding: 0,
|
|
2155
|
+
// NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
|
|
2156
|
+
fontSize: "16px"
|
|
2135
2157
|
},
|
|
2136
|
-
{
|
|
2137
|
-
color: "
|
|
2138
|
-
|
|
2158
|
+
"&light .cm-content": {
|
|
2159
|
+
color: get5(tokens, "extend.colors.neutral.900", "black"),
|
|
2160
|
+
caretColor: "black"
|
|
2139
2161
|
},
|
|
2140
|
-
{
|
|
2141
|
-
color: "
|
|
2142
|
-
|
|
2162
|
+
"&dark .cm-content": {
|
|
2163
|
+
color: get5(tokens, "extend.colors.neutral.100", "white"),
|
|
2164
|
+
caretColor: "white"
|
|
2143
2165
|
},
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2166
|
+
//
|
|
2167
|
+
// Cursor
|
|
2168
|
+
//
|
|
2169
|
+
"&light .cm-cursor, &light .cm-dropCursor": {
|
|
2170
|
+
borderLeft: "2px solid black"
|
|
2147
2171
|
},
|
|
2148
|
-
{
|
|
2149
|
-
|
|
2150
|
-
light: "#8acb8833"
|
|
2172
|
+
"&dark .cm-cursor, &dark .cm-dropCursor": {
|
|
2173
|
+
borderLeft: "2px solid white"
|
|
2151
2174
|
},
|
|
2152
|
-
{
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2175
|
+
"& .cm-placeholder": {
|
|
2176
|
+
fontWeight: 100
|
|
2177
|
+
},
|
|
2178
|
+
//
|
|
2179
|
+
// line
|
|
2180
|
+
//
|
|
2181
|
+
"& .cm-line": {
|
|
2182
|
+
paddingInline: 0
|
|
2183
|
+
},
|
|
2184
|
+
"& .cm-activeLine": {
|
|
2185
|
+
background: "inherit"
|
|
2186
|
+
},
|
|
2187
|
+
//
|
|
2188
|
+
// Selection
|
|
2189
|
+
//
|
|
2190
|
+
"&light .cm-selectionBackground": {
|
|
2191
|
+
background: get5(tokens, "extend.colors.primary.100")
|
|
2192
|
+
},
|
|
2193
|
+
"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2194
|
+
background: get5(tokens, "extend.colors.primary.200")
|
|
2195
|
+
},
|
|
2196
|
+
"&dark .cm-selectionBackground": {
|
|
2197
|
+
background: get5(tokens, "extend.colors.primary.700")
|
|
2198
|
+
},
|
|
2199
|
+
"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
|
|
2200
|
+
background: get5(tokens, "extend.colors.primary.600")
|
|
2201
|
+
},
|
|
2202
|
+
//
|
|
2203
|
+
// Search
|
|
2204
|
+
//
|
|
2205
|
+
"&light .cm-searchMatch": {
|
|
2206
|
+
backgroundColor: get5(tokens, "extend.colors.yellow.100")
|
|
2207
|
+
},
|
|
2208
|
+
"&dark .cm-searchMatch": {
|
|
2209
|
+
backgroundColor: get5(tokens, "extend.colors.yellow.700")
|
|
2210
|
+
},
|
|
2211
|
+
//
|
|
2212
|
+
// collaboration
|
|
2213
|
+
// TODO(burdon): Review classnames (YJS dependent?) Note: e2e tests depend on this class.
|
|
2214
|
+
//
|
|
2215
|
+
"&light .cm-ySelection, &light .cm-yLineSelection": {
|
|
2216
|
+
mixBlendMode: "multiply"
|
|
2217
|
+
},
|
|
2218
|
+
"&dark .cm-ySelection, &dark .cm-yLineSelection": {
|
|
2219
|
+
mixBlendMode: "screen"
|
|
2220
|
+
},
|
|
2221
|
+
"& .cm-ySelectionInfo": {
|
|
2222
|
+
padding: "2px 4px",
|
|
2223
|
+
marginBlockStart: "-4px"
|
|
2224
|
+
},
|
|
2225
|
+
"& .cm-ySelection, & .cm-selectionMatch": {
|
|
2226
|
+
paddingBlockStart: ".15em",
|
|
2227
|
+
paddingBlockEnd: ".15em"
|
|
2228
|
+
},
|
|
2229
|
+
"& .cm-ySelectionCaret": {
|
|
2230
|
+
display: "inline-block",
|
|
2231
|
+
insetBlockStart: ".1em",
|
|
2232
|
+
blockSize: "1.4em",
|
|
2233
|
+
verticalAlign: "top"
|
|
2234
|
+
},
|
|
2235
|
+
"& .cm-yLineSelection": {
|
|
2236
|
+
margin: 0
|
|
2237
|
+
},
|
|
2238
|
+
//
|
|
2239
|
+
// link
|
|
2240
|
+
//
|
|
2241
|
+
"& .cm-link": {
|
|
2242
|
+
color: get5(tokens, "extend.colors.primary.500"),
|
|
2243
|
+
textDecorationLine: "underline",
|
|
2244
|
+
textDecorationThickness: "1px",
|
|
2245
|
+
textUnderlineOffset: "2px",
|
|
2246
|
+
borderRadius: ".125rem",
|
|
2247
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2248
|
+
},
|
|
2249
|
+
//
|
|
2250
|
+
// tooltip
|
|
2251
|
+
//
|
|
2252
|
+
"& .cm-tooltip": {
|
|
2253
|
+
border: "none"
|
|
2254
|
+
},
|
|
2255
|
+
"& .cm-tooltip-below": {},
|
|
2256
|
+
//
|
|
2257
|
+
// autocomplete
|
|
2258
|
+
//
|
|
2259
|
+
"& .cm-tooltip-autocomplete": {
|
|
2260
|
+
marginTop: "4px",
|
|
2261
|
+
marginLeft: "-3px"
|
|
2262
|
+
},
|
|
2263
|
+
"& .cm-tooltip-autocomplete ul li": {},
|
|
2264
|
+
"& .cm-tooltip-autocomplete ul li[aria-selected]": {},
|
|
2265
|
+
"& .cm-completionIcon": {
|
|
2266
|
+
display: "none"
|
|
2267
|
+
},
|
|
2268
|
+
"& .cm-completionLabel": {
|
|
2269
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2270
|
+
},
|
|
2271
|
+
"& .cm-completionMatchedText": {
|
|
2272
|
+
textDecoration: "none"
|
|
2273
|
+
},
|
|
2274
|
+
//
|
|
2275
|
+
// widgets
|
|
2276
|
+
//
|
|
2277
|
+
"& .cm-widgetBuffer": {
|
|
2278
|
+
display: "none",
|
|
2279
|
+
height: 0
|
|
2280
|
+
},
|
|
2281
|
+
//
|
|
2282
|
+
// table
|
|
2283
|
+
//
|
|
2284
|
+
"& .cm-table *": {
|
|
2285
|
+
fontFamily: `${get5(tokens, "fontFamily.mono", []).join(",")} !important`,
|
|
2286
|
+
textDecoration: "none !important"
|
|
2287
|
+
},
|
|
2288
|
+
"& .cm-table-head": {
|
|
2289
|
+
padding: "2px 16px 2px 0px",
|
|
2290
|
+
borderBottom: `1px solid ${get5(tokens, "extend.colors.neutral.500")}`,
|
|
2291
|
+
fontWeight: 100,
|
|
2292
|
+
textAlign: "left",
|
|
2293
|
+
color: get5(tokens, "extend.colors.neutral.500")
|
|
2294
|
+
},
|
|
2295
|
+
"& .cm-table-cell": {
|
|
2296
|
+
padding: "2px 16px 2px 0px"
|
|
2297
|
+
},
|
|
2298
|
+
//
|
|
2299
|
+
// image
|
|
2300
|
+
//
|
|
2301
|
+
"& .cm-image": {
|
|
2302
|
+
margin: "0.5rem 0"
|
|
2303
|
+
},
|
|
2304
|
+
//
|
|
2305
|
+
// font size
|
|
2306
|
+
// TODO(thure): This appears to be the best or only way to set selection caret heights, but it's far more verbose than it needs to be.
|
|
2307
|
+
//
|
|
2308
|
+
...Object.keys(get5(tokens, "extend.fontSize", {})).reduce((acc, fontSize) => {
|
|
2309
|
+
const height = get5(tokens, [
|
|
2310
|
+
"extend",
|
|
2311
|
+
"fontSize",
|
|
2312
|
+
fontSize,
|
|
2313
|
+
1,
|
|
2314
|
+
"lineHeight"
|
|
2315
|
+
]);
|
|
2316
|
+
acc[`& .text-${fontSize} + .cm-ySelectionCaret`] = {
|
|
2317
|
+
height
|
|
2318
|
+
};
|
|
2319
|
+
acc[`& .text-${fontSize} + .cm-ySelection + .cm-ySelectionCaret`] = {
|
|
2320
|
+
height
|
|
2321
|
+
};
|
|
2322
|
+
acc[`& .text-${fontSize} + .cm-widgetBuffer + .cm-ySelectionCaret`] = {
|
|
2323
|
+
height
|
|
2324
|
+
};
|
|
2325
|
+
return acc;
|
|
2326
|
+
}, {}),
|
|
2327
|
+
/**
|
|
2328
|
+
* Panels
|
|
2329
|
+
* TODO(burdon): Needs styling attention (esp. dark mode).
|
|
2330
|
+
* https://github.com/codemirror/search/blob/main/src/search.ts#L745
|
|
2331
|
+
*
|
|
2332
|
+
* Find/replace panel.
|
|
2333
|
+
* <div class="cm-announced">...</div>
|
|
2334
|
+
* <div class="cm-scroller">...</div>
|
|
2335
|
+
* <div class="cm-panels cm-panels-bottom">
|
|
2336
|
+
* <div class="cm-search cm-panel">
|
|
2337
|
+
* <input class="cm-textfield" />
|
|
2338
|
+
* <button class="cm-button">...</button>
|
|
2339
|
+
* <label><input type="checkbox" />...</label>
|
|
2340
|
+
* </div>
|
|
2341
|
+
* </div
|
|
2342
|
+
*/
|
|
2343
|
+
"& .cm-panels": {
|
|
2344
|
+
border: `1px solid ${get5(tokens, "extend.colors.neutral.200")}`
|
|
2345
|
+
},
|
|
2346
|
+
"& .cm-panel": {
|
|
2347
|
+
background: get5(tokens, "extend.colors.neutral.50"),
|
|
2348
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2349
|
+
},
|
|
2350
|
+
"& .cm-button": {
|
|
2351
|
+
margin: "4px",
|
|
2352
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(","),
|
|
2353
|
+
background: get5(tokens, "extend.colors.neutral.100"),
|
|
2354
|
+
backgroundImage: "none",
|
|
2355
|
+
border: "none",
|
|
2356
|
+
"&:hover": {
|
|
2357
|
+
background: get5(tokens, "extend.colors.neutral.200")
|
|
2358
|
+
},
|
|
2359
|
+
"&:active": {
|
|
2360
|
+
background: get5(tokens, "extend.colors.neutral.300"),
|
|
2361
|
+
backgroundImage: "none"
|
|
2181
2362
|
}
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
]))
|
|
2190
|
-
|
|
2363
|
+
},
|
|
2364
|
+
"& .cm-panel input[type=checkbox]": {
|
|
2365
|
+
marginRight: "0.4rem !important"
|
|
2366
|
+
}
|
|
2367
|
+
};
|
|
2368
|
+
var textTheme = {
|
|
2369
|
+
"& .cm-scroller": {
|
|
2370
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2371
|
+
},
|
|
2372
|
+
"& .cm-placeholder": {
|
|
2373
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2374
|
+
}
|
|
2375
|
+
};
|
|
2376
|
+
var markdownTheme = {
|
|
2377
|
+
// NOTE: Must leave base font family as is (i.e., monospace) due to fenced code blocks.
|
|
2378
|
+
"& .cm-placeholder": {
|
|
2379
|
+
fontFamily: get5(tokens, "fontFamily.body", []).join(",")
|
|
2191
2380
|
}
|
|
2192
|
-
|
|
2193
|
-
|
|
2381
|
+
};
|
|
2382
|
+
var codeTheme = {
|
|
2383
|
+
"& .cm-scroller": {
|
|
2384
|
+
fontFamily: get5(tokens, "fontFamily.mono", []).join(",")
|
|
2385
|
+
},
|
|
2386
|
+
"& .cm-placeholder": {
|
|
2387
|
+
fontFamily: get5(tokens, "fontFamily.mono", []).join(",")
|
|
2194
2388
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2389
|
+
};
|
|
2390
|
+
|
|
2391
|
+
// packages/ui/react-ui-editor/src/components/TextEditor/TextEditor.tsx
|
|
2392
|
+
var EditorModes = [
|
|
2393
|
+
"default",
|
|
2394
|
+
"vim"
|
|
2395
|
+
];
|
|
2396
|
+
var BaseTextEditor = /* @__PURE__ */ forwardRef(({ model, readonly, comments: comments2, extensions = [], editorMode, slots = defaultSlots }, forwardedRef) => {
|
|
2397
|
+
const { themeMode } = useThemeContext();
|
|
2398
|
+
const tabsterDOMAttribute = useFocusableGroup({
|
|
2399
|
+
tabBehavior: "limited"
|
|
2400
|
+
});
|
|
2401
|
+
const [root, setRoot] = useState2(null);
|
|
2402
|
+
const [{ state = void 0, view = void 0 } = {}, setEditor] = useState2();
|
|
2403
|
+
useImperativeHandle(forwardedRef, () => ({
|
|
2404
|
+
root,
|
|
2405
|
+
state,
|
|
2406
|
+
view
|
|
2407
|
+
}), [
|
|
2408
|
+
view,
|
|
2409
|
+
state,
|
|
2410
|
+
root
|
|
2411
|
+
]);
|
|
2412
|
+
const { awareness, peer } = model;
|
|
2413
|
+
useEffect2(() => {
|
|
2414
|
+
if (awareness && peer) {
|
|
2415
|
+
awareness.setLocalStateField("user", {
|
|
2416
|
+
name: peer.name ?? generateName(peer.id),
|
|
2417
|
+
color: getColorForValue({
|
|
2418
|
+
value: peer.id,
|
|
2419
|
+
type: "color"
|
|
2420
|
+
}),
|
|
2421
|
+
colorLight: getColorForValue({
|
|
2422
|
+
value: peer.id,
|
|
2423
|
+
themeMode,
|
|
2424
|
+
type: "highlight"
|
|
2425
|
+
})
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
}, [
|
|
2429
|
+
awareness,
|
|
2430
|
+
peer,
|
|
2431
|
+
themeMode
|
|
2432
|
+
]);
|
|
2433
|
+
useEffect2(() => {
|
|
2434
|
+
if (view && comments2?.length) {
|
|
2435
|
+
view.dispatch({
|
|
2436
|
+
effects: setCommentRange.of({
|
|
2437
|
+
model,
|
|
2438
|
+
comments: comments2
|
|
2439
|
+
})
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
}, [
|
|
2443
|
+
view,
|
|
2444
|
+
comments2
|
|
2445
|
+
]);
|
|
2446
|
+
useEffect2(() => {
|
|
2447
|
+
if (!root) {
|
|
2448
|
+
return;
|
|
2449
|
+
}
|
|
2450
|
+
const state2 = EditorState2.create({
|
|
2451
|
+
doc: model.text(),
|
|
2452
|
+
extensions: [
|
|
2453
|
+
readonly && EditorState2.readOnly.of(readonly),
|
|
2454
|
+
// TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
|
|
2455
|
+
editorMode === "vim" && vim(),
|
|
2456
|
+
// Theme.
|
|
2457
|
+
// TODO(burdon): Make theme configurable.
|
|
2458
|
+
EditorView12.baseTheme(defaultTheme),
|
|
2459
|
+
EditorView12.theme(slots?.editor?.theme ?? {}),
|
|
2460
|
+
EditorView12.darkTheme.of(themeMode === "dark"),
|
|
2461
|
+
// Storage and replication.
|
|
2462
|
+
model.extension,
|
|
2463
|
+
// Custom.
|
|
2464
|
+
...extensions
|
|
2465
|
+
].filter(Boolean)
|
|
2206
2466
|
});
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
log("space message", payload, {
|
|
2215
|
-
F: __dxlog_file3,
|
|
2216
|
-
L: 79,
|
|
2217
|
-
S: this,
|
|
2218
|
-
C: (f, a) => f(...a)
|
|
2467
|
+
view?.destroy();
|
|
2468
|
+
setEditor({
|
|
2469
|
+
state: state2,
|
|
2470
|
+
view: new EditorView12({
|
|
2471
|
+
state: state2,
|
|
2472
|
+
parent: root
|
|
2473
|
+
})
|
|
2219
2474
|
});
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2475
|
+
return () => {
|
|
2476
|
+
view?.destroy();
|
|
2477
|
+
setEditor(void 0);
|
|
2478
|
+
};
|
|
2479
|
+
}, [
|
|
2480
|
+
root,
|
|
2481
|
+
model,
|
|
2482
|
+
readonly,
|
|
2483
|
+
editorMode,
|
|
2484
|
+
themeMode
|
|
2485
|
+
]);
|
|
2486
|
+
const handleKeyUp = useCallback((event) => {
|
|
2487
|
+
const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
|
|
2488
|
+
switch (key) {
|
|
2489
|
+
case "Enter": {
|
|
2490
|
+
view?.contentDOM.focus();
|
|
2234
2491
|
break;
|
|
2235
2492
|
}
|
|
2236
|
-
case
|
|
2237
|
-
|
|
2238
|
-
encoding.writeVarUint8Array(encoder, awarenessProtocol.encodeAwarenessUpdate(this._awareness, Array.from(this._awareness.getStates().keys())));
|
|
2239
|
-
sendReply = true;
|
|
2493
|
+
case "Escape": {
|
|
2494
|
+
editorMode === "vim" && (altKey || shiftKey || metaKey || ctrlKey) && root?.focus();
|
|
2240
2495
|
break;
|
|
2241
2496
|
}
|
|
2242
|
-
default: {
|
|
2243
|
-
console.error("Invalid message:", messageType);
|
|
2244
|
-
return encoder;
|
|
2245
|
-
}
|
|
2246
|
-
}
|
|
2247
|
-
if (!sendReply) {
|
|
2248
|
-
return null;
|
|
2249
2497
|
}
|
|
2250
|
-
return encoder;
|
|
2251
|
-
}
|
|
2252
|
-
_handleBeforeUnload() {
|
|
2253
|
-
awarenessProtocol.removeAwarenessStates(this._awareness, [
|
|
2254
|
-
this._clientId
|
|
2255
|
-
], "window unload");
|
|
2256
|
-
}
|
|
2257
|
-
};
|
|
2258
|
-
|
|
2259
|
-
// packages/ui/react-ui-editor/src/hooks/useTextModel.ts
|
|
2260
|
-
var __dxlog_file4 = "/home/circleci/project/packages/ui/react-ui-editor/src/hooks/useTextModel.ts";
|
|
2261
|
-
var useTextModel = ({ identity, space, text }) => {
|
|
2262
|
-
const [model, setModel] = useState2(() => createModel({
|
|
2263
|
-
identity,
|
|
2264
|
-
space,
|
|
2265
|
-
text
|
|
2266
|
-
}));
|
|
2267
|
-
useEffect3(() => {
|
|
2268
|
-
setModel(createModel({
|
|
2269
|
-
identity,
|
|
2270
|
-
space,
|
|
2271
|
-
text
|
|
2272
|
-
}));
|
|
2273
2498
|
}, [
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
text
|
|
2499
|
+
view,
|
|
2500
|
+
editorMode
|
|
2277
2501
|
]);
|
|
2278
|
-
return
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2502
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
2503
|
+
key: model.id,
|
|
2504
|
+
ref: setRoot,
|
|
2505
|
+
tabIndex: 0,
|
|
2506
|
+
...slots?.root,
|
|
2507
|
+
...editorMode !== "vim" && tabsterDOMAttribute,
|
|
2508
|
+
onKeyUp: handleKeyUp
|
|
2509
|
+
});
|
|
2510
|
+
});
|
|
2511
|
+
var TextEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
2512
|
+
const { themeMode } = useThemeContext();
|
|
2513
|
+
const slots = defaultsDeep2({}, _slots, defaultTextSlots);
|
|
2514
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2515
|
+
ref: forwardedRef,
|
|
2516
|
+
readonly,
|
|
2517
|
+
extensions: [
|
|
2518
|
+
basicBundle({
|
|
2519
|
+
readonly,
|
|
2520
|
+
themeMode,
|
|
2521
|
+
placeholder: slots?.editor?.placeholder
|
|
2522
|
+
}),
|
|
2523
|
+
...extensions
|
|
2524
|
+
],
|
|
2525
|
+
slots,
|
|
2526
|
+
...props
|
|
2527
|
+
});
|
|
2528
|
+
});
|
|
2529
|
+
var MarkdownEditor = /* @__PURE__ */ forwardRef(({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
|
|
2530
|
+
const { themeMode } = useThemeContext();
|
|
2531
|
+
const slots = defaultsDeep2({}, _slots, defaultMarkdownSlots);
|
|
2532
|
+
return /* @__PURE__ */ React.createElement(BaseTextEditor, {
|
|
2533
|
+
ref: forwardedRef,
|
|
2534
|
+
readonly,
|
|
2535
|
+
extensions: [
|
|
2536
|
+
markdownBundle({
|
|
2537
|
+
readonly,
|
|
2538
|
+
themeMode,
|
|
2539
|
+
placeholder: slots?.editor?.placeholder
|
|
2540
|
+
}),
|
|
2541
|
+
...extensions
|
|
2542
|
+
],
|
|
2543
|
+
slots,
|
|
2544
|
+
...props
|
|
2545
|
+
});
|
|
2546
|
+
});
|
|
2547
|
+
var defaultSlots = {
|
|
2548
|
+
root: {
|
|
2549
|
+
className: mx3("p-2", inputSurface)
|
|
2289
2550
|
}
|
|
2290
2551
|
};
|
|
2291
|
-
var
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
A: [
|
|
2297
|
-
"text?.doc && text?.content",
|
|
2298
|
-
""
|
|
2299
|
-
]
|
|
2300
|
-
});
|
|
2301
|
-
const provider = space ? new SpaceAwarenessProvider({
|
|
2302
|
-
space,
|
|
2303
|
-
doc: text.doc,
|
|
2304
|
-
channel: `yjs.awareness.${text.id}`
|
|
2305
|
-
}) : void 0;
|
|
2306
|
-
return {
|
|
2307
|
-
id: text.doc.guid,
|
|
2308
|
-
content: text.content,
|
|
2309
|
-
text: () => text.content.toString(),
|
|
2310
|
-
ranges: [],
|
|
2311
|
-
extension: yCollab(text.content, provider?.awareness),
|
|
2312
|
-
awareness: provider?.awareness,
|
|
2313
|
-
peer: identity ? {
|
|
2314
|
-
id: identity.identityKey.toHex(),
|
|
2315
|
-
name: identity.profile?.displayName
|
|
2316
|
-
} : void 0
|
|
2317
|
-
};
|
|
2552
|
+
var defaultTextSlots = {
|
|
2553
|
+
...defaultSlots,
|
|
2554
|
+
editor: {
|
|
2555
|
+
theme: textTheme
|
|
2556
|
+
}
|
|
2318
2557
|
};
|
|
2319
|
-
var
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
return {
|
|
2325
|
-
id: obj.id,
|
|
2326
|
-
content: doc,
|
|
2327
|
-
text: () => get4(doc.handle.docSync(), doc.path),
|
|
2328
|
-
ranges: [],
|
|
2329
|
-
extension: automergePlugin(doc.handle, doc.path),
|
|
2330
|
-
peer: identity ? {
|
|
2331
|
-
id: identity.identityKey.toHex(),
|
|
2332
|
-
name: identity.profile?.displayName
|
|
2333
|
-
} : void 0
|
|
2334
|
-
};
|
|
2558
|
+
var defaultMarkdownSlots = {
|
|
2559
|
+
...defaultSlots,
|
|
2560
|
+
editor: {
|
|
2561
|
+
theme: markdownTheme
|
|
2562
|
+
}
|
|
2335
2563
|
};
|
|
2336
2564
|
export {
|
|
2337
2565
|
BaseTextEditor,
|
|
@@ -2355,25 +2583,24 @@ export {
|
|
|
2355
2583
|
defaultSlots,
|
|
2356
2584
|
defaultTextSlots,
|
|
2357
2585
|
defaultTheme,
|
|
2358
|
-
|
|
2359
|
-
highlight,
|
|
2360
|
-
highlightDecorations,
|
|
2361
|
-
highlightStateField,
|
|
2586
|
+
hr,
|
|
2362
2587
|
image,
|
|
2363
2588
|
link,
|
|
2364
|
-
|
|
2589
|
+
listener,
|
|
2365
2590
|
markdownBundle,
|
|
2366
2591
|
markdownHighlightStyle,
|
|
2367
2592
|
markdownTags,
|
|
2368
2593
|
markdownTagsExtensions,
|
|
2369
2594
|
markdownTheme,
|
|
2370
2595
|
mention,
|
|
2596
|
+
mermaid,
|
|
2597
|
+
modelState,
|
|
2598
|
+
setCommentRange,
|
|
2371
2599
|
table,
|
|
2372
2600
|
tags2 as tags,
|
|
2373
2601
|
tasklist,
|
|
2374
2602
|
textTheme,
|
|
2375
|
-
|
|
2376
|
-
useHighlights,
|
|
2603
|
+
typewriter,
|
|
2377
2604
|
useTextModel
|
|
2378
2605
|
};
|
|
2379
2606
|
//# sourceMappingURL=index.mjs.map
|