@dxos/react-ui-editor 0.3.9-main.4e39f3a → 0.3.9-main.501fe30
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 +284 -48
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts +2 -2
- package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts +2 -2
- package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-plugin/handle.d.ts +9 -0
- package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +1 -0
- package/dist/types/src/automerge/automerge-plugin/index.d.ts +2 -2
- package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge-plugin/plugin.d.ts +6 -5
- package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +1 -1
- package/dist/types/src/automerge/automerge.stories.d.ts +1 -0
- package/dist/types/src/automerge/automerge.stories.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.d.ts +0 -1
- package/dist/types/src/components/Editor/Editor.d.ts.map +1 -1
- package/dist/types/src/components/Editor/Editor.stories.d.ts +3 -8
- package/dist/types/src/components/Editor/Editor.stories.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.d.ts +4 -2
- package/dist/types/src/components/Markdown/Markdown.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts +1 -1
- package/dist/types/src/components/Markdown/Markdown.stories.d.ts.map +1 -1
- package/dist/types/src/components/Markdown/markdownTheme.d.ts +10 -17
- package/dist/types/src/components/Markdown/markdownTheme.d.ts.map +1 -1
- package/dist/types/src/components/RichText/RichText.stories.d.ts +2 -3
- package/dist/types/src/components/RichText/RichText.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -0
- package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/extensions/hyperlinkDecoration.d.ts +11 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkDecoration.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts +8 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkTooltip.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkWidget.d.ts +10 -0
- package/dist/types/src/components/TextEditor/extensions/hyperlinkWidget.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts +4 -0
- package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts +1 -0
- package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
- package/dist/types/src/components/TextEditor/theme.d.ts +1 -0
- package/dist/types/src/components/TextEditor/theme.d.ts.map +1 -1
- package/dist/types/stories/html/code-mirror.stories.d.ts.map +1 -1
- package/package.json +28 -26
- package/src/automerge/automerge-plugin/PatchSemaphore.ts +5 -5
- package/src/automerge/automerge-plugin/codeMirrorToAm.ts +10 -7
- package/src/automerge/automerge-plugin/handle.ts +14 -0
- package/src/automerge/automerge-plugin/index.ts +2 -2
- package/src/automerge/automerge-plugin/plugin.ts +39 -10
- package/src/automerge/automerge.stories.tsx +31 -46
- package/src/components/Editor/Editor.stories.tsx +5 -5
- package/src/components/Editor/Editor.tsx +4 -1
- package/src/components/Markdown/Markdown.stories.tsx +38 -25
- package/src/components/Markdown/Markdown.tsx +53 -24
- package/src/components/Markdown/markdownTheme.ts +32 -17
- package/src/components/RichText/RichText.stories.tsx +37 -33
- package/src/components/TextEditor/TextEditor.stories.tsx +47 -6
- package/src/components/TextEditor/TextEditor.tsx +1 -0
- package/src/components/TextEditor/extensions/hyperlinkDecoration.ts +99 -0
- package/src/components/TextEditor/extensions/hyperlinkTooltip.tsx +61 -0
- package/src/components/TextEditor/extensions/hyperlinkWidget.tsx +119 -0
- package/src/components/TextEditor/extensions/index.ts +7 -0
- package/src/components/TextEditor/index.ts +2 -0
- package/src/components/TextEditor/theme.ts +10 -4
- package/dist/types/src/automerge/demo.d.ts +0 -25
- package/dist/types/src/automerge/demo.d.ts.map +0 -1
- package/src/automerge/demo.ts +0 -106
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
// Copyright CodeMirror
|
|
4
|
+
//
|
|
5
|
+
|
|
6
|
+
import { syntaxTree } from '@codemirror/language';
|
|
7
|
+
import { type EditorState, type RangeSet, RangeSetBuilder, StateField, type Transaction } from '@codemirror/state';
|
|
8
|
+
import { Decoration, EditorView, WidgetType } from '@codemirror/view';
|
|
9
|
+
|
|
10
|
+
// Adapted from:
|
|
11
|
+
// https://codemirror.net/try/?c=aW1wb3J0IHsgYmFzaWNTZXR1cCwgRWRpdG9yVmlldyB9IGZyb20gImNvZGVtaXJyb3IiCmltcG9ydCB7IG1hcmtkb3duIH0gZnJvbSAiQGNvZGVtaXJyb3IvbGFuZy1tYXJrZG93biIKaW1wb3J0IHsgc3ludGF4VHJlZSB9IGZyb20gIkBjb2RlbWlycm9yL2xhbmd1YWdlIjsKaW1wb3J0IHsgRWRpdG9yU3RhdGUsIFJhbmdlU2V0QnVpbGRlciwgU3RhdGVGaWVsZCB9IGZyb20gIkBjb2RlbWlycm9yL3N0YXRlIjsKaW1wb3J0IHsgRGVjb3JhdGlvbiwgV2lkZ2V0VHlwZSB9IGZyb20gIkBjb2RlbWlycm9yL3ZpZXciOwoKY2xhc3MgTGluayBleHRlbmRzIFdpZGdldFR5cGUgewogIGNvbnN0cnVjdG9yKHRleHQsIHVybCkgewogICAgc3VwZXIoKQogICAgdGhpcy50ZXh0ID0gdGV4dAogICAgdGhpcy51cmwgPSB1cmwKICB9CiAgZXEob3RoZXIpIHsgcmV0dXJuIHRoaXMudGV4dCA9PSBvdGhlci50ZXh0ICYmIHRoaXMudXJsID09IG90aGVyLnVybCB9CiAgdG9ET00oKSB7CiAgICBsZXQgbGluayA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoImEiKQogICAgbGluay50ZXh0Q29udGVudCA9IHRoaXMudGV4dAogICAgbGluay5ocmVmID0gdGhpcy51cmwKICAgIHJldHVybiBsaW5rOwogIH0KfQoKbGV0IGRlY29yYXRpb25zRmllbGQgPSBTdGF0ZUZpZWxkLmRlZmluZSh7CiAgY3JlYXRlKCkgewogICAgcmV0dXJuIERlY29yYXRpb24ubm9uZTsKICB9LAogIHVwZGF0ZShfLCB0cikgewogICAgY29uc3QgYnVpbGRlciA9IG5ldyBSYW5nZVNldEJ1aWxkZXIoKTsKICAgIGxldCBjdXJzb3IgPSB0ci5zdGF0ZS5zZWxlY3Rpb24ubWFpbi5oZWFkOwogICAgc3ludGF4VHJlZSh0ci5zdGF0ZSkuaXRlcmF0ZSh7CiAgICAgIGVudGVyOiAobm9kZSkgPT4gewogICAgICAgIGlmICgoY3Vyc29yIDwgbm9kZS5mcm9tIHx8IGN1cnNvciA+IG5vZGUudG8pICYmIG5vZGUubmFtZSA9PSAiTGluayIpIHsKICAgICAgICAgIGxldCBtYXJrcyA9IG5vZGUubm9kZS5nZXRDaGlsZHJlbigiTGlua01hcmsiKTsKICAgICAgICAgIGxldCB0ZXh0ID0gbWFya3MubGVuZ3RoID49IDIgPyB0ci5zdGF0ZS5zbGljZURvYyhtYXJrc1swXS50bywgbWFya3NbMV0uZnJvbSkgOiAiIgoKICAgICAgICAgIGxldCB1cmxOb2RlID0gbm9kZS5ub2RlLmdldENoaWxkKCJVUkwiKTsKICAgICAgICAgIGxldCB1cmwgPSB1cmxOb2RlID8gdHIuc3RhdGUuc2xpY2VEb2ModXJsTm9kZS5mcm9tLCB1cmxOb2RlLnRvKSA6ICIiCiAgICAgICAgICBidWlsZGVyLmFkZChub2RlLmZyb20sIG5vZGUudG8sIERlY29yYXRpb24ucmVwbGFjZSh7CiAgICAgICAgICAgIHdpZGdldDogbmV3IExpbmsodGV4dCwgdXJsKSwKICAgICAgICAgIH0pLAogICAgICAgICAgICAgICAgICAgICApOwogICAgICAgICAgcmV0dXJuIGZhbHNlOwogICAgICAgIH0KICAgICAgICByZXR1cm4gdHJ1ZTsKICAgICAgfSwKICAgIH0pOwogICAgcmV0dXJuIGJ1aWxkZXIuZmluaXNoKCk7CiAgfSwKICBwcm92aWRlOiAoZikgPT4gRWRpdG9yVmlldy5kZWNvcmF0aW9ucy5mcm9tKGYpLAp9KTsKCmxldCB2aWV3ID0gbmV3IEVkaXRvclZpZXcoewogIGRvYzogIiMgSGVsbG8gV29ybGRcblxuW1Rlc3RdKGh0dHBzOi8vZXhhbXBsZS5jb20pXG5cblxuIiwKICBleHRlbnNpb25zOiBbZGVjb3JhdGlvbnNGaWVsZCwgbWFya2Rvd24oKV0sCiAgcGFyZW50OiBkb2N1bWVudC5ib2R5LAp9KTsKCnZpZXcuZm9jdXMoKTs=
|
|
12
|
+
// https://discuss.codemirror.net/t/link-widget-is-clicked-only-if-editor-is-out-of-focus/7433
|
|
13
|
+
|
|
14
|
+
// TODO(burdon): Adapt to make all markdown editable?
|
|
15
|
+
|
|
16
|
+
// TODO(burdon): Research overlays.
|
|
17
|
+
// https://stackoverflow.com/questions/26933344/codemirror-detect-and-create-links-inside-editor
|
|
18
|
+
// https://codemirror.net/5/doc/manual.html#addOverlay
|
|
19
|
+
// https://codemirror.net/5/demo/simplemode.html
|
|
20
|
+
// https://codemirror.net/5/demo/search.html (Search dialog).
|
|
21
|
+
|
|
22
|
+
class Link extends WidgetType {
|
|
23
|
+
constructor(private readonly _anchor: number, private readonly _text: string, private readonly _url?: string) {
|
|
24
|
+
super();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override eq(other: any) {
|
|
28
|
+
return this._text === other.text && this._url === other.url;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
toDOM(view: EditorView) {
|
|
32
|
+
const link = document.createElement('a');
|
|
33
|
+
link.setAttribute('class', 'cm-link');
|
|
34
|
+
link.textContent = this._text;
|
|
35
|
+
if (this._url) {
|
|
36
|
+
link.setAttribute('target', '_blank');
|
|
37
|
+
link.href = this._url;
|
|
38
|
+
} else {
|
|
39
|
+
link.onclick = () => {
|
|
40
|
+
view.dispatch({
|
|
41
|
+
selection: {
|
|
42
|
+
anchor: this._anchor,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return link;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type HyperlinkDecorationConfig = {
|
|
53
|
+
link?: boolean;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Creates a state field to replace AST elements with a hyperlink widget.
|
|
58
|
+
* https://codemirror.net/docs/ref/#state.StateField
|
|
59
|
+
*/
|
|
60
|
+
export const hyperlinkDecoration = ({ link }: HyperlinkDecorationConfig = {}) => {
|
|
61
|
+
return StateField.define<RangeSet<any>>({
|
|
62
|
+
create: (state) => update(state, link),
|
|
63
|
+
update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, link),
|
|
64
|
+
provide: (field) => EditorView.decorations.from(field),
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const update = (state: EditorState, link?: boolean) => {
|
|
69
|
+
const builder = new RangeSetBuilder();
|
|
70
|
+
const cursor = state.selection.main.head;
|
|
71
|
+
syntaxTree(state).iterate({
|
|
72
|
+
enter: (node) => {
|
|
73
|
+
// Check if cursor is inside text.
|
|
74
|
+
if (cursor < node.from || cursor > node.to) {
|
|
75
|
+
if (node.name === 'Link') {
|
|
76
|
+
const marks = node.node.getChildren('LinkMark');
|
|
77
|
+
const text = marks.length >= 2 ? state.sliceDoc(marks[0].to, marks[1].from) : '';
|
|
78
|
+
|
|
79
|
+
const urlNode = node.node.getChild('URL');
|
|
80
|
+
const url = urlNode ? state.sliceDoc(urlNode.from, urlNode.to) : '';
|
|
81
|
+
|
|
82
|
+
builder.add(
|
|
83
|
+
node.from,
|
|
84
|
+
node.to,
|
|
85
|
+
Decoration.replace({
|
|
86
|
+
widget: new Link(node.from, text, link ? url : undefined),
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return true;
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
return builder.finish();
|
|
99
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { hoverTooltip } from '@codemirror/view';
|
|
6
|
+
|
|
7
|
+
import { tooltipContent } from '@dxos/react-ui-theme';
|
|
8
|
+
|
|
9
|
+
const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)/;
|
|
10
|
+
|
|
11
|
+
export type OnHyperlinkHover = (el: Element, url: string) => void;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* https://codemirror.net/examples/tooltip
|
|
15
|
+
* https://codemirror.net/docs/ref/#view.hoverTooltip
|
|
16
|
+
* https://github.com/codemirror/view/blob/main/src/tooltip.ts
|
|
17
|
+
*/
|
|
18
|
+
// TODO(burdon): Create config object.
|
|
19
|
+
export const createHyperlinkTooltip = (onHover: OnHyperlinkHover, regexp = markdownLinkRegexp) =>
|
|
20
|
+
hoverTooltip((view, pos) => {
|
|
21
|
+
const { from, text } = view.state.doc.lineAt(pos);
|
|
22
|
+
const p = pos - from;
|
|
23
|
+
|
|
24
|
+
let idx = 0;
|
|
25
|
+
let match;
|
|
26
|
+
do {
|
|
27
|
+
match = text.substring(idx).match(regexp);
|
|
28
|
+
if (!match) {
|
|
29
|
+
match = undefined;
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
idx = text.indexOf(match[0]);
|
|
34
|
+
if (p >= idx && p < idx + match[0].length) {
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
idx += match[0].length;
|
|
39
|
+
} while (true);
|
|
40
|
+
|
|
41
|
+
if (!match) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const [, , url] = match ?? [];
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
pos: idx + from,
|
|
49
|
+
end: idx + from + match[0].length,
|
|
50
|
+
above: true,
|
|
51
|
+
create: () => {
|
|
52
|
+
const el = document.createElement('a');
|
|
53
|
+
el.innerText = '_'; // Required so doesn't collapse.
|
|
54
|
+
el.className = tooltipContent({}, 'mb-2 p-1');
|
|
55
|
+
el.setAttribute('target', '_blank');
|
|
56
|
+
el.setAttribute('href', url);
|
|
57
|
+
onHover(el, url);
|
|
58
|
+
return { dom: el };
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
// Adapted from: https://github.com/uiwjs/react-codemirror/blob/master/extensions/hyper-link/src/index.ts
|
|
4
|
+
//
|
|
5
|
+
|
|
6
|
+
import { type Extension } from '@codemirror/state';
|
|
7
|
+
import {
|
|
8
|
+
Decoration,
|
|
9
|
+
type DecorationSet,
|
|
10
|
+
EditorView,
|
|
11
|
+
MatchDecorator,
|
|
12
|
+
ViewPlugin,
|
|
13
|
+
type ViewUpdate,
|
|
14
|
+
} from '@codemirror/view';
|
|
15
|
+
|
|
16
|
+
// Notes: Can't edit widget content (or cursor nav through them).
|
|
17
|
+
// - https://discuss.codemirror.net/t/focusing-inputs-within-widgets/5178/7
|
|
18
|
+
// Widgets intentionally always get set to contenteditable=false,
|
|
19
|
+
// or they would become part of CodeMirror’s editable content element.
|
|
20
|
+
// You should be able to introduce new contenteditable=true child elements inside of them.
|
|
21
|
+
|
|
22
|
+
const markdownLinkRegexp = /\[([^\]]+)]\(([^)]+)\)(!?)/gi;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Custom link decorator.
|
|
26
|
+
* NOTE: The default markdown extension formats links by default.
|
|
27
|
+
* @deprecated
|
|
28
|
+
*/
|
|
29
|
+
const linkDecorator = () =>
|
|
30
|
+
new MatchDecorator({
|
|
31
|
+
regexp: markdownLinkRegexp,
|
|
32
|
+
decorate: (add, from, to, match, view) => {
|
|
33
|
+
const [_, label, url] = match;
|
|
34
|
+
const p0 = {
|
|
35
|
+
start: from,
|
|
36
|
+
end: from + 1,
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const p1 = {
|
|
40
|
+
start: from + 1,
|
|
41
|
+
end: from + 1 + label.length,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const p2 = {
|
|
45
|
+
start: p1.end,
|
|
46
|
+
end: p1.end + 1,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const p3 = {
|
|
50
|
+
start: p1.end + 1,
|
|
51
|
+
end: p1.end + 1 + url.length + 2,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
add(
|
|
55
|
+
p0.start,
|
|
56
|
+
p0.end,
|
|
57
|
+
Decoration.mark({ class: 'cm-hyperlink-bracket', _attributes: { style: 'display: none' } }),
|
|
58
|
+
);
|
|
59
|
+
add(p1.start, p1.end, Decoration.mark({ class: 'cm-hyperlink-label' }));
|
|
60
|
+
add(
|
|
61
|
+
p2.start,
|
|
62
|
+
p2.end,
|
|
63
|
+
Decoration.mark({ class: 'cm-hyperlink-bracket', _attributes: { style: 'display: none' } }),
|
|
64
|
+
);
|
|
65
|
+
add(p3.start, p3.end, Decoration.mark({ class: 'cm-hyperlink-url', _attributes: { style: 'display: none' } }));
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export function hyperLinkExtension() {
|
|
70
|
+
return ViewPlugin.fromClass(
|
|
71
|
+
class HyperLinkView {
|
|
72
|
+
readonly decorator: MatchDecorator;
|
|
73
|
+
decorations: DecorationSet;
|
|
74
|
+
constructor(view: EditorView) {
|
|
75
|
+
this.decorator = linkDecorator();
|
|
76
|
+
this.decorations = this.decorator.createDeco(view);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
update(update: ViewUpdate) {
|
|
80
|
+
if (update.docChanged || update.viewportChanged) {
|
|
81
|
+
this.decorations = this.decorator.updateDeco(update, this.decorations);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
decorations: (view) => view.decorations,
|
|
87
|
+
eventHandlers: {
|
|
88
|
+
// TODO(burdon): CLick to expand link.
|
|
89
|
+
// https://discuss.codemirror.net/t/decorator-iteration-on-click-event/4226/2
|
|
90
|
+
mousedown: function (event, view) {
|
|
91
|
+
const pos = view.posAtDOM(event.target as Node);
|
|
92
|
+
if (pos !== 0) {
|
|
93
|
+
console.log(this);
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
// TODO(burdon): Hide when cursor moves away.
|
|
97
|
+
keydown: (event, view) => {
|
|
98
|
+
view.update([]);
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export const hyperlinkStyle = EditorView.baseTheme({
|
|
106
|
+
'.cm-hyperlink-label': {
|
|
107
|
+
cursor: 'pointer',
|
|
108
|
+
textDecoration: 'underline',
|
|
109
|
+
},
|
|
110
|
+
'.cm-hyperlink-bracket': {
|
|
111
|
+
opacity: 0.2,
|
|
112
|
+
},
|
|
113
|
+
'.cm-hyperlink-url': {
|
|
114
|
+
opacity: 0.5,
|
|
115
|
+
fontFamily: 'monospace',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export const hyperlinkWidget: Extension = [hyperLinkExtension(), hyperlinkStyle];
|
|
@@ -10,6 +10,7 @@ import { tailwindConfig, type TailwindConfig } from '@dxos/react-ui-theme';
|
|
|
10
10
|
const tokens: TailwindConfig['theme'] = tailwindConfig({}).theme;
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
+
* Minimal styles.
|
|
13
14
|
* https://codemirror.net/examples/styling
|
|
14
15
|
*/
|
|
15
16
|
// TODO(burdon): If given a "theme" suffix, `__docgen` properties are added to the object.
|
|
@@ -19,11 +20,16 @@ export const defaultStyles: {
|
|
|
19
20
|
'&.cm-focused': {
|
|
20
21
|
outline: 'none',
|
|
21
22
|
},
|
|
22
|
-
'.cm-
|
|
23
|
-
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
24
|
-
},
|
|
25
|
-
'& .cm-scroller': {
|
|
23
|
+
'.cm-scroller': {
|
|
26
24
|
fontFamily: get(tokens, 'fontFamily.body', []).join(','),
|
|
27
25
|
overflow: 'visible',
|
|
28
26
|
},
|
|
27
|
+
'.cm-tooltip': {
|
|
28
|
+
backgroundColor: 'transparent',
|
|
29
|
+
border: 'none',
|
|
30
|
+
},
|
|
31
|
+
'.cm-link': {
|
|
32
|
+
color: get(tokens, 'extend.colors.primary.500'),
|
|
33
|
+
textDecoration: 'underline',
|
|
34
|
+
},
|
|
29
35
|
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { Event } from '@dxos/async';
|
|
2
|
-
import { type Doc, next as automerge, type ChangeOptions, type ChangeFn, type Heads } from '@dxos/automerge/automerge';
|
|
3
|
-
export declare class Peer {
|
|
4
|
-
changeEvent: Event<void>;
|
|
5
|
-
storage: Map<string, Uint8Array>;
|
|
6
|
-
stats: {
|
|
7
|
-
messagesSent: number;
|
|
8
|
-
messagesReceived: number;
|
|
9
|
-
bytesSent: number;
|
|
10
|
-
bytesReceived: number;
|
|
11
|
-
snapshotBytes: number;
|
|
12
|
-
incrementalBytes: number;
|
|
13
|
-
};
|
|
14
|
-
doc: Doc<{
|
|
15
|
-
text: string;
|
|
16
|
-
}>;
|
|
17
|
-
change(options: string | ChangeOptions<any> | ChangeFn<any>, callback?: ChangeFn<any>): void;
|
|
18
|
-
changeAt(scope: Heads, options: string | ChangeOptions<any> | ChangeFn<any>, callback?: ChangeFn<any>): automerge.Heads | null;
|
|
19
|
-
replicate(): {
|
|
20
|
-
readable: ReadableStream<Uint8Array>;
|
|
21
|
-
writable: WritableStream<Uint8Array>;
|
|
22
|
-
};
|
|
23
|
-
private _handleChange;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=demo.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../../../src/automerge/demo.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EACL,KAAK,GAAG,EACR,IAAI,IAAI,SAAS,EACjB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,KAAK,EAEX,MAAM,2BAA2B,CAAC;AAEnC,qBAAa,IAAI;IACf,WAAW,cAAe;IAE1B,OAAO,0BAAiC;IACxC,KAAK;;;;;;;MAOH;IAEF,GAAG,EAAG,GAAG,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE5B,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC;IAKrF,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC;IAOrG,SAAS,IAAI;QAAE,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAC;QAAC,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,CAAA;KAAE;IAgD3F,OAAO,CAAC,aAAa;CAgBtB"}
|
package/src/automerge/demo.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
//
|
|
2
|
-
// Copyright 2023 DXOS.org
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
import { Event } from '@dxos/async';
|
|
6
|
-
import {
|
|
7
|
-
type Doc,
|
|
8
|
-
next as automerge,
|
|
9
|
-
type ChangeOptions,
|
|
10
|
-
type ChangeFn,
|
|
11
|
-
type Heads,
|
|
12
|
-
type SyncState,
|
|
13
|
-
} from '@dxos/automerge/automerge';
|
|
14
|
-
|
|
15
|
-
export class Peer {
|
|
16
|
-
changeEvent = new Event();
|
|
17
|
-
|
|
18
|
-
storage = new Map<string, Uint8Array>();
|
|
19
|
-
stats = {
|
|
20
|
-
messagesSent: 0,
|
|
21
|
-
messagesReceived: 0,
|
|
22
|
-
bytesSent: 0,
|
|
23
|
-
bytesReceived: 0,
|
|
24
|
-
snapshotBytes: 0,
|
|
25
|
-
incrementalBytes: 0,
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
doc!: Doc<{ text: string }>;
|
|
29
|
-
|
|
30
|
-
change(options: string | ChangeOptions<any> | ChangeFn<any>, callback?: ChangeFn<any>) {
|
|
31
|
-
this.doc = automerge.change(this.doc, options, callback);
|
|
32
|
-
this._handleChange();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
changeAt(scope: Heads, options: string | ChangeOptions<any> | ChangeFn<any>, callback?: ChangeFn<any>) {
|
|
36
|
-
const { newDoc, newHeads } = automerge.changeAt(this.doc, scope, options, callback);
|
|
37
|
-
this.doc = newDoc;
|
|
38
|
-
this._handleChange();
|
|
39
|
-
return newHeads;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
replicate(): { readable: ReadableStream<Uint8Array>; writable: WritableStream<Uint8Array> } {
|
|
43
|
-
let syncState: SyncState = automerge.initSyncState();
|
|
44
|
-
|
|
45
|
-
let unsub: (() => void) | undefined;
|
|
46
|
-
const readable = new ReadableStream({
|
|
47
|
-
start: (controller) => {
|
|
48
|
-
unsub = this.changeEvent.on(() => {
|
|
49
|
-
const [newState, msg] = automerge.generateSyncMessage(this.doc, syncState);
|
|
50
|
-
syncState = newState;
|
|
51
|
-
|
|
52
|
-
if (msg) {
|
|
53
|
-
this.stats.messagesSent++;
|
|
54
|
-
this.stats.bytesSent += msg.byteLength;
|
|
55
|
-
controller.enqueue(msg);
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
},
|
|
59
|
-
pull: (controller) => {
|
|
60
|
-
const [newState, msg] = automerge.generateSyncMessage(this.doc, syncState);
|
|
61
|
-
syncState = newState;
|
|
62
|
-
|
|
63
|
-
if (msg) {
|
|
64
|
-
this.stats.messagesSent++;
|
|
65
|
-
this.stats.bytesSent += msg.byteLength;
|
|
66
|
-
controller.enqueue(msg);
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
cancel: () => {
|
|
70
|
-
unsub?.();
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const writable = new WritableStream({
|
|
75
|
-
write: (chunk) => {
|
|
76
|
-
this.stats.messagesReceived++;
|
|
77
|
-
this.stats.bytesReceived += chunk.byteLength;
|
|
78
|
-
|
|
79
|
-
const [newDoc, newState] = automerge.receiveSyncMessage(this.doc, syncState, chunk);
|
|
80
|
-
this.doc = newDoc;
|
|
81
|
-
syncState = newState;
|
|
82
|
-
|
|
83
|
-
this._handleChange();
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return { readable, writable };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private _handleChange() {
|
|
91
|
-
// TODO(dmaretskyi): Save since + debounce.
|
|
92
|
-
|
|
93
|
-
if (this.stats.incrementalBytes > 1024 && this.stats.incrementalBytes > this.stats.snapshotBytes) {
|
|
94
|
-
const snapshot = automerge.save(this.doc);
|
|
95
|
-
this.stats.snapshotBytes = snapshot.byteLength;
|
|
96
|
-
this.stats.incrementalBytes = 0;
|
|
97
|
-
console.log('snapshot save', snapshot.byteLength);
|
|
98
|
-
} else {
|
|
99
|
-
const chunk = automerge.saveIncremental(this.doc);
|
|
100
|
-
this.stats.incrementalBytes += chunk.length;
|
|
101
|
-
console.log('incremental save', chunk.length);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
this.changeEvent.emit();
|
|
105
|
-
}
|
|
106
|
-
}
|