@dxos/ui-editor 0.8.4-main.bc674ce → 0.8.4-main.c85a9c8dae
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 +483 -417
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node-esm/index.mjs +483 -417
- 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 +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/auto-scroll.d.ts +6 -0
- package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -0
- package/dist/types/src/extensions/factories.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +2 -2
- package/dist/types/src/extensions/index.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/preview/preview.d.ts +1 -1
- package/dist/types/src/extensions/scroller.d.ts +66 -0
- package/dist/types/src/extensions/scroller.d.ts.map +1 -0
- 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 +4 -4
- package/src/extensions/annotations.ts +1 -1
- package/src/extensions/auto-scroll.ts +126 -0
- package/src/extensions/automerge/automerge.test.tsx +2 -2
- package/src/extensions/blocks.ts +5 -5
- package/src/extensions/comments.ts +5 -5
- package/src/extensions/dnd.ts +2 -2
- package/src/extensions/factories.ts +6 -7
- package/src/extensions/folding.ts +2 -2
- package/src/extensions/index.ts +2 -2
- package/src/extensions/markdown/decorate.ts +4 -3
- package/src/extensions/markdown/highlight.ts +25 -11
- package/src/extensions/markdown/link.ts +27 -33
- package/src/extensions/markdown/styles.ts +6 -6
- package/src/extensions/outliner/outliner.ts +3 -3
- package/src/extensions/preview/preview.ts +8 -8
- package/src/extensions/scroller.ts +232 -0
- package/src/extensions/tags/streamer.ts +1 -1
- package/src/extensions/tags/xml-tags.ts +7 -4
- package/src/styles/index.ts +0 -2
- package/src/styles/theme.ts +106 -29
- package/src/util/cursor.ts +1 -1
- 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/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/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.c85a9c8dae",
|
|
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/context": "0.8.4-main.
|
|
72
|
-
"@dxos/
|
|
73
|
-
"@dxos/echo": "0.8.4-main.
|
|
74
|
-
"@dxos/invariant": "0.8.4-main.
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/ui": "0.8.4-main.
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/
|
|
81
|
-
"@dxos/
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/
|
|
68
|
+
"@dxos/app-graph": "0.8.4-main.c85a9c8dae",
|
|
69
|
+
"@dxos/async": "0.8.4-main.c85a9c8dae",
|
|
70
|
+
"@dxos/client": "0.8.4-main.c85a9c8dae",
|
|
71
|
+
"@dxos/context": "0.8.4-main.c85a9c8dae",
|
|
72
|
+
"@dxos/debug": "0.8.4-main.c85a9c8dae",
|
|
73
|
+
"@dxos/echo": "0.8.4-main.c85a9c8dae",
|
|
74
|
+
"@dxos/invariant": "0.8.4-main.c85a9c8dae",
|
|
75
|
+
"@dxos/log": "0.8.4-main.c85a9c8dae",
|
|
76
|
+
"@dxos/lit-ui": "0.8.4-main.c85a9c8dae",
|
|
77
|
+
"@dxos/protocols": "0.8.4-main.c85a9c8dae",
|
|
78
|
+
"@dxos/ui": "0.8.4-main.c85a9c8dae",
|
|
79
|
+
"@dxos/ui-theme": "0.8.4-main.c85a9c8dae",
|
|
80
|
+
"@dxos/ui-types": "0.8.4-main.c85a9c8dae",
|
|
81
|
+
"@dxos/util": "0.8.4-main.c85a9c8dae",
|
|
82
|
+
"@dxos/echo-db": "0.8.4-main.c85a9c8dae",
|
|
83
|
+
"@dxos/display-name": "0.8.4-main.c85a9c8dae"
|
|
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.19.
|
|
98
|
-
"happy-dom": "^
|
|
97
|
+
"effect": "3.19.16",
|
|
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/config": "0.8.4-main.
|
|
105
|
-
"@dxos/echo": "0.8.4-main.
|
|
106
|
-
"@dxos/
|
|
107
|
-
"@dxos/
|
|
108
|
-
"@dxos/
|
|
109
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
110
|
-
"@dxos/
|
|
104
|
+
"@dxos/config": "0.8.4-main.c85a9c8dae",
|
|
105
|
+
"@dxos/echo": "0.8.4-main.c85a9c8dae",
|
|
106
|
+
"@dxos/random": "0.8.4-main.c85a9c8dae",
|
|
107
|
+
"@dxos/keyboard": "0.8.4-main.c85a9c8dae",
|
|
108
|
+
"@dxos/storybook-utils": "0.8.4-main.c85a9c8dae",
|
|
109
|
+
"@dxos/ui-theme": "0.8.4-main.c85a9c8dae",
|
|
110
|
+
"@dxos/schema": "0.8.4-main.c85a9c8dae"
|
|
111
111
|
},
|
|
112
112
|
"peerDependencies": {
|
|
113
|
-
"@effect/platform": "0.
|
|
114
|
-
"effect": "3.19.
|
|
115
|
-
"@dxos/ui-theme": "0.8.4-main.
|
|
113
|
+
"@effect/platform": "0.94.4",
|
|
114
|
+
"effect": "3.19.16",
|
|
115
|
+
"@dxos/ui-theme": "0.8.4-main.c85a9c8dae"
|
|
116
116
|
},
|
|
117
117
|
"publishConfig": {
|
|
118
118
|
"access": "public"
|
package/src/defaults.ts
CHANGED
|
@@ -12,11 +12,11 @@ import { type ThemeExtensionsOptions } from './extensions';
|
|
|
12
12
|
* 50rem = 800px. Maximum content width for solo mode.
|
|
13
13
|
* NOTE: Max width - 4rem = 2rem left/right margin (or 2rem gutter plus 1rem left/right margin).
|
|
14
14
|
*/
|
|
15
|
-
export const editorWidth = '!
|
|
15
|
+
export const editorWidth = '!mx-auto w-full max-w-[min(50rem,100%-4rem)]';
|
|
16
16
|
|
|
17
17
|
export const editorSlots: ThemeExtensionsOptions['slots'] = {
|
|
18
18
|
scroll: {
|
|
19
|
-
className: '
|
|
19
|
+
className: 'pt-2',
|
|
20
20
|
},
|
|
21
21
|
content: {
|
|
22
22
|
className: editorWidth,
|
|
@@ -29,6 +29,6 @@ export const editorWithToolbarLayout =
|
|
|
29
29
|
// NOTE: Padding is added to the editor to account for the focus ring (since otherwise the CM gutter will clip it)
|
|
30
30
|
export const stackItemContentEditorClassNames = (role?: string) =>
|
|
31
31
|
mx(
|
|
32
|
-
'p-0.5 dx-focus-ring-inset
|
|
33
|
-
role === 'section' ? '[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-
|
|
32
|
+
'dx-attention-surface p-0.5 dx-focus-ring-inset data-[toolbar=disabled]:pt-2',
|
|
33
|
+
role === 'section' ? '[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-h-24' : 'dx-container overflow-hidden',
|
|
34
34
|
);
|
|
@@ -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,126 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
6
|
+
|
|
7
|
+
import { addEventListener, combine, throttle } from '@dxos/async';
|
|
8
|
+
import { Domino } from '@dxos/ui';
|
|
9
|
+
|
|
10
|
+
import { scrollerCrawlEffect, scrollerLineEffect } from './scroller';
|
|
11
|
+
|
|
12
|
+
export type AutoScrollToProps = {};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
|
|
16
|
+
*/
|
|
17
|
+
export const autoScroll = (_: AutoScrollToProps = {}) => {
|
|
18
|
+
let buttonContainer: HTMLDivElement | undefined;
|
|
19
|
+
let isPinned = true;
|
|
20
|
+
|
|
21
|
+
const setPinned = (pinned: boolean) => {
|
|
22
|
+
buttonContainer?.classList.toggle('opacity-0', pinned);
|
|
23
|
+
isPinned = pinned;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
return [
|
|
27
|
+
// Update listener for logging when scrolling is needed.
|
|
28
|
+
EditorView.updateListener.of(({ view, heightChanged, state }) => {
|
|
29
|
+
// Maybe scroll if doc changed and pinned.
|
|
30
|
+
// NOTE: Geometry changed is triggered when widgets change height (e.g., toggle tool block).
|
|
31
|
+
if (heightChanged) {
|
|
32
|
+
if (isPinned) {
|
|
33
|
+
// NOTE: Use scroll geometry instead of coordsAtPos to avoid forced layout/scroll side-effects.
|
|
34
|
+
const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
|
|
35
|
+
const delta = scrollHeight - scrollTop - clientHeight;
|
|
36
|
+
if (delta > 0 && scrollTop > 0) {
|
|
37
|
+
setPinned(true);
|
|
38
|
+
view.dispatch({
|
|
39
|
+
effects: scrollerCrawlEffect.of(true),
|
|
40
|
+
});
|
|
41
|
+
} else if (delta < 0) {
|
|
42
|
+
setPinned(false);
|
|
43
|
+
}
|
|
44
|
+
} else {
|
|
45
|
+
// TODO(burdon): Should re-pin if content shrinks.
|
|
46
|
+
if (state.doc.length === 0) {
|
|
47
|
+
setPinned(true);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}),
|
|
52
|
+
|
|
53
|
+
// Detect user scroll and unpin (or re-pin if scrolled to the bottom).
|
|
54
|
+
ViewPlugin.fromClass(
|
|
55
|
+
class {
|
|
56
|
+
private readonly cleanup: () => void;
|
|
57
|
+
constructor(view: EditorView) {
|
|
58
|
+
this.cleanup = createUserScrollDetector(
|
|
59
|
+
view.scrollDOM,
|
|
60
|
+
throttle(() => {
|
|
61
|
+
requestAnimationFrame(() => {
|
|
62
|
+
const { scrollTop, scrollHeight, clientHeight } = view.scrollDOM;
|
|
63
|
+
const delta = scrollHeight - scrollTop - clientHeight;
|
|
64
|
+
const pinned = delta === 0;
|
|
65
|
+
setPinned(pinned);
|
|
66
|
+
if (!pinned) {
|
|
67
|
+
view.dispatch({ effects: scrollerCrawlEffect.of(false) });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}, 500),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
destroy() {
|
|
74
|
+
this.cleanup();
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
),
|
|
78
|
+
|
|
79
|
+
// Scroll button.
|
|
80
|
+
ViewPlugin.fromClass(
|
|
81
|
+
class {
|
|
82
|
+
constructor(view: EditorView) {
|
|
83
|
+
const icon = Domino.of('dx-icon' as any).attributes({ icon: 'ph--arrow-down--regular' });
|
|
84
|
+
const button = Domino.of('button')
|
|
85
|
+
.classNames('dx-button bg-accent-surface')
|
|
86
|
+
.attributes({ 'data-density': 'fine' })
|
|
87
|
+
.children(icon)
|
|
88
|
+
.on('click', () => {
|
|
89
|
+
setPinned(true);
|
|
90
|
+
view.dispatch({
|
|
91
|
+
effects: scrollerLineEffect.of({ line: -1, position: 'end', behavior: 'smooth' }),
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
buttonContainer = Domino.of('div')
|
|
96
|
+
.classNames('cm-scroll-button transition-opacity duration-300 opacity-0')
|
|
97
|
+
.children(button).root as HTMLDivElement;
|
|
98
|
+
|
|
99
|
+
view.scrollDOM.parentElement!.appendChild(buttonContainer);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
),
|
|
103
|
+
];
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Attaches listeners to detect genuine user-initiated scrolling on an element.
|
|
108
|
+
* Two sources are covered:
|
|
109
|
+
* - `wheel`: fires only from physical mouse wheel / trackpad gestures.
|
|
110
|
+
* - `pointerdown` on the scrollbar gutter: detected by comparing clientX to
|
|
111
|
+
* the element's clientWidth (the content area, excluding the scrollbar).
|
|
112
|
+
* Returns a cleanup function that removes the listeners.
|
|
113
|
+
*/
|
|
114
|
+
// TODO(burdon): Still jumps when widgets are rendered.
|
|
115
|
+
// - Track position of specific element/line in document and scroll relative to that.
|
|
116
|
+
function createUserScrollDetector(element: HTMLElement, onUserScroll: () => void): () => void {
|
|
117
|
+
return combine(
|
|
118
|
+
addEventListener(element, 'wheel', () => onUserScroll(), { passive: true }),
|
|
119
|
+
addEventListener(element, 'pointerdown', (event) => {
|
|
120
|
+
// If the pointer lands beyond the content width it hit the scrollbar gutter.
|
|
121
|
+
if (event.clientX > element.getBoundingClientRect().right - (element.offsetWidth - element.clientWidth)) {
|
|
122
|
+
onUserScroll();
|
|
123
|
+
}
|
|
124
|
+
}),
|
|
125
|
+
);
|
|
126
|
+
}
|
|
@@ -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
|
|
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',
|
|
@@ -103,14 +103,14 @@ export const commentsState = StateField.define<CommentsState>({
|
|
|
103
103
|
const styles = EditorView.theme({
|
|
104
104
|
'.cm-comment, .cm-comment-current': {
|
|
105
105
|
padding: '3px 0',
|
|
106
|
-
color: 'var(--
|
|
107
|
-
backgroundColor: 'var(--
|
|
106
|
+
color: 'var(--color-cm-comment-text)',
|
|
107
|
+
backgroundColor: 'var(--color-cm-comment-surface)',
|
|
108
108
|
},
|
|
109
109
|
'.cm-comment > span, .cm-comment-current > span': {
|
|
110
110
|
boxDecorationBreak: 'clone',
|
|
111
|
-
boxShadow: '0 0 1px 3px var(--
|
|
112
|
-
backgroundColor: 'var(--
|
|
113
|
-
color: 'var(--
|
|
111
|
+
boxShadow: '0 0 1px 3px var(--color-cm-comment-surface)',
|
|
112
|
+
backgroundColor: 'var(--color-cm-comment-surface)',
|
|
113
|
+
color: 'var(--color-cm-comment-text)',
|
|
114
114
|
cursor: 'pointer',
|
|
115
115
|
},
|
|
116
116
|
});
|
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': {
|
|
@@ -27,8 +27,7 @@ import { type DocAccessor } from '@dxos/echo-db';
|
|
|
27
27
|
import { log } from '@dxos/log';
|
|
28
28
|
import { type Messenger } from '@dxos/protocols';
|
|
29
29
|
import { type Identity } from '@dxos/protocols/proto/dxos/client/services';
|
|
30
|
-
import { type
|
|
31
|
-
import { type ThemeMode } from '@dxos/ui-types';
|
|
30
|
+
import { type ChromaticPalette, type ThemeMode } from '@dxos/ui-types';
|
|
32
31
|
import { hexToHue, isTruthy } from '@dxos/util';
|
|
33
32
|
|
|
34
33
|
import { baseTheme, createFontTheme, editorGutter } from '../styles';
|
|
@@ -198,13 +197,13 @@ export type ThemeExtensionsOptions = {
|
|
|
198
197
|
|
|
199
198
|
export const grow: ThemeExtensionsOptions['slots'] = {
|
|
200
199
|
editor: {
|
|
201
|
-
className: '
|
|
200
|
+
className: 'h-full w-full',
|
|
202
201
|
},
|
|
203
202
|
} as const;
|
|
204
203
|
|
|
205
204
|
export const fullWidth: ThemeExtensionsOptions['slots'] = {
|
|
206
205
|
editor: {
|
|
207
|
-
className: '
|
|
206
|
+
className: 'w-full',
|
|
208
207
|
},
|
|
209
208
|
} as const;
|
|
210
209
|
|
|
@@ -263,7 +262,7 @@ export const createDataExtensions = <T>({ id, text, messenger, identity }: DataE
|
|
|
263
262
|
|
|
264
263
|
if (messenger && identity) {
|
|
265
264
|
const peerId = identity?.identityKey.toHex();
|
|
266
|
-
const hue = (identity?.profile?.data?.hue as
|
|
265
|
+
const hue = (identity?.profile?.data?.hue as ChromaticPalette | undefined) ?? hexToHue(peerId ?? '0');
|
|
267
266
|
extensions.push(
|
|
268
267
|
awareness(
|
|
269
268
|
new SpaceAwarenessProvider({
|
|
@@ -271,8 +270,8 @@ export const createDataExtensions = <T>({ id, text, messenger, identity }: DataE
|
|
|
271
270
|
channel: `awareness.${id}`,
|
|
272
271
|
peerId: identity.identityKey.toHex(),
|
|
273
272
|
info: {
|
|
274
|
-
darkColor: `var(--
|
|
275
|
-
lightColor: `var(--
|
|
273
|
+
darkColor: `var(--color-${hue}-border)`,
|
|
274
|
+
lightColor: `var(--color-${hue}-border)`,
|
|
276
275
|
displayName: identity.profile?.displayName ?? generateName(identity.identityKey.toHex()),
|
|
277
276
|
},
|
|
278
277
|
}),
|
|
@@ -19,10 +19,10 @@ export const folding = (): Extension => {
|
|
|
19
19
|
foldGutter({
|
|
20
20
|
markerDOM: (open) => {
|
|
21
21
|
return Domino.of('div')
|
|
22
|
-
.classNames('flex
|
|
22
|
+
.classNames('flex h-full justify-center items-center')
|
|
23
23
|
.children(
|
|
24
24
|
Domino.of('svg', Domino.SVG)
|
|
25
|
-
.classNames(mx('
|
|
25
|
+
.classNames(mx('w-4 h-4 cursor-pointer', open && 'rotate-90'))
|
|
26
26
|
.children(
|
|
27
27
|
Domino.of('use', Domino.SVG).attributes({
|
|
28
28
|
href: Domino.icon('ph--caret-right--regular'),
|
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,8 @@ export * from './modes';
|
|
|
26
26
|
export * from './outliner';
|
|
27
27
|
export * from './preview';
|
|
28
28
|
export * from './replacer';
|
|
29
|
+
export * from './scroller';
|
|
29
30
|
export * from './selection';
|
|
30
|
-
export * from './scrolling';
|
|
31
31
|
export * from './state';
|
|
32
32
|
export * from './submit';
|
|
33
33
|
export * from './tags';
|
|
@@ -56,7 +56,6 @@ class LinkButton extends WidgetType {
|
|
|
56
56
|
return this.url === other.url;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// TODO(burdon): Create icon and link directly without react?
|
|
60
59
|
override toDOM(view: EditorView) {
|
|
61
60
|
const el = document.createElement('span');
|
|
62
61
|
this.render(el, { url: this.url }, view);
|
|
@@ -251,7 +250,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
|
251
250
|
from: mark.from,
|
|
252
251
|
to: mark.from + len,
|
|
253
252
|
deco: Decoration.replace({
|
|
254
|
-
widget: new TextWidget(num, markdownTheme.heading(level)),
|
|
253
|
+
widget: new TextWidget(num, markdownTheme.heading(level).className),
|
|
255
254
|
}),
|
|
256
255
|
});
|
|
257
256
|
}
|
|
@@ -457,7 +456,9 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
|
457
456
|
from: marks[1].from,
|
|
458
457
|
to: node.to,
|
|
459
458
|
deco: options.renderLinkButton
|
|
460
|
-
? Decoration.replace({
|
|
459
|
+
? Decoration.replace({
|
|
460
|
+
widget: new LinkButton(url, options.renderLinkButton),
|
|
461
|
+
})
|
|
461
462
|
: hide,
|
|
462
463
|
});
|
|
463
464
|
}
|
|
@@ -9,6 +9,12 @@ import { type MarkdownConfig, Table } from '@lezer/markdown';
|
|
|
9
9
|
|
|
10
10
|
import { fontBody, markdownTheme } from '../../styles';
|
|
11
11
|
|
|
12
|
+
const styles = {
|
|
13
|
+
code: 'font-mono no-underline! text-cm-code',
|
|
14
|
+
codeMark: 'font-mono text-cm-code-mark',
|
|
15
|
+
mark: 'opacity-50',
|
|
16
|
+
};
|
|
17
|
+
|
|
12
18
|
/**
|
|
13
19
|
* Custom tags defined and processed by the GFM lezer extension.
|
|
14
20
|
* https://github.com/lezer-parser/markdown
|
|
@@ -114,7 +120,7 @@ export const markdownHighlightStyle = (_options: HighlightOptions = {}) => {
|
|
|
114
120
|
markdownTags.LinkReference,
|
|
115
121
|
markdownTags.ListMark,
|
|
116
122
|
],
|
|
117
|
-
class:
|
|
123
|
+
class: styles.mark,
|
|
118
124
|
},
|
|
119
125
|
|
|
120
126
|
// Markdown marks.
|
|
@@ -126,7 +132,7 @@ export const markdownHighlightStyle = (_options: HighlightOptions = {}) => {
|
|
|
126
132
|
markdownTags.QuoteMark,
|
|
127
133
|
markdownTags.EmphasisMark,
|
|
128
134
|
],
|
|
129
|
-
class:
|
|
135
|
+
class: styles.mark,
|
|
130
136
|
},
|
|
131
137
|
|
|
132
138
|
// E.g., code block language (after ```).
|
|
@@ -136,7 +142,7 @@ export const markdownHighlightStyle = (_options: HighlightOptions = {}) => {
|
|
|
136
142
|
tags.function(tags.variableName),
|
|
137
143
|
tags.labelName,
|
|
138
144
|
],
|
|
139
|
-
class:
|
|
145
|
+
class: styles.codeMark,
|
|
140
146
|
},
|
|
141
147
|
|
|
142
148
|
// Fonts.
|
|
@@ -145,13 +151,21 @@ export const markdownHighlightStyle = (_options: HighlightOptions = {}) => {
|
|
|
145
151
|
class: 'font-mono',
|
|
146
152
|
},
|
|
147
153
|
|
|
148
|
-
// Headings
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
// Headings — use CSS properties only (no class:) so CodeMirror generates scoped CSS via
|
|
155
|
+
// StyleModule that overrides vscodeDarkStyle's t.heading rule. When class: is present,
|
|
156
|
+
// HighlightStyle silently ignores all other CSS properties (they're mutually exclusive).
|
|
157
|
+
// Font sizes use Tailwind v4 CSS variables so nothing is hardcoded.
|
|
158
|
+
{
|
|
159
|
+
tag: tags.heading,
|
|
160
|
+
color: 'var(--color-cm-heading) !important',
|
|
161
|
+
fontWeight: '300',
|
|
162
|
+
},
|
|
163
|
+
{ tag: tags.heading1, ...markdownTheme.heading(1) },
|
|
164
|
+
{ tag: tags.heading2, ...markdownTheme.heading(2) },
|
|
165
|
+
{ tag: tags.heading3, ...markdownTheme.heading(3) },
|
|
166
|
+
{ tag: tags.heading4, ...markdownTheme.heading(4) },
|
|
167
|
+
{ tag: tags.heading5, ...markdownTheme.heading(5) },
|
|
168
|
+
{ tag: tags.heading6, ...markdownTheme.heading(6) },
|
|
155
169
|
|
|
156
170
|
// Emphasis.
|
|
157
171
|
{ tag: tags.emphasis, class: 'italic' },
|
|
@@ -165,7 +179,7 @@ export const markdownHighlightStyle = (_options: HighlightOptions = {}) => {
|
|
|
165
179
|
// IMPORTANT: Therefore, the fenced code block will use the base editor font unless changed by an extension.
|
|
166
180
|
{
|
|
167
181
|
tag: [markdownTags.CodeText, markdownTags.InlineCode],
|
|
168
|
-
class:
|
|
182
|
+
class: styles.code,
|
|
169
183
|
},
|
|
170
184
|
|
|
171
185
|
{
|
|
@@ -12,39 +12,33 @@ import { tooltipContent } from '@dxos/ui-theme';
|
|
|
12
12
|
import { type RenderCallback } from '../../types';
|
|
13
13
|
|
|
14
14
|
export const linkTooltip = (renderTooltip: RenderCallback<{ url: string }>) => {
|
|
15
|
-
return hoverTooltip(
|
|
16
|
-
(view
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
15
|
+
return hoverTooltip((view, pos, side) => {
|
|
16
|
+
const syntax = syntaxTree(view.state).resolveInner(pos, side);
|
|
17
|
+
let link = null;
|
|
18
|
+
for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
|
|
19
|
+
link = node.name === 'Link' ? node : null;
|
|
20
|
+
}
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const url = link && link.getChild('URL');
|
|
23
|
+
if (!url || !link) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
27
26
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
// NOTE: 0 = default of 300ms.
|
|
47
|
-
hoverTime: 1,
|
|
48
|
-
},
|
|
49
|
-
);
|
|
27
|
+
const urlText = view.state.sliceDoc(url.from, url.to);
|
|
28
|
+
if (urlText.startsWith('dxn')) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
pos: link.from,
|
|
34
|
+
end: link.to,
|
|
35
|
+
above: true,
|
|
36
|
+
create: () => {
|
|
37
|
+
const el = document.createElement('div');
|
|
38
|
+
el.className = tooltipContent({});
|
|
39
|
+
renderTooltip(el, { url: urlText }, view);
|
|
40
|
+
return { dom: el, offset: { x: 0, y: 4 } };
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
});
|
|
50
44
|
};
|
|
@@ -18,7 +18,7 @@ export const formattingStyles = EditorView.theme({
|
|
|
18
18
|
width: '100%',
|
|
19
19
|
height: '0',
|
|
20
20
|
verticalAlign: 'middle',
|
|
21
|
-
borderTop: '1px solid var(--
|
|
21
|
+
borderTop: '1px solid var(--color-cm-separator)',
|
|
22
22
|
opacity: 0.5,
|
|
23
23
|
},
|
|
24
24
|
|
|
@@ -43,8 +43,8 @@ export const formattingStyles = EditorView.theme({
|
|
|
43
43
|
* Blockquote.
|
|
44
44
|
*/
|
|
45
45
|
'& .cm-blockquote': {
|
|
46
|
-
background: 'var(--
|
|
47
|
-
borderLeft: '2px solid var(--
|
|
46
|
+
background: 'var(--color-cm-codeblock)',
|
|
47
|
+
borderLeft: '2px solid var(--color-cm-separator)',
|
|
48
48
|
paddingLeft: '1rem',
|
|
49
49
|
margin: '0',
|
|
50
50
|
},
|
|
@@ -56,7 +56,7 @@ export const formattingStyles = EditorView.theme({
|
|
|
56
56
|
fontFamily: fontMono,
|
|
57
57
|
},
|
|
58
58
|
'& .cm-codeblock-line': {
|
|
59
|
-
background: 'var(--
|
|
59
|
+
background: 'var(--color-cm-codeblock)',
|
|
60
60
|
paddingInline: '1rem !important',
|
|
61
61
|
},
|
|
62
62
|
'& .cm-codeblock-start': {
|
|
@@ -92,8 +92,8 @@ export const formattingStyles = EditorView.theme({
|
|
|
92
92
|
'.cm-table-head': {
|
|
93
93
|
padding: '2px 16px 2px 0px',
|
|
94
94
|
textAlign: 'left',
|
|
95
|
-
borderBottom: '1px solid var(--
|
|
96
|
-
color: 'var(--
|
|
95
|
+
borderBottom: '1px solid var(--color-cm-separator)',
|
|
96
|
+
color: 'var(--color-subdued)',
|
|
97
97
|
},
|
|
98
98
|
'.cm-table-cell': {
|
|
99
99
|
padding: '2px 16px 2px 0px',
|