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