@dxos/ui-editor 0.8.4-main.937b3ca → 0.8.4-main.9be5663bfe
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 +1487 -725
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +1487 -725
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/types/src/defaults.d.ts +3 -10
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/auto-scroll.d.ts +8 -0
- package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/extensions/factories.d.ts +2 -2
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +3 -2
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/action.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/bundle.d.ts +3 -0
- package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
- package/dist/types/src/extensions/outliner/outliner.d.ts.map +1 -1
- package/dist/types/src/extensions/preview/preview.d.ts +3 -1
- package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
- package/dist/types/src/extensions/scroll-past-end.d.ts +3 -0
- package/dist/types/src/extensions/scroll-past-end.d.ts.map +1 -0
- package/dist/types/src/extensions/scroller.d.ts +63 -0
- package/dist/types/src/extensions/scroller.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/extended-markdown.d.ts.map +1 -1
- package/dist/types/src/extensions/tags/fader.d.ts +12 -0
- package/dist/types/src/extensions/tags/fader.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/index.d.ts +2 -1
- package/dist/types/src/extensions/tags/index.d.ts.map +1 -1
- package/dist/types/src/extensions/tags/wire.d.ts +23 -0
- package/dist/types/src/extensions/tags/wire.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/wire.test.d.ts +2 -0
- package/dist/types/src/extensions/tags/wire.test.d.ts.map +1 -0
- package/dist/types/src/extensions/tags/xml-tags.d.ts +36 -9
- package/dist/types/src/extensions/tags/xml-tags.d.ts.map +1 -1
- package/dist/types/src/styles/index.d.ts +0 -2
- package/dist/types/src/styles/index.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts +15 -0
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/util/cursor.d.ts +1 -1
- package/dist/types/src/util/cursor.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +32 -32
- package/src/defaults.ts +25 -21
- package/src/extensions/annotations.ts +1 -1
- package/src/extensions/auto-scroll.ts +179 -0
- package/src/extensions/automerge/automerge.test.tsx +2 -2
- package/src/extensions/automerge/automerge.ts +6 -6
- package/src/extensions/blocks.ts +5 -5
- package/src/extensions/comments.ts +5 -6
- package/src/extensions/dnd.ts +2 -2
- package/src/extensions/factories.ts +8 -10
- package/src/extensions/folding.ts +5 -22
- package/src/extensions/index.ts +3 -2
- package/src/extensions/markdown/action.ts +0 -1
- package/src/extensions/markdown/bundle.ts +23 -9
- package/src/extensions/markdown/decorate.ts +15 -12
- package/src/extensions/markdown/highlight.ts +15 -7
- package/src/extensions/markdown/link.ts +27 -33
- package/src/extensions/markdown/parser.test.ts +0 -1
- package/src/extensions/markdown/styles.ts +42 -9
- package/src/extensions/markdown/table.ts +24 -2
- package/src/extensions/outliner/outliner.test.ts +0 -1
- package/src/extensions/outliner/outliner.ts +3 -4
- package/src/extensions/outliner/tree.test.ts +0 -1
- package/src/extensions/preview/preview.ts +62 -15
- package/src/extensions/scroll-past-end.ts +32 -0
- package/src/extensions/scroller.ts +245 -0
- package/src/extensions/selection.ts +1 -1
- package/src/extensions/tags/extended-markdown.test.ts +120 -2
- package/src/extensions/tags/extended-markdown.ts +80 -1
- package/src/extensions/tags/fader.ts +195 -0
- package/src/extensions/tags/index.ts +2 -1
- package/src/extensions/tags/wire.test.ts +65 -0
- package/src/extensions/tags/wire.ts +459 -0
- package/src/extensions/tags/xml-tags.ts +211 -34
- package/src/extensions/tags/xml-util.test.ts +111 -23
- package/src/styles/index.ts +0 -2
- package/src/styles/theme.ts +116 -34
- package/src/util/cursor.ts +1 -2
- package/dist/types/src/extensions/autoscroll.d.ts +0 -20
- package/dist/types/src/extensions/autoscroll.d.ts.map +0 -1
- package/dist/types/src/extensions/scrolling.d.ts +0 -78
- package/dist/types/src/extensions/scrolling.d.ts.map +0 -1
- package/dist/types/src/extensions/tags/streamer.d.ts +0 -12
- package/dist/types/src/extensions/tags/streamer.d.ts.map +0 -1
- package/dist/types/src/styles/markdown.d.ts +0 -8
- package/dist/types/src/styles/markdown.d.ts.map +0 -1
- package/dist/types/src/styles/tokens.d.ts +0 -3
- package/dist/types/src/styles/tokens.d.ts.map +0 -1
- package/src/extensions/autoscroll.ts +0 -165
- package/src/extensions/scrolling.ts +0 -189
- package/src/extensions/tags/streamer.ts +0 -243
- package/src/styles/markdown.ts +0 -26
- package/src/styles/tokens.ts +0 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/ui-editor",
|
|
3
|
-
"version": "0.8.4-main.
|
|
3
|
+
"version": "0.8.4-main.9be5663bfe",
|
|
4
4
|
"description": "Text editor components.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -58,35 +58,35 @@
|
|
|
58
58
|
"@replit/codemirror-vim": "^6.2.1",
|
|
59
59
|
"@replit/codemirror-vscode-keymap": "^6.0.2",
|
|
60
60
|
"@uiw/codemirror-theme-vscode": "^4.25.2",
|
|
61
|
-
"ajv": "^8.
|
|
61
|
+
"ajv": "^8.18.0",
|
|
62
62
|
"codemirror": "^6.0.1",
|
|
63
63
|
"lib0": "^0.2.65",
|
|
64
64
|
"lodash.defaultsdeep": "^4.6.1",
|
|
65
65
|
"lodash.merge": "^4.6.2",
|
|
66
66
|
"lodash.sortby": "^4.7.0",
|
|
67
67
|
"style-mod": "^4.1.0",
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/display-name": "0.8.4-main.
|
|
73
|
-
"@dxos/echo": "0.8.4-main.
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/lit-ui": "0.8.4-main.
|
|
78
|
-
"@dxos/
|
|
79
|
-
"@dxos/ui": "0.8.4-main.
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/util": "0.8.4-main.
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/
|
|
68
|
+
"@dxos/context": "0.8.4-main.9be5663bfe",
|
|
69
|
+
"@dxos/app-graph": "0.8.4-main.9be5663bfe",
|
|
70
|
+
"@dxos/debug": "0.8.4-main.9be5663bfe",
|
|
71
|
+
"@dxos/client": "0.8.4-main.9be5663bfe",
|
|
72
|
+
"@dxos/display-name": "0.8.4-main.9be5663bfe",
|
|
73
|
+
"@dxos/echo": "0.8.4-main.9be5663bfe",
|
|
74
|
+
"@dxos/echo-db": "0.8.4-main.9be5663bfe",
|
|
75
|
+
"@dxos/invariant": "0.8.4-main.9be5663bfe",
|
|
76
|
+
"@dxos/async": "0.8.4-main.9be5663bfe",
|
|
77
|
+
"@dxos/lit-ui": "0.8.4-main.9be5663bfe",
|
|
78
|
+
"@dxos/ui": "0.8.4-main.9be5663bfe",
|
|
79
|
+
"@dxos/ui-theme": "0.8.4-main.9be5663bfe",
|
|
80
|
+
"@dxos/protocols": "0.8.4-main.9be5663bfe",
|
|
81
|
+
"@dxos/util": "0.8.4-main.9be5663bfe",
|
|
82
|
+
"@dxos/log": "0.8.4-main.9be5663bfe",
|
|
83
|
+
"@dxos/ui-types": "0.8.4-main.9be5663bfe"
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@automerge/automerge": "3.2.3",
|
|
87
87
|
"@automerge/automerge-repo": "2.5.1",
|
|
88
88
|
"@automerge/automerge-repo-network-broadcastchannel": "2.5.1",
|
|
89
|
-
"@effect/platform": "0.
|
|
89
|
+
"@effect/platform": "0.94.4",
|
|
90
90
|
"@types/chai": "^4.2.15",
|
|
91
91
|
"@types/chai-dom": "^1.11.0",
|
|
92
92
|
"@types/lodash.defaultsdeep": "^4.6.6",
|
|
@@ -94,25 +94,25 @@
|
|
|
94
94
|
"@types/lodash.sortby": "^4.7.7",
|
|
95
95
|
"chai": "^4.4.1",
|
|
96
96
|
"chai-dom": "^1.11.0",
|
|
97
|
-
"effect": "3.
|
|
98
|
-
"happy-dom": "^
|
|
97
|
+
"effect": "3.20.0",
|
|
98
|
+
"happy-dom": "^20.0.0",
|
|
99
99
|
"jsdom": "^27.0.0",
|
|
100
100
|
"mocha": "^10.6.0",
|
|
101
|
-
"vite": "7.1.
|
|
101
|
+
"vite": "^7.1.11",
|
|
102
102
|
"vite-plugin-top-level-await": "^1.6.0",
|
|
103
103
|
"vite-plugin-wasm": "^3.5.0",
|
|
104
|
-
"@dxos/
|
|
105
|
-
"@dxos/
|
|
106
|
-
"@dxos/
|
|
107
|
-
"@dxos/
|
|
108
|
-
"@dxos/storybook-utils": "0.8.4-main.
|
|
109
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
110
|
-
"@dxos/
|
|
104
|
+
"@dxos/echo": "0.8.4-main.9be5663bfe",
|
|
105
|
+
"@dxos/keyboard": "0.8.4-main.9be5663bfe",
|
|
106
|
+
"@dxos/config": "0.8.4-main.9be5663bfe",
|
|
107
|
+
"@dxos/schema": "0.8.4-main.9be5663bfe",
|
|
108
|
+
"@dxos/storybook-utils": "0.8.4-main.9be5663bfe",
|
|
109
|
+
"@dxos/ui-theme": "0.8.4-main.9be5663bfe",
|
|
110
|
+
"@dxos/random": "0.8.4-main.9be5663bfe"
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
|
-
"@effect/platform": "0.
|
|
114
|
-
"effect": "3.
|
|
115
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
113
|
+
"@effect/platform": "0.94.4",
|
|
114
|
+
"effect": "3.20.0",
|
|
115
|
+
"@dxos/ui-theme": "0.8.4-main.9be5663bfe"
|
|
116
116
|
},
|
|
117
117
|
"publishConfig": {
|
|
118
118
|
"access": "public"
|
package/src/defaults.ts
CHANGED
|
@@ -6,29 +6,33 @@ import { mx } from '@dxos/ui-theme';
|
|
|
6
6
|
|
|
7
7
|
import { type ThemeExtensionsOptions } from './extensions';
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export const editorWidth = '!mli-auto is-full max-is-[min(50rem,100%-4rem)]';
|
|
9
|
+
// NOTE: Padding is added to the editor to account for the focus ring (since otherwise the CM gutter will clip it)
|
|
10
|
+
export const editorClassNames = (role?: string) =>
|
|
11
|
+
mx(
|
|
12
|
+
'dx-attention-surface p-0.5 data-[toolbar=disabled]:pt-2 dx-focus-ring-inset',
|
|
13
|
+
role === 'section' ? '[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-h-24' : 'dx-container overflow-hidden',
|
|
14
|
+
);
|
|
16
15
|
|
|
17
|
-
export const
|
|
18
|
-
scroll: {
|
|
19
|
-
className: 'pbs-2',
|
|
20
|
-
},
|
|
16
|
+
export const documentSlots: ThemeExtensionsOptions['slots'] = {
|
|
21
17
|
content: {
|
|
22
|
-
|
|
18
|
+
/**
|
|
19
|
+
* CodeMirror content width.
|
|
20
|
+
* 40rem = 640px. Corresponds to initial plank width (Google docs, Stashpad, etc.)
|
|
21
|
+
* 50rem = 800px. Maximum content width for solo mode.
|
|
22
|
+
* NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).
|
|
23
|
+
*/
|
|
24
|
+
className: mx(
|
|
25
|
+
// NOTE: Container for widget sizing (must have `max-w-[100cqi]`).
|
|
26
|
+
'dx-size-container',
|
|
27
|
+
// Wider margin for web (vs. mobile).
|
|
28
|
+
'pointer-fine:max-w-[min(50rem,100%-4rem)] pointer-coarse:max-w-[min(50rem,100%-2rem)]',
|
|
29
|
+
'mx-auto! w-full',
|
|
30
|
+
),
|
|
23
31
|
},
|
|
24
32
|
};
|
|
25
33
|
|
|
26
|
-
export const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
mx(
|
|
32
|
-
'p-0.5 dx-focus-ring-inset attention-surface data-[toolbar=disabled]:pbs-2',
|
|
33
|
-
role === 'section' ? '[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24' : 'min-bs-0',
|
|
34
|
-
);
|
|
34
|
+
export const compactSlots: ThemeExtensionsOptions['slots'] = {
|
|
35
|
+
content: {
|
|
36
|
+
className: 'mx-2! w-full',
|
|
37
|
+
},
|
|
38
|
+
};
|
|
@@ -48,7 +48,7 @@ export const annotations = ({ match }: AnnotationOptions = {}): Extension => {
|
|
|
48
48
|
'.cm-annotation': {
|
|
49
49
|
textDecoration: 'underline',
|
|
50
50
|
textDecorationStyle: 'wavy',
|
|
51
|
-
textDecorationColor: 'var(--
|
|
51
|
+
textDecorationColor: 'var(--color-error-text)',
|
|
52
52
|
},
|
|
53
53
|
}),
|
|
54
54
|
];
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { StateEffect } from '@codemirror/state';
|
|
6
|
+
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
import { addEventListener, combine, throttle } from '@dxos/async';
|
|
9
|
+
import { Domino } from '@dxos/ui';
|
|
10
|
+
import { getSize } from '@dxos/ui-theme';
|
|
11
|
+
|
|
12
|
+
import { scrollerCrawlEffect, scrollerLineEffect } from './scroller';
|
|
13
|
+
|
|
14
|
+
/** Enable or disable autoscroll. */
|
|
15
|
+
export const autoScrollEffect = StateEffect.define<boolean>();
|
|
16
|
+
|
|
17
|
+
export type AutoScrollProps = {};
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
|
|
21
|
+
*/
|
|
22
|
+
export const autoScroll = (_: AutoScrollProps = {}) => {
|
|
23
|
+
let buttonContainer: HTMLDivElement | undefined;
|
|
24
|
+
let isPinned = true;
|
|
25
|
+
let jumpPending = false;
|
|
26
|
+
let enabled = true;
|
|
27
|
+
let firstUpdate = true;
|
|
28
|
+
|
|
29
|
+
const setPinned = (pinned: boolean) => {
|
|
30
|
+
buttonContainer?.classList.toggle('opacity-0', pinned);
|
|
31
|
+
isPinned = pinned;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return [
|
|
35
|
+
// Update listener for scrolling when content changes.
|
|
36
|
+
EditorView.updateListener.of((update) => {
|
|
37
|
+
const { view, heightChanged, state, startState } = update;
|
|
38
|
+
|
|
39
|
+
// Handle enable/disable effect.
|
|
40
|
+
for (const tr of update.transactions) {
|
|
41
|
+
for (const effect of tr.effects) {
|
|
42
|
+
if (effect.is(autoScrollEffect)) {
|
|
43
|
+
enabled = effect.value;
|
|
44
|
+
if (enabled) {
|
|
45
|
+
setPinned(true);
|
|
46
|
+
view.dispatch({
|
|
47
|
+
effects: scrollerCrawlEffect.of(true),
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
view.dispatch({
|
|
51
|
+
effects: scrollerCrawlEffect.of(false),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!enabled) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Jump to bottom instantly when content first appears (either inserted into
|
|
63
|
+
// an empty doc, or present as initialValue when the editor is created).
|
|
64
|
+
if (isPinned && (firstUpdate || startState.doc.length === 0) && state.doc.length > 0) {
|
|
65
|
+
firstUpdate = false;
|
|
66
|
+
jumpPending = true;
|
|
67
|
+
requestAnimationFrame(() => {
|
|
68
|
+
view.scrollDOM.scrollTop = view.scrollDOM.scrollHeight;
|
|
69
|
+
jumpPending = false;
|
|
70
|
+
});
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
firstUpdate = false;
|
|
74
|
+
|
|
75
|
+
// Suppress crawl while the initial jump is pending.
|
|
76
|
+
if (jumpPending) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Maybe scroll if doc changed and pinned.
|
|
81
|
+
// NOTE: Geometry changed is triggered when widgets change height (e.g., toggle tool block).
|
|
82
|
+
if (heightChanged) {
|
|
83
|
+
if (isPinned) {
|
|
84
|
+
// NOTE: Use scroll geometry instead of coordsAtPos to avoid forced layout/scroll side-effects.
|
|
85
|
+
const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
|
|
86
|
+
const delta = scrollHeight - scrollTop - clientHeight;
|
|
87
|
+
if (delta > 0) {
|
|
88
|
+
setPinned(true);
|
|
89
|
+
view.dispatch({
|
|
90
|
+
effects: scrollerCrawlEffect.of(true),
|
|
91
|
+
});
|
|
92
|
+
} else if (delta < -1) {
|
|
93
|
+
setPinned(false);
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
// TODO(burdon): Should re-pin if content shrinks.
|
|
97
|
+
if (state.doc.length === 0) {
|
|
98
|
+
setPinned(true);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}),
|
|
103
|
+
|
|
104
|
+
// Detect user scroll and unpin (or re-pin if scrolled to the bottom).
|
|
105
|
+
ViewPlugin.fromClass(
|
|
106
|
+
class {
|
|
107
|
+
private readonly cleanup: () => void;
|
|
108
|
+
constructor(view: EditorView) {
|
|
109
|
+
this.cleanup = createUserScrollDetector(
|
|
110
|
+
view.scrollDOM,
|
|
111
|
+
throttle(() => {
|
|
112
|
+
requestAnimationFrame(() => {
|
|
113
|
+
const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
|
|
114
|
+
const delta = scrollHeight - scrollTop - clientHeight;
|
|
115
|
+
const pinned = delta === 0;
|
|
116
|
+
setPinned(pinned);
|
|
117
|
+
if (!pinned) {
|
|
118
|
+
view.dispatch({ effects: scrollerCrawlEffect.of(false) });
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}, 500),
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
destroy() {
|
|
125
|
+
this.cleanup();
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
),
|
|
129
|
+
|
|
130
|
+
// Scroll button.
|
|
131
|
+
ViewPlugin.fromClass(
|
|
132
|
+
class {
|
|
133
|
+
constructor(view: EditorView) {
|
|
134
|
+
const icon = Domino.of('dx-icon' as any)
|
|
135
|
+
.classNames(getSize(4))
|
|
136
|
+
.attributes({ icon: 'ph--arrow-down--regular' });
|
|
137
|
+
const button = Domino.of('button')
|
|
138
|
+
.classNames('dx-button bg-accent-surface')
|
|
139
|
+
.attributes({ 'data-density': 'fine' })
|
|
140
|
+
.append(icon)
|
|
141
|
+
.on('click', () => {
|
|
142
|
+
setPinned(true);
|
|
143
|
+
view.dispatch({
|
|
144
|
+
effects: scrollerLineEffect.of({ line: -1, position: 'end', behavior: 'smooth' }),
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
buttonContainer = Domino.of('div')
|
|
149
|
+
.classNames('cm-scroll-button transition-opacity duration-300 opacity-0')
|
|
150
|
+
.append(button).root as HTMLDivElement;
|
|
151
|
+
|
|
152
|
+
view.scrollDOM.parentElement!.appendChild(buttonContainer);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
),
|
|
156
|
+
];
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Attaches listeners to detect genuine user-initiated scrolling on an element.
|
|
161
|
+
* Two sources are covered:
|
|
162
|
+
* - `wheel`: fires only from physical mouse wheel / trackpad gestures.
|
|
163
|
+
* - `pointerdown` on the scrollbar gutter: detected by comparing clientX to
|
|
164
|
+
* the element's clientWidth (the content area, excluding the scrollbar).
|
|
165
|
+
* Returns a cleanup function that removes the listeners.
|
|
166
|
+
*/
|
|
167
|
+
// TODO(burdon): Still jumps when widgets are rendered.
|
|
168
|
+
// - Track position of specific element/line in document and scroll relative to that.
|
|
169
|
+
function createUserScrollDetector(element: HTMLElement, onUserScroll: () => void): () => void {
|
|
170
|
+
return combine(
|
|
171
|
+
addEventListener(element, 'wheel', () => onUserScroll(), { passive: true }),
|
|
172
|
+
addEventListener(element, 'pointerdown', (event) => {
|
|
173
|
+
// If the pointer lands beyond the content width it hit the scrollbar gutter.
|
|
174
|
+
if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
|
|
175
|
+
onUserScroll();
|
|
176
|
+
}
|
|
177
|
+
}),
|
|
178
|
+
);
|
|
179
|
+
}
|
|
@@ -9,7 +9,7 @@ import { render, screen } from '@testing-library/react';
|
|
|
9
9
|
import React, { type FC, useEffect, useRef, useState } from 'react';
|
|
10
10
|
import { describe, test } from 'vitest';
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { getDeep } from '@dxos/util';
|
|
13
13
|
|
|
14
14
|
import { automerge } from './automerge';
|
|
15
15
|
|
|
@@ -46,7 +46,7 @@ const Test: FC<{ handle: DocHandle<TestObject>; generator: Generator }> = ({ han
|
|
|
46
46
|
];
|
|
47
47
|
|
|
48
48
|
const view = new EditorView({
|
|
49
|
-
state: EditorState.create({ doc:
|
|
49
|
+
state: EditorState.create({ doc: getDeep(handle.doc()!, path), extensions }),
|
|
50
50
|
parent: ref.current!,
|
|
51
51
|
});
|
|
52
52
|
|
|
@@ -12,7 +12,6 @@ import { DocAccessor } from '@dxos/echo-db';
|
|
|
12
12
|
|
|
13
13
|
import { Cursor } from '../../util';
|
|
14
14
|
import { initialSync } from '../state';
|
|
15
|
-
|
|
16
15
|
import { cursorConverter } from './cursor';
|
|
17
16
|
import { type State, isReconcile, updateHeadsEffect } from './defs';
|
|
18
17
|
import { Syncer } from './sync';
|
|
@@ -72,11 +71,12 @@ export const automerge = (accessor: DocAccessor): Extension => {
|
|
|
72
71
|
const value = DocAccessor.getValue<string>(accessor);
|
|
73
72
|
const current = this._view.state.doc.toString();
|
|
74
73
|
if (value !== current) {
|
|
75
|
-
// TODO(burdon): This attempts to set the initial state
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
74
|
+
// TODO(burdon): This attempts to set the initial state.
|
|
75
|
+
console.warn('ENABLING INITIAL SYNC -- THIS MAY BE A REGRESSION');
|
|
76
|
+
this._view.dispatch({
|
|
77
|
+
changes: { from: 0, to: this._view.state.doc.length, insert: value },
|
|
78
|
+
annotations: initialSync,
|
|
79
|
+
});
|
|
80
80
|
}
|
|
81
81
|
});
|
|
82
82
|
}
|
package/src/extensions/blocks.ts
CHANGED
|
@@ -101,11 +101,11 @@ export const blocks = () => [
|
|
|
101
101
|
'.cm-line.block-line': {
|
|
102
102
|
paddingLeft: '0.75rem',
|
|
103
103
|
paddingRight: '0.75rem',
|
|
104
|
-
borderLeft: '1px solid var(--
|
|
105
|
-
borderRight: '1px solid var(--
|
|
104
|
+
borderLeft: '1px solid var(--color-subdued-separator)',
|
|
105
|
+
borderRight: '1px solid var(--color-subdued-separator)',
|
|
106
106
|
},
|
|
107
107
|
'.cm-line.block-single': {
|
|
108
|
-
border: '1px solid var(--
|
|
108
|
+
border: '1px solid var(--color-subdued-separator)',
|
|
109
109
|
borderRadius: '6px',
|
|
110
110
|
paddingTop: '0.5rem',
|
|
111
111
|
paddingBottom: '0.5rem',
|
|
@@ -113,7 +113,7 @@ export const blocks = () => [
|
|
|
113
113
|
marginBottom: '0.5rem',
|
|
114
114
|
},
|
|
115
115
|
'.cm-line.block-first': {
|
|
116
|
-
borderTop: '1px solid var(--
|
|
116
|
+
borderTop: '1px solid var(--color-subdued-separator)',
|
|
117
117
|
borderTopLeftRadius: '6px',
|
|
118
118
|
borderTopRightRadius: '6px',
|
|
119
119
|
paddingTop: '0.5rem',
|
|
@@ -121,7 +121,7 @@ export const blocks = () => [
|
|
|
121
121
|
},
|
|
122
122
|
'.cm-line.block-middle': {},
|
|
123
123
|
'.cm-line.block-last': {
|
|
124
|
-
borderBottom: '1px solid var(--
|
|
124
|
+
borderBottom: '1px solid var(--color-subdued-separator)',
|
|
125
125
|
borderBottomLeftRadius: '6px',
|
|
126
126
|
borderBottomRightRadius: '6px',
|
|
127
127
|
paddingBottom: '0.5rem',
|
|
@@ -22,7 +22,6 @@ import { isNonNullable } from '@dxos/util';
|
|
|
22
22
|
|
|
23
23
|
import { type Comment, type Range, type RenderCallback } from '../types';
|
|
24
24
|
import { Cursor, singleValueFacet, wrapWithCatch } from '../util';
|
|
25
|
-
|
|
26
25
|
import { documentId } from './selection';
|
|
27
26
|
|
|
28
27
|
//
|
|
@@ -103,14 +102,14 @@ export const commentsState = StateField.define<CommentsState>({
|
|
|
103
102
|
const styles = EditorView.theme({
|
|
104
103
|
'.cm-comment, .cm-comment-current': {
|
|
105
104
|
padding: '3px 0',
|
|
106
|
-
color: 'var(--
|
|
107
|
-
backgroundColor: 'var(--
|
|
105
|
+
color: 'var(--color-cm-comment-text)',
|
|
106
|
+
backgroundColor: 'var(--color-cm-comment-surface)',
|
|
108
107
|
},
|
|
109
108
|
'.cm-comment > span, .cm-comment-current > span': {
|
|
110
109
|
boxDecorationBreak: 'clone',
|
|
111
|
-
boxShadow: '0 0 1px 3px var(--
|
|
112
|
-
backgroundColor: 'var(--
|
|
113
|
-
color: 'var(--
|
|
110
|
+
boxShadow: '0 0 1px 3px var(--color-cm-comment-surface)',
|
|
111
|
+
backgroundColor: 'var(--color-cm-comment-surface)',
|
|
112
|
+
color: 'var(--color-cm-comment-text)',
|
|
114
113
|
cursor: 'pointer',
|
|
115
114
|
},
|
|
116
115
|
});
|
package/src/extensions/dnd.ts
CHANGED
|
@@ -29,8 +29,8 @@ export const dropFile = (options: DropOptions = {}): Extension => {
|
|
|
29
29
|
|
|
30
30
|
const styles = EditorView.theme({
|
|
31
31
|
'.cm-dropCursor': {
|
|
32
|
-
borderLeft: '2px solid var(--
|
|
33
|
-
color: 'var(--
|
|
32
|
+
borderLeft: '2px solid var(--color-accent-text)',
|
|
33
|
+
color: 'var(--color-accent-text)',
|
|
34
34
|
padding: '0 4px',
|
|
35
35
|
},
|
|
36
36
|
'.cm-dropCursor:after': {
|
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
keymap,
|
|
18
18
|
lineNumbers,
|
|
19
19
|
placeholder,
|
|
20
|
-
scrollPastEnd,
|
|
21
20
|
} from '@codemirror/view';
|
|
22
21
|
import { vscodeDarkStyle, vscodeLightStyle } from '@uiw/codemirror-theme-vscode';
|
|
23
22
|
import defaultsDeep from 'lodash.defaultsdeep';
|
|
@@ -27,15 +26,14 @@ import { type DocAccessor } from '@dxos/echo-db';
|
|
|
27
26
|
import { log } from '@dxos/log';
|
|
28
27
|
import { type Messenger } from '@dxos/protocols';
|
|
29
28
|
import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
|
|
30
|
-
import { type
|
|
31
|
-
import { type ThemeMode } from '@dxos/ui-types';
|
|
29
|
+
import { type ChromaticPalette, type ThemeMode } from '@dxos/ui-types';
|
|
32
30
|
import { hexToHue, isTruthy } from '@dxos/util';
|
|
33
31
|
|
|
34
32
|
import { baseTheme, createFontTheme, editorGutter } from '../styles';
|
|
35
|
-
|
|
36
33
|
import { automerge } from './automerge';
|
|
37
34
|
import { SpaceAwarenessProvider, awareness } from './awareness';
|
|
38
35
|
import { focus } from './focus';
|
|
36
|
+
import { scrollPastEnd } from './scroll-past-end';
|
|
39
37
|
|
|
40
38
|
//
|
|
41
39
|
// Basic
|
|
@@ -185,7 +183,7 @@ export type ThemeExtensionsOptions = {
|
|
|
185
183
|
editor?: {
|
|
186
184
|
className?: string;
|
|
187
185
|
};
|
|
188
|
-
|
|
186
|
+
scroller?: {
|
|
189
187
|
// NOTE: Do not apply vertical padding to scroll container.
|
|
190
188
|
className?: string;
|
|
191
189
|
};
|
|
@@ -198,13 +196,13 @@ export type ThemeExtensionsOptions = {
|
|
|
198
196
|
|
|
199
197
|
export const grow: ThemeExtensionsOptions['slots'] = {
|
|
200
198
|
editor: {
|
|
201
|
-
className: '
|
|
199
|
+
className: 'h-full w-full',
|
|
202
200
|
},
|
|
203
201
|
} as const;
|
|
204
202
|
|
|
205
203
|
export const fullWidth: ThemeExtensionsOptions['slots'] = {
|
|
206
204
|
editor: {
|
|
207
|
-
className: '
|
|
205
|
+
className: 'w-full',
|
|
208
206
|
},
|
|
209
207
|
} as const;
|
|
210
208
|
|
|
@@ -263,7 +261,7 @@ export const createDataExtensions = <T>({ id, text, messenger, identity }: DataE
|
|
|
263
261
|
|
|
264
262
|
if (messenger && identity) {
|
|
265
263
|
const peerId = identity?.identityKey.toHex();
|
|
266
|
-
const hue = (identity?.profile?.data?.hue as
|
|
264
|
+
const hue = (identity?.profile?.data?.hue as ChromaticPalette | undefined) ?? hexToHue(peerId ?? '0');
|
|
267
265
|
extensions.push(
|
|
268
266
|
awareness(
|
|
269
267
|
new SpaceAwarenessProvider({
|
|
@@ -271,8 +269,8 @@ export const createDataExtensions = <T>({ id, text, messenger, identity }: DataE
|
|
|
271
269
|
channel: `awareness.${id}`,
|
|
272
270
|
peerId: identity.identityKey.toHex(),
|
|
273
271
|
info: {
|
|
274
|
-
darkColor: `var(--
|
|
275
|
-
lightColor: `var(--
|
|
272
|
+
darkColor: `var(--color-${hue}-border)`,
|
|
273
|
+
lightColor: `var(--color-${hue}-border)`,
|
|
276
274
|
displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
|
277
275
|
},
|
|
278
276
|
}),
|
|
@@ -17,37 +17,20 @@ export const folding = (): Extension => {
|
|
|
17
17
|
placeholderDOM: () => Domino.of('span').root,
|
|
18
18
|
}),
|
|
19
19
|
foldGutter({
|
|
20
|
+
// NOTE: We can't animate since the element is remounted on state change.
|
|
20
21
|
markerDOM: (open) => {
|
|
21
22
|
return Domino.of('div')
|
|
22
|
-
.classNames('flex
|
|
23
|
-
.
|
|
23
|
+
.classNames('flex h-full justify-center items-center')
|
|
24
|
+
.append(
|
|
24
25
|
Domino.of('svg', Domino.SVG)
|
|
25
|
-
.classNames(mx('
|
|
26
|
-
.
|
|
26
|
+
.classNames(mx('w-4 h-4 cursor-pointer', open && 'rotate-90'))
|
|
27
|
+
.append(
|
|
27
28
|
Domino.of('use', Domino.SVG).attributes({
|
|
28
29
|
href: Domino.icon('ph--caret-right--regular'),
|
|
29
30
|
}),
|
|
30
31
|
),
|
|
31
32
|
).root;
|
|
32
33
|
},
|
|
33
|
-
// TODO(burdon): markerDOM is called either way, defeating the animation: transition-transform duration-200
|
|
34
|
-
// domEventHandlers: {
|
|
35
|
-
// click: (view, line: BlockInfo, event) => {
|
|
36
|
-
// event.preventDefault();
|
|
37
|
-
// event.stopPropagation();
|
|
38
|
-
// const range = foldable(view.state, line.from, line.to);
|
|
39
|
-
// if (range) {
|
|
40
|
-
// view.dispatch({ effects: foldEffect.of(range) });
|
|
41
|
-
// (event.target as HTMLElement)?.classList.add('rotate-90');
|
|
42
|
-
// } else {
|
|
43
|
-
// foldedRanges(view.state).between(line.from, line.to, (from, to) => {
|
|
44
|
-
// view.dispatch({ effects: unfoldEffect.of({ from, to }) });
|
|
45
|
-
// (event.target as HTMLElement)?.classList.remove('rotate-90');
|
|
46
|
-
// });
|
|
47
|
-
// }
|
|
48
|
-
// return true;
|
|
49
|
-
// },
|
|
50
|
-
// },
|
|
51
34
|
}),
|
|
52
35
|
EditorView.theme({
|
|
53
36
|
'.cm-foldGutter': {
|
package/src/extensions/index.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
export * from './annotations';
|
|
6
6
|
export * from './autocomplete';
|
|
7
|
-
export * from './
|
|
7
|
+
export * from './auto-scroll';
|
|
8
8
|
export * from './automerge';
|
|
9
9
|
export * from './awareness';
|
|
10
10
|
export * from './blast';
|
|
@@ -26,8 +26,9 @@ export * from './modes';
|
|
|
26
26
|
export * from './outliner';
|
|
27
27
|
export * from './preview';
|
|
28
28
|
export * from './replacer';
|
|
29
|
+
export * from './scroll-past-end';
|
|
30
|
+
export * from './scroller';
|
|
29
31
|
export * from './selection';
|
|
30
|
-
export * from './scrolling';
|
|
31
32
|
export * from './state';
|
|
32
33
|
export * from './submit';
|
|
33
34
|
export * from './tags';
|