@dxos/react-ui-editor 0.3.11-main.9bacd9f → 0.3.11-main.9c6b000
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 +2056 -1661
- 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 -13
- package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts +20 -17
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -3
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/{hooks/automerge → components/TextEditor}/automerge.stories.d.ts +6 -1
- package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
- package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts +0 -2
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts +9 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts +5 -0
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/defs.d.ts +24 -0
- package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/index.d.ts +3 -0
- package/dist/types/src/extensions/automerge/index.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts +14 -0
- package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts +5 -0
- package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -0
- package/dist/types/src/extensions/awareness.d.ts +46 -0
- package/dist/types/src/extensions/awareness.d.ts.map +1 -0
- package/dist/types/src/extensions/basic.d.ts +8 -0
- package/dist/types/src/extensions/basic.d.ts.map +1 -0
- package/dist/types/src/extensions/blast.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/comments.d.ts +33 -18
- package/dist/types/src/extensions/comments.d.ts.map +1 -0
- package/dist/types/src/extensions/cursor.d.ts +25 -0
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/{components/TextEditor/extensions → extensions}/index.d.ts +4 -7
- package/dist/types/src/extensions/index.d.ts.map +1 -0
- package/dist/types/src/extensions/listener.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts +14 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
- package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/index.d.ts +10 -0
- package/dist/types/src/extensions/markdown/index.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
- package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
- package/dist/types/src/extensions/mention.d.ts.map +1 -0
- package/dist/types/src/extensions/outliner.d.ts +4 -0
- package/dist/types/src/extensions/outliner.d.ts.map +1 -0
- package/dist/types/src/extensions/typewriter.d.ts.map +1 -0
- package/dist/types/src/hooks/awareness-provider.d.ts +31 -0
- package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -0
- package/dist/types/src/hooks/defs.d.ts +25 -0
- package/dist/types/src/hooks/defs.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +3 -1
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
- package/dist/types/src/hooks/useTextModel.d.ts +2 -31
- package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/cursors.d.ts.map +1 -0
- package/dist/types/src/styles/index.d.ts +1 -0
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +2 -2
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/tokens.d.ts +1 -0
- package/dist/types/src/styles/tokens.d.ts.map +1 -1
- package/dist/types/src/{components/TextEditor/themes → themes}/default.d.ts +1 -1
- package/dist/types/src/themes/default.d.ts.map +1 -0
- package/dist/types/src/themes/index.d.ts.map +1 -0
- package/dist/types/src/util.d.ts +7 -0
- package/dist/types/src/util.d.ts.map +1 -0
- package/package.json +33 -34
- package/src/components/TextEditor/MarkdownEditor.stories.tsx +83 -84
- package/src/components/TextEditor/TextEditor.stories.tsx +13 -34
- package/src/components/TextEditor/TextEditor.tsx +123 -94
- package/src/{hooks/automerge → components/TextEditor}/automerge.stories.tsx +66 -6
- package/src/components/TextEditor/comments.stories.tsx +357 -0
- package/src/components/TextEditor/index.ts +0 -3
- package/src/{components/TextEditor/extensions → extensions}/autocomplete.ts +23 -19
- package/src/extensions/automerge/automerge.ts +88 -0
- package/src/extensions/automerge/cursor.ts +52 -0
- package/src/extensions/automerge/defs.ts +41 -0
- package/src/extensions/automerge/index.ts +6 -0
- package/src/{hooks → extensions}/automerge/semaphore.ts +34 -29
- package/src/{hooks → extensions}/automerge/update-automerge.ts +5 -6
- package/src/{hooks → extensions}/automerge/update-codemirror.ts +13 -5
- package/src/extensions/awareness.ts +310 -0
- package/src/extensions/basic.ts +37 -0
- package/src/extensions/comments.ts +428 -0
- package/src/extensions/cursor.ts +50 -0
- package/src/{components/TextEditor/extensions → extensions}/index.ts +4 -7
- package/src/{components/TextEditor/extensions → extensions}/markdown/bundle.ts +20 -30
- package/src/extensions/markdown/code.ts +127 -0
- package/src/extensions/markdown/heading.ts +59 -0
- package/src/{components/TextEditor/extensions → extensions}/markdown/highlight.ts +12 -27
- package/src/extensions/markdown/hr.ts +71 -0
- package/src/extensions/markdown/image.ts +87 -0
- package/src/extensions/markdown/index.ts +13 -0
- package/src/{components/TextEditor/extensions → extensions/markdown}/link.ts +85 -88
- package/src/{components/TextEditor/extensions → extensions/markdown}/table.ts +10 -4
- package/src/extensions/markdown/tasklist.ts +110 -0
- package/src/{components/TextEditor/extensions → extensions}/mention.ts +9 -2
- package/src/extensions/outliner.ts +12 -0
- package/src/{components/TextEditor/extensions → extensions}/typewriter.ts +1 -1
- package/src/hooks/awareness-provider.ts +124 -0
- package/src/hooks/defs.ts +42 -0
- package/src/hooks/index.ts +4 -1
- package/src/hooks/useTextEditor.ts +29 -0
- package/src/hooks/useTextModel.ts +30 -115
- package/src/index.ts +5 -0
- package/src/styles/index.ts +1 -0
- package/src/styles/markdown.ts +9 -12
- package/src/styles/tokens.ts +3 -0
- package/src/{components/TextEditor/themes → themes}/default.ts +50 -79
- package/src/util.ts +41 -0
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
- package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts +0 -9
- package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/hr.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +0 -17
- package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +0 -3
- package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts +0 -3
- package/dist/types/src/components/TextEditor/extensions/mermaid.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/typewriter.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/extensions/util.d.ts +0 -5
- package/dist/types/src/components/TextEditor/extensions/util.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/default.d.ts.map +0 -1
- package/dist/types/src/components/TextEditor/themes/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/handle.d.ts +0 -9
- package/dist/types/src/hooks/automerge/handle.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/index.d.ts +0 -3
- package/dist/types/src/hooks/automerge/index.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/plugin.d.ts +0 -26
- package/dist/types/src/hooks/automerge/plugin.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/semaphore.d.ts +0 -19
- package/dist/types/src/hooks/automerge/semaphore.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-automerge.d.ts +0 -6
- package/dist/types/src/hooks/automerge/update-automerge.d.ts.map +0 -1
- package/dist/types/src/hooks/automerge/update-codemirror.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/cursors.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/index.d.ts +0 -3
- package/dist/types/src/hooks/yjs/index.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/space-provider.d.ts +0 -27
- package/dist/types/src/hooks/yjs/space-provider.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts +0 -12
- package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +0 -1
- package/dist/types/src/hooks/yjs/yjs.test.d.ts +0 -2
- package/dist/types/src/hooks/yjs/yjs.test.d.ts.map +0 -1
- package/dist/types/src/testing/index.d.ts +0 -3
- package/dist/types/src/testing/index.d.ts.map +0 -1
- package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
- package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
- package/dist/types/src/testing/proto/index.d.ts +0 -2
- package/dist/types/src/testing/proto/index.d.ts.map +0 -1
- package/dist/types/src/testing/replicator.d.ts +0 -45
- package/dist/types/src/testing/replicator.d.ts.map +0 -1
- package/src/components/TextEditor/codemirror.stories.ts +0 -114
- package/src/components/TextEditor/extensions/basic.ts +0 -32
- package/src/components/TextEditor/extensions/code.ts +0 -99
- package/src/components/TextEditor/extensions/comments.ts +0 -389
- package/src/components/TextEditor/extensions/hr.ts +0 -74
- package/src/components/TextEditor/extensions/image.ts +0 -69
- package/src/components/TextEditor/extensions/markdown/index.ts +0 -6
- package/src/components/TextEditor/extensions/mermaid.ts +0 -11
- package/src/components/TextEditor/extensions/tasklist.ts +0 -121
- package/src/components/TextEditor/extensions/util.ts +0 -18
- package/src/hooks/automerge/handle.ts +0 -14
- package/src/hooks/automerge/index.ts +0 -6
- package/src/hooks/automerge/plugin.ts +0 -134
- package/src/hooks/yjs/index.ts +0 -6
- package/src/hooks/yjs/space-provider.ts +0 -126
- package/src/hooks/yjs/yjs.stories.tsx +0 -56
- package/src/hooks/yjs/yjs.test.ts +0 -59
- package/src/testing/index.ts +0 -6
- package/src/testing/proto/gen/schema.ts +0 -49
- package/src/testing/proto/index.ts +0 -5
- package/src/testing/proto/schema.proto +0 -15
- package/src/testing/replicator.ts +0 -189
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/autocomplete.d.ts +0 -0
- /package/dist/types/src/{hooks → extensions}/automerge/update-codemirror.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/blast.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/listener.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/code.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/markdown/highlight.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/hr.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/image.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/link.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/table.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions/markdown}/tasklist.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/mention.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/extensions → extensions}/typewriter.d.ts +0 -0
- /package/dist/types/src/{hooks/yjs → styles}/cursors.d.ts +0 -0
- /package/dist/types/src/{components/TextEditor/themes → themes}/index.d.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/blast.ts +0 -0
- /package/src/{components/TextEditor/extensions → extensions}/listener.ts +0 -0
- /package/src/{hooks/yjs → styles}/cursors.ts +0 -0
- /package/src/{components/TextEditor/themes → themes}/index.ts +0 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { syntaxTree } from '@codemirror/language';
|
|
6
|
+
import { RangeSetBuilder } from '@codemirror/state';
|
|
7
|
+
import {
|
|
8
|
+
ViewPlugin,
|
|
9
|
+
type DecorationSet,
|
|
10
|
+
EditorView,
|
|
11
|
+
type ViewUpdate,
|
|
12
|
+
Decoration,
|
|
13
|
+
type BlockInfo,
|
|
14
|
+
WidgetType,
|
|
15
|
+
} from '@codemirror/view';
|
|
16
|
+
|
|
17
|
+
import { mx } from '@dxos/react-ui-theme';
|
|
18
|
+
|
|
19
|
+
import { getToken } from '../../styles';
|
|
20
|
+
|
|
21
|
+
// TODO(burdon): Reconcile with theme.
|
|
22
|
+
// [aria-readonly="true"]
|
|
23
|
+
const styles = EditorView.baseTheme({
|
|
24
|
+
'& .cm-code': {
|
|
25
|
+
paddingInline: '1rem !important',
|
|
26
|
+
fontFamily: getToken('fontFamily.mono', []).join(','),
|
|
27
|
+
},
|
|
28
|
+
'& .cm-codeblock': {
|
|
29
|
+
display: 'inline-block',
|
|
30
|
+
width: '100%',
|
|
31
|
+
},
|
|
32
|
+
'&light .cm-codeblock, &light .cm-codeblock.cm-activeLine': {
|
|
33
|
+
background: getToken('extend.colors.neutral.25'),
|
|
34
|
+
},
|
|
35
|
+
'&dark .cm-codeblock, &dark .cm-codeblock.cm-activeLine': {
|
|
36
|
+
background: getToken('extend.colors.neutral.850'),
|
|
37
|
+
},
|
|
38
|
+
'& .cm-codeblock-first': {
|
|
39
|
+
borderTopLeftRadius: '.5rem',
|
|
40
|
+
borderTopRightRadius: '.5rem',
|
|
41
|
+
},
|
|
42
|
+
'& .cm-codeblock-last': {
|
|
43
|
+
borderBottomLeftRadius: '.5rem',
|
|
44
|
+
borderBottomRightRadius: '.5rem',
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// TODO(burdon): Start from EditorView.lineBlockAt(pos).
|
|
49
|
+
const getLineRange = (lines: BlockInfo[], from: number, to: number) => {
|
|
50
|
+
const start = lines.findIndex((line) => line.from >= from);
|
|
51
|
+
const end = lines.findIndex((line) => line.to >= to);
|
|
52
|
+
return [start, end];
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
class LineWidget extends WidgetType {
|
|
56
|
+
constructor(private readonly _className: string) {
|
|
57
|
+
super();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override toDOM() {
|
|
61
|
+
const el = document.createElement('span');
|
|
62
|
+
el.innerHTML = ' ';
|
|
63
|
+
el.className = mx('cm-code cm-codeblock', this._className);
|
|
64
|
+
return el;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const top = new LineWidget('cm-codeblock-first');
|
|
69
|
+
const bottom = new LineWidget('cm-codeblock-last');
|
|
70
|
+
|
|
71
|
+
const buildDecorations = (view: EditorView): DecorationSet => {
|
|
72
|
+
const builder = new RangeSetBuilder<Decoration>();
|
|
73
|
+
const { state } = view;
|
|
74
|
+
const blocks = view.viewportLineBlocks;
|
|
75
|
+
const cursor = state.selection.main.head;
|
|
76
|
+
|
|
77
|
+
// TODO(burdon): Add copy to clipboard widget.
|
|
78
|
+
for (const { from, to } of view.visibleRanges) {
|
|
79
|
+
syntaxTree(state).iterate({
|
|
80
|
+
enter: (node) => {
|
|
81
|
+
if (node.name === 'FencedCode') {
|
|
82
|
+
const edit = !view.state.readOnly && cursor >= node.from && cursor <= node.to;
|
|
83
|
+
const range = getLineRange(blocks, node.from, node.to);
|
|
84
|
+
for (let i = range[0]; i <= range[1]; i++) {
|
|
85
|
+
const block = blocks[i];
|
|
86
|
+
if (!edit && (i === range[0] || i === range[1])) {
|
|
87
|
+
builder.add(block.from, block.to, Decoration.replace({ widget: i === range[0] ? top : bottom }));
|
|
88
|
+
} else {
|
|
89
|
+
builder.add(
|
|
90
|
+
block.from,
|
|
91
|
+
block.from,
|
|
92
|
+
Decoration.line({
|
|
93
|
+
class: mx('cm-code cm-codeblock'),
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
from,
|
|
101
|
+
to,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return builder.finish();
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
export const code = () => {
|
|
109
|
+
return [
|
|
110
|
+
ViewPlugin.fromClass(
|
|
111
|
+
class {
|
|
112
|
+
decorations: DecorationSet;
|
|
113
|
+
constructor(view: EditorView) {
|
|
114
|
+
this.decorations = buildDecorations(view);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
update(update: ViewUpdate) {
|
|
118
|
+
this.decorations = buildDecorations(update.view);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
decorations: (value) => value.decorations,
|
|
123
|
+
},
|
|
124
|
+
),
|
|
125
|
+
styles,
|
|
126
|
+
];
|
|
127
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { syntaxTree } from '@codemirror/language';
|
|
6
|
+
import { RangeSetBuilder } from '@codemirror/state';
|
|
7
|
+
import { Decoration, type DecorationSet, type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view';
|
|
8
|
+
|
|
9
|
+
const buildDecorations = (view: EditorView): DecorationSet => {
|
|
10
|
+
const builder = new RangeSetBuilder<Decoration>();
|
|
11
|
+
const { state } = view;
|
|
12
|
+
const cursor = state.selection.main.head;
|
|
13
|
+
|
|
14
|
+
for (const { from, to } of view.visibleRanges) {
|
|
15
|
+
syntaxTree(state).iterate({
|
|
16
|
+
enter: (node) => {
|
|
17
|
+
switch (node.name) {
|
|
18
|
+
case 'ATXHeading1':
|
|
19
|
+
case 'ATXHeading2':
|
|
20
|
+
case 'ATXHeading3':
|
|
21
|
+
case 'ATXHeading4':
|
|
22
|
+
case 'ATXHeading5':
|
|
23
|
+
case 'ATXHeading6': {
|
|
24
|
+
const mark = node.node.getChild('HeaderMark');
|
|
25
|
+
if (mark) {
|
|
26
|
+
if (view.state.readOnly || cursor < node.from || cursor > node.to) {
|
|
27
|
+
builder.add(mark.from, mark.to + 1, Decoration.replace({}));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
from,
|
|
34
|
+
to,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return builder.finish();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const heading = () => {
|
|
42
|
+
return [
|
|
43
|
+
ViewPlugin.fromClass(
|
|
44
|
+
class {
|
|
45
|
+
decorations: DecorationSet;
|
|
46
|
+
constructor(view: EditorView) {
|
|
47
|
+
this.decorations = buildDecorations(view);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
update(update: ViewUpdate) {
|
|
51
|
+
this.decorations = buildDecorations(update.view);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
decorations: (value) => value.decorations,
|
|
56
|
+
},
|
|
57
|
+
),
|
|
58
|
+
];
|
|
59
|
+
};
|
|
@@ -6,20 +6,19 @@ import { markdownLanguage } from '@codemirror/lang-markdown';
|
|
|
6
6
|
import { HighlightStyle } from '@codemirror/language';
|
|
7
7
|
import { tags, styleTags, Tag } from '@lezer/highlight';
|
|
8
8
|
import { type MarkdownConfig, Table } from '@lezer/markdown';
|
|
9
|
-
import get from 'lodash.get';
|
|
10
9
|
|
|
11
10
|
import {
|
|
12
11
|
blockquote,
|
|
13
12
|
bold,
|
|
14
13
|
code,
|
|
15
14
|
codeMark,
|
|
15
|
+
getToken,
|
|
16
16
|
heading,
|
|
17
17
|
inlineUrl,
|
|
18
18
|
italic,
|
|
19
19
|
mark,
|
|
20
20
|
strikethrough,
|
|
21
|
-
|
|
22
|
-
} from '../../../../styles';
|
|
21
|
+
} from '../../styles';
|
|
23
22
|
|
|
24
23
|
/**
|
|
25
24
|
* Custom tags defined and processed by the GFM lezer extension.
|
|
@@ -32,7 +31,6 @@ export const markdownTags = {
|
|
|
32
31
|
CodeText: Tag.define(),
|
|
33
32
|
EmphasisMark: Tag.define(),
|
|
34
33
|
HeaderMark: Tag.define(),
|
|
35
|
-
// HorizontalRule: Tag.define(),
|
|
36
34
|
InlineCode: Tag.define(),
|
|
37
35
|
LinkLabel: Tag.define(),
|
|
38
36
|
LinkReference: Tag.define(),
|
|
@@ -130,13 +128,13 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
|
|
|
130
128
|
// Markdown marks.
|
|
131
129
|
{
|
|
132
130
|
tag: [markdownTags.CodeMark, markdownTags.HeaderMark, markdownTags.QuoteMark, markdownTags.EmphasisMark],
|
|
133
|
-
class:
|
|
131
|
+
class: mark,
|
|
134
132
|
},
|
|
135
133
|
|
|
136
134
|
// E.g., code block language (after ```).
|
|
137
135
|
{
|
|
138
136
|
tag: [tags.function(tags.variableName), tags.labelName],
|
|
139
|
-
class:
|
|
137
|
+
class: codeMark,
|
|
140
138
|
},
|
|
141
139
|
|
|
142
140
|
{
|
|
@@ -145,12 +143,12 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
|
|
|
145
143
|
},
|
|
146
144
|
|
|
147
145
|
// Headings.
|
|
148
|
-
{ tag: tags.heading1, class: heading(1
|
|
149
|
-
{ tag: tags.heading2, class: heading(2
|
|
150
|
-
{ tag: tags.heading3, class: heading(3
|
|
151
|
-
{ tag: tags.heading4, class: heading(4
|
|
152
|
-
{ tag: tags.heading5, class: heading(5
|
|
153
|
-
{ tag: tags.heading6, class: heading(6
|
|
146
|
+
{ tag: tags.heading1, class: heading(1) },
|
|
147
|
+
{ tag: tags.heading2, class: heading(2) },
|
|
148
|
+
{ tag: tags.heading3, class: heading(3) },
|
|
149
|
+
{ tag: tags.heading4, class: heading(4) },
|
|
150
|
+
{ tag: tags.heading5, class: heading(5) },
|
|
151
|
+
{ tag: tags.heading6, class: heading(6) },
|
|
154
152
|
|
|
155
153
|
// Emphasis.
|
|
156
154
|
{ tag: tags.emphasis, class: italic },
|
|
@@ -167,7 +165,7 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
|
|
|
167
165
|
// However, since `codeLanguages` is also defined, the `lezer` will not parse fenced code blocks,
|
|
168
166
|
// when a language is specified. In this case, the syntax highlighting extensions will colorize
|
|
169
167
|
// the code, but all other CSS properties will be inherited.
|
|
170
|
-
// IMPORTANT: Therefore, the fenced code block will use the base editor font.
|
|
168
|
+
// IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
|
|
171
169
|
{
|
|
172
170
|
tag: [markdownTags.CodeText, markdownTags.InlineCode],
|
|
173
171
|
class: code,
|
|
@@ -178,27 +176,14 @@ export const markdownHighlightStyle = (readonly?: boolean) => {
|
|
|
178
176
|
class: blockquote,
|
|
179
177
|
},
|
|
180
178
|
|
|
181
|
-
// TODO(burdon): Replace with extension.
|
|
182
|
-
// {
|
|
183
|
-
// tag: [markdownTags.HorizontalRule],
|
|
184
|
-
// class: mx(horizontalRule, readonly && '-indent-[100rem]'),
|
|
185
|
-
// },
|
|
186
|
-
|
|
187
|
-
// TODO(burdon): Only if being edited.
|
|
188
179
|
{
|
|
189
180
|
tag: [markdownTags.TableCell],
|
|
190
181
|
class: 'font-mono',
|
|
191
182
|
},
|
|
192
|
-
|
|
193
|
-
// Main content, paragraphs, etc.
|
|
194
|
-
// {
|
|
195
|
-
// tag: [tags.content],
|
|
196
|
-
// fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
197
|
-
// },
|
|
198
183
|
],
|
|
199
184
|
{
|
|
200
185
|
scope: markdownLanguage,
|
|
201
|
-
all: { fontFamily:
|
|
186
|
+
all: { fontFamily: getToken('fontFamily.body', []).join(',') },
|
|
202
187
|
},
|
|
203
188
|
);
|
|
204
189
|
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { syntaxTree } from '@codemirror/language';
|
|
6
|
+
import { RangeSetBuilder } from '@codemirror/state';
|
|
7
|
+
import { Decoration, type DecorationSet, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from '@codemirror/view';
|
|
8
|
+
|
|
9
|
+
import { getToken } from '../../styles';
|
|
10
|
+
|
|
11
|
+
// TODO(burdon): Reconcile with theme.
|
|
12
|
+
const styles = EditorView.baseTheme({
|
|
13
|
+
'& .cm-hr': {
|
|
14
|
+
// Note that block-level decorations should not have vertical margins,
|
|
15
|
+
borderTop: `1px solid ${getToken('extend.colors.neutral.200')}`,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
class HorizontalRuleWidget extends WidgetType {
|
|
20
|
+
override toDOM() {
|
|
21
|
+
const el = document.createElement('div');
|
|
22
|
+
el.className = 'cm-hr';
|
|
23
|
+
return el;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const decoration = Decoration.replace({ widget: new HorizontalRuleWidget() });
|
|
28
|
+
|
|
29
|
+
const buildDecorations = (view: EditorView): DecorationSet => {
|
|
30
|
+
const builder = new RangeSetBuilder<Decoration>();
|
|
31
|
+
const { state } = view;
|
|
32
|
+
const cursor = state.selection.main.head;
|
|
33
|
+
|
|
34
|
+
for (const { from, to } of view.visibleRanges) {
|
|
35
|
+
syntaxTree(state).iterate({
|
|
36
|
+
enter: (node) => {
|
|
37
|
+
if (node.name === 'HorizontalRule') {
|
|
38
|
+
// Check if cursor is inside text.
|
|
39
|
+
if (cursor <= node.from || cursor >= node.to) {
|
|
40
|
+
builder.add(node.from, node.to, decoration);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
from,
|
|
45
|
+
to,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return builder.finish();
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const hr = () => {
|
|
53
|
+
return [
|
|
54
|
+
ViewPlugin.fromClass(
|
|
55
|
+
class {
|
|
56
|
+
decorations: DecorationSet;
|
|
57
|
+
constructor(view: EditorView) {
|
|
58
|
+
this.decorations = buildDecorations(view);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
update(update: ViewUpdate) {
|
|
62
|
+
this.decorations = buildDecorations(update.view);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
decorations: (value) => value.decorations,
|
|
67
|
+
},
|
|
68
|
+
),
|
|
69
|
+
styles,
|
|
70
|
+
];
|
|
71
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { syntaxTree } from '@codemirror/language';
|
|
6
|
+
import { type EditorState, type Extension, StateField, type Transaction, type Range } from '@codemirror/state';
|
|
7
|
+
import { Decoration, type DecorationSet, EditorView, WidgetType } from '@codemirror/view';
|
|
8
|
+
|
|
9
|
+
export type ImageOptions = {};
|
|
10
|
+
|
|
11
|
+
export const image = (options: ImageOptions = {}): Extension => {
|
|
12
|
+
return StateField.define<DecorationSet>({
|
|
13
|
+
create: (state) => {
|
|
14
|
+
return Decoration.set(buildDecorations(0, state.doc.length, state));
|
|
15
|
+
},
|
|
16
|
+
update: (value: DecorationSet, tr: Transaction) => {
|
|
17
|
+
if (!tr.docChanged && !tr.selection) {
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
// Find range of changes and cursor changes.
|
|
21
|
+
const cursor = tr.state.selection.main.head;
|
|
22
|
+
const oldCursor = tr.changes.mapPos(tr.startState.selection.main.head);
|
|
23
|
+
let from = Math.min(cursor, oldCursor);
|
|
24
|
+
let to = Math.max(cursor, oldCursor);
|
|
25
|
+
tr.changes.iterChangedRanges((fromA, toA, fromB, toB) => {
|
|
26
|
+
from = Math.min(from, fromB);
|
|
27
|
+
to = Math.max(to, toB);
|
|
28
|
+
});
|
|
29
|
+
// Expand to cover lines
|
|
30
|
+
from = tr.state.doc.lineAt(from).from;
|
|
31
|
+
to = tr.state.doc.lineAt(to).to;
|
|
32
|
+
return value.map(tr.changes).update({
|
|
33
|
+
filterFrom: from,
|
|
34
|
+
filterTo: to,
|
|
35
|
+
filter: () => false,
|
|
36
|
+
add: buildDecorations(from, to, tr.state),
|
|
37
|
+
});
|
|
38
|
+
},
|
|
39
|
+
provide: (field) => EditorView.decorations.from(field),
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const buildDecorations = (from: number, to: number, state: EditorState) => {
|
|
44
|
+
const decorations: Range<Decoration>[] = [];
|
|
45
|
+
const cursor = state.selection.main.head;
|
|
46
|
+
|
|
47
|
+
syntaxTree(state).iterate({
|
|
48
|
+
enter: (node) => {
|
|
49
|
+
if (node.name === 'Image') {
|
|
50
|
+
const urlNode = node.node.getChild('URL');
|
|
51
|
+
if (urlNode) {
|
|
52
|
+
const hide = state.readOnly || cursor < node.from || cursor > node.to;
|
|
53
|
+
const url = state.sliceDoc(urlNode.from, urlNode.to);
|
|
54
|
+
decorations.push(
|
|
55
|
+
Decoration.replace({
|
|
56
|
+
block: true, // Prevent cursor from entering.
|
|
57
|
+
widget: new ImageWidget(url),
|
|
58
|
+
}).range(hide ? node.from : node.to, node.to),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
from,
|
|
64
|
+
to,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
return decorations;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
class ImageWidget extends WidgetType {
|
|
71
|
+
constructor(readonly _url: string) {
|
|
72
|
+
super();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
override eq(other: this) {
|
|
76
|
+
return this._url === (other as any as ImageWidget)._url;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
override toDOM(view: EditorView) {
|
|
80
|
+
const img = document.createElement('img');
|
|
81
|
+
img.setAttribute('src', this._url);
|
|
82
|
+
img.setAttribute('class', 'cm-image');
|
|
83
|
+
// Images are hidden until successfully loaded to avoid flickering effects.
|
|
84
|
+
img.onload = () => img.classList.add('cm-loaded-image');
|
|
85
|
+
return img;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export * from './bundle';
|
|
6
|
+
export * from './code';
|
|
7
|
+
export * from './heading';
|
|
8
|
+
export * from './highlight';
|
|
9
|
+
export * from './hr';
|
|
10
|
+
export * from './image';
|
|
11
|
+
export * from './link';
|
|
12
|
+
export * from './table';
|
|
13
|
+
export * from './tasklist';
|