@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
|
@@ -61,7 +61,7 @@ export const outliner = (_options: OutlinerProps = {}): Extension => [
|
|
|
61
61
|
decorateMarkdown({ listPaddingLeft: 8 }),
|
|
62
62
|
|
|
63
63
|
// Researve space for menu.
|
|
64
|
-
EditorView.contentAttributes.of({ class: '
|
|
64
|
+
EditorView.contentAttributes.of({ class: 'w-full !mr-[3rem]' }),
|
|
65
65
|
];
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -157,10 +157,10 @@ const decorations = () => [
|
|
|
157
157
|
},
|
|
158
158
|
|
|
159
159
|
'.cm-list-item-focused': {
|
|
160
|
-
borderColor: 'var(--
|
|
160
|
+
borderColor: 'var(--color-neutral-focus-indicator)',
|
|
161
161
|
},
|
|
162
162
|
'&:focus-within .cm-list-item-selected': {
|
|
163
|
-
borderColor: 'var(--
|
|
163
|
+
borderColor: 'var(--color-separator)',
|
|
164
164
|
},
|
|
165
165
|
}),
|
|
166
166
|
),
|
|
@@ -16,7 +16,7 @@ export type PreviewLinkRef = {
|
|
|
16
16
|
suggest?: boolean;
|
|
17
17
|
block?: boolean;
|
|
18
18
|
label: string;
|
|
19
|
-
|
|
19
|
+
dxn: string;
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
export type PreviewLinkTarget = {
|
|
@@ -119,13 +119,13 @@ export const getLinkRef = (state: EditorState, node: SyntaxNode): PreviewLinkRef
|
|
|
119
119
|
const mark = node.getChildren('LinkMark');
|
|
120
120
|
const urlNode = node.getChild('URL');
|
|
121
121
|
if (mark && urlNode) {
|
|
122
|
-
const
|
|
123
|
-
if (
|
|
122
|
+
const dxn = state.sliceDoc(urlNode.from, urlNode.to);
|
|
123
|
+
if (dxn.startsWith('dxn:')) {
|
|
124
124
|
const label = state.sliceDoc(mark[0].to, mark[1].from);
|
|
125
125
|
return {
|
|
126
126
|
block: state.sliceDoc(mark[0].from, mark[0].from + 1) === '!',
|
|
127
127
|
label,
|
|
128
|
-
|
|
128
|
+
dxn,
|
|
129
129
|
};
|
|
130
130
|
}
|
|
131
131
|
}
|
|
@@ -148,14 +148,14 @@ class PreviewInlineWidget extends WidgetType {
|
|
|
148
148
|
// }
|
|
149
149
|
|
|
150
150
|
override eq(other: this) {
|
|
151
|
-
return this._link.
|
|
151
|
+
return this._link.dxn === other._link.dxn && this._link.label === other._link.label;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
154
|
override toDOM(_view: EditorView) {
|
|
155
155
|
const root = document.createElement('dx-anchor');
|
|
156
156
|
root.classList.add('dx-tag--anchor');
|
|
157
157
|
root.textContent = this._link.label;
|
|
158
|
-
root.setAttribute('
|
|
158
|
+
root.setAttribute('dxn', this._link.dxn);
|
|
159
159
|
return root;
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -177,12 +177,12 @@ class PreviewBlockWidget extends WidgetType {
|
|
|
177
177
|
// }
|
|
178
178
|
|
|
179
179
|
override eq(other: this) {
|
|
180
|
-
return this._link.
|
|
180
|
+
return this._link.dxn === other._link.dxn;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
183
|
override toDOM(_view: EditorView) {
|
|
184
184
|
const root = document.createElement('div');
|
|
185
|
-
root.classList.add('cm-preview-block', 'density-fine');
|
|
185
|
+
root.classList.add('cm-preview-block', 'dx-density-fine');
|
|
186
186
|
this._options.addBlockContainer?.({ link: this._link, el: root });
|
|
187
187
|
return root;
|
|
188
188
|
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { StateEffect } from '@codemirror/state';
|
|
6
|
+
import { EditorView, ViewPlugin } from '@codemirror/view';
|
|
7
|
+
|
|
8
|
+
import { log } from '@dxos/log';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parameters for the scroll to line effect.
|
|
12
|
+
*/
|
|
13
|
+
export type ScrollToProps = {
|
|
14
|
+
/**
|
|
15
|
+
* Zero-based line number; -1 for end of document.
|
|
16
|
+
* NOTE: view.state.doc.lineAt() uses 1-based line numbers.
|
|
17
|
+
*/
|
|
18
|
+
line: number;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Additional offset from the target line in pixels.
|
|
22
|
+
* Positive values scroll past the line, negative values stop before it.
|
|
23
|
+
* @default 0
|
|
24
|
+
*/
|
|
25
|
+
offset?: number;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Position of the target line in the viewport.
|
|
29
|
+
* - 'start': Line appears at the start (top) of the screen
|
|
30
|
+
* - 'end': Line appears at the end (bottom) of the screen
|
|
31
|
+
* @default 'start'
|
|
32
|
+
*/
|
|
33
|
+
position?: 'start' | 'end';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Whether to use smooth scrolling.
|
|
37
|
+
* @default 'instant'
|
|
38
|
+
*/
|
|
39
|
+
behavior?: ScrollBehavior;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
/** Scroll to a specific line. */
|
|
43
|
+
export const scrollerLineEffect = StateEffect.define<ScrollToProps>();
|
|
44
|
+
|
|
45
|
+
/** Start/stop crawling the end of the document. */
|
|
46
|
+
export const scrollerCrawlEffect = StateEffect.define<boolean>();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Helper function to scroll to a specific line.
|
|
50
|
+
* This is a convenience function that can be used directly with an EditorView.
|
|
51
|
+
*/
|
|
52
|
+
export const scrollToLine = (view: EditorView, options: ScrollToProps) => {
|
|
53
|
+
view.dispatch({
|
|
54
|
+
effects: scrollerLineEffect.of(options),
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type ScrollerOptions = {
|
|
59
|
+
/** Threshold in px to trigger scroll from bottom. */
|
|
60
|
+
overScroll?: number;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Extension that provides smooth scrolling to specific lines in the editor.
|
|
65
|
+
*/
|
|
66
|
+
export const scroller = ({ overScroll = 0 }: ScrollerOptions = {}) => {
|
|
67
|
+
// ViewPlugin to manage scroll animations.
|
|
68
|
+
const scrollPlugin = ViewPlugin.fromClass(
|
|
69
|
+
class ScrollerPlugin {
|
|
70
|
+
private readonly crawler: ReturnType<typeof createCrawler>;
|
|
71
|
+
constructor(private readonly view: EditorView) {
|
|
72
|
+
this.crawler = createCrawler(this.view);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// No-op.
|
|
76
|
+
destroy() {
|
|
77
|
+
this.crawler.cancel();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
cancel() {
|
|
81
|
+
this.crawler.cancel();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
crawl(start = false) {
|
|
85
|
+
if (start) {
|
|
86
|
+
this.crawler.scroll();
|
|
87
|
+
} else {
|
|
88
|
+
this.crawler.cancel();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
scroll({ line, offset = 0, position, behavior = 'instant' }: ScrollToProps) {
|
|
93
|
+
const { scrollTop, scrollHeight, clientHeight } = this.view.scrollDOM;
|
|
94
|
+
const scrollerRect = this.view.scrollDOM.getBoundingClientRect();
|
|
95
|
+
const doc = this.view.state.doc;
|
|
96
|
+
|
|
97
|
+
let targetScrollTop = scrollHeight - clientHeight + offset;
|
|
98
|
+
if (line >= 0 && line <= doc.lines - 1) {
|
|
99
|
+
const lineStart = doc.line(line + 1).from;
|
|
100
|
+
const coords = this.view.coordsAtPos(lineStart);
|
|
101
|
+
if (coords) {
|
|
102
|
+
// Calculate target scroll position based on position option.
|
|
103
|
+
const currentScrollTop = scrollTop;
|
|
104
|
+
const maxScrollTop = scrollHeight - clientHeight;
|
|
105
|
+
|
|
106
|
+
if (position === 'end') {
|
|
107
|
+
// Position line at end (bottom) of viewport.
|
|
108
|
+
// Calculate how far down we need to scroll so the line's bottom aligns with viewport bottom.
|
|
109
|
+
targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + offset;
|
|
110
|
+
} else {
|
|
111
|
+
// Default: position line at start (top) of viewport.
|
|
112
|
+
targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + offset;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Clamp to valid scroll range.
|
|
116
|
+
targetScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
requestAnimationFrame(() => {
|
|
121
|
+
this.view.scrollDOM.scrollTo({ top: targetScrollTop, behavior });
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return [
|
|
128
|
+
scrollPlugin,
|
|
129
|
+
|
|
130
|
+
// Listen for effect.s
|
|
131
|
+
EditorView.updateListener.of((update) => {
|
|
132
|
+
update.transactions.forEach((transaction) => {
|
|
133
|
+
try {
|
|
134
|
+
const plugin = update.view.plugin(scrollPlugin);
|
|
135
|
+
if (plugin) {
|
|
136
|
+
for (const effect of transaction.effects) {
|
|
137
|
+
if (effect.is(scrollerCrawlEffect)) {
|
|
138
|
+
plugin.crawl(effect.value);
|
|
139
|
+
} else if (effect.is(scrollerLineEffect)) {
|
|
140
|
+
plugin.scroll(effect.value);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} catch (err) {
|
|
145
|
+
log.catch(err);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}),
|
|
149
|
+
|
|
150
|
+
// Styles.
|
|
151
|
+
EditorView.theme({
|
|
152
|
+
'.cm-content': {
|
|
153
|
+
paddingBottom: `${overScroll}px`,
|
|
154
|
+
},
|
|
155
|
+
'.cm-scroller': {
|
|
156
|
+
overflowAnchor: 'none',
|
|
157
|
+
paddingBottom: '0',
|
|
158
|
+
},
|
|
159
|
+
'.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {
|
|
160
|
+
display: 'none',
|
|
161
|
+
},
|
|
162
|
+
'.cm-scroller::-webkit-scrollbar-thumb': {
|
|
163
|
+
background: 'transparent',
|
|
164
|
+
transition: 'background 0.15s',
|
|
165
|
+
},
|
|
166
|
+
'&:hover .cm-scroller::-webkit-scrollbar-thumb': {
|
|
167
|
+
background: 'var(--color-scrollbar-thumb)',
|
|
168
|
+
},
|
|
169
|
+
'.cm-scroll-button': {
|
|
170
|
+
position: 'absolute',
|
|
171
|
+
bottom: '0.5rem',
|
|
172
|
+
right: '1rem',
|
|
173
|
+
},
|
|
174
|
+
}),
|
|
175
|
+
];
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Creates a smooth crawler that follows the live bottom of a CodeMirror 6 EditorView.
|
|
180
|
+
*
|
|
181
|
+
* Each animation frame the step is:
|
|
182
|
+
* step = clamp(|delta| * k, minStep, maxStep) -- but never more than |delta|
|
|
183
|
+
*
|
|
184
|
+
* Capping at |delta| is critical: it prevents overshoot, which would otherwise cause
|
|
185
|
+
* oscillation when the proportional step (or minStep) exceeds the remaining distance.
|
|
186
|
+
* The crawler settles exactly at the target with no bounce.
|
|
187
|
+
*
|
|
188
|
+
* @param k Fraction of remaining distance moved each frame (0 < k < 1).
|
|
189
|
+
* @param minStep Min step size in px; prevents stalling on large distances.
|
|
190
|
+
* @param maxStep Max step size in px.
|
|
191
|
+
* @param targetDelta Snap-to-target threshold in px.
|
|
192
|
+
*/
|
|
193
|
+
export function createCrawler(view: EditorView, k = 0.3, maxStep = 2, targetDelta = 0.5) {
|
|
194
|
+
const el = view.scrollDOM;
|
|
195
|
+
|
|
196
|
+
let currentTop = 0; // Float-precision position; avoids browser integer-rounding of scrollTop.
|
|
197
|
+
let rafId: number | null = null;
|
|
198
|
+
|
|
199
|
+
function frame() {
|
|
200
|
+
// Recompute each frame so the animation chases a live-updating document's bottom.
|
|
201
|
+
const targetTop = el.scrollHeight - el.clientHeight;
|
|
202
|
+
const delta = targetTop - currentTop;
|
|
203
|
+
const absDelta = Math.abs(delta);
|
|
204
|
+
if (absDelta < targetDelta) {
|
|
205
|
+
el.scrollTop = targetTop;
|
|
206
|
+
currentTop = targetTop;
|
|
207
|
+
rafId = null;
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Clamp step to [minStep, maxStep], then cap at absDelta to prevent overshoot.
|
|
212
|
+
const step = Math.sign(delta) * Math.min(absDelta, Math.max(1, Math.min(absDelta * k, maxStep)));
|
|
213
|
+
currentTop += step;
|
|
214
|
+
el.scrollTop = currentTop;
|
|
215
|
+
rafId = requestAnimationFrame(frame);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
scroll: () => {
|
|
220
|
+
if (rafId === null) {
|
|
221
|
+
currentTop = el.scrollTop;
|
|
222
|
+
rafId = requestAnimationFrame(frame);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
cancel: () => {
|
|
226
|
+
if (rafId !== null) {
|
|
227
|
+
cancelAnimationFrame(rafId);
|
|
228
|
+
rafId = null;
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
}
|
|
@@ -114,7 +114,7 @@ class CursorWidget extends WidgetType {
|
|
|
114
114
|
*/
|
|
115
115
|
const fadeIn = (options: { removalDelay?: number } = {}): Extension => {
|
|
116
116
|
const FADE_IN_DURATION = 1_000; // ms.
|
|
117
|
-
const DEFAULT_REMOVAL_DELAY =
|
|
117
|
+
const DEFAULT_REMOVAL_DELAY = 3_000; // ms.
|
|
118
118
|
const removalDelay = options.removalDelay ?? DEFAULT_REMOVAL_DELAY;
|
|
119
119
|
|
|
120
120
|
// Effect to remove a specific decoration by range.
|
|
@@ -21,7 +21,7 @@ import { log } from '@dxos/log';
|
|
|
21
21
|
|
|
22
22
|
import { type Range } from '../../types';
|
|
23
23
|
import { decorationSetToArray } from '../../util';
|
|
24
|
-
import {
|
|
24
|
+
import { scrollerLineEffect } from '../scroller';
|
|
25
25
|
|
|
26
26
|
import { nodeToJson } from './xml-util';
|
|
27
27
|
|
|
@@ -268,7 +268,7 @@ const createNavigationEffectPlugin = (
|
|
|
268
268
|
const line = view.state.doc.lineAt(widget?.from ?? 0);
|
|
269
269
|
view.dispatch({
|
|
270
270
|
selection: { anchor: line.from, head: line.from },
|
|
271
|
-
effects:
|
|
271
|
+
effects: scrollerLineEffect.of({ line: line.number - 1, offset: -16 }),
|
|
272
272
|
});
|
|
273
273
|
|
|
274
274
|
continue;
|
|
@@ -294,13 +294,13 @@ const createNavigationEffectPlugin = (
|
|
|
294
294
|
const line = view.state.doc.lineAt(widget?.from);
|
|
295
295
|
view.dispatch({
|
|
296
296
|
selection: { anchor: line.to, head: line.to },
|
|
297
|
-
effects:
|
|
297
|
+
effects: scrollerLineEffect.of({ line: line.number - 1, offset: -16 }),
|
|
298
298
|
});
|
|
299
299
|
} else {
|
|
300
300
|
const line = view.state.doc.lineAt(view.state.doc.length);
|
|
301
301
|
view.dispatch({
|
|
302
302
|
selection: { anchor: line.to, head: line.to },
|
|
303
|
-
effects:
|
|
303
|
+
effects: scrollerLineEffect.of({ line: line.number - 1, position: 'end' }),
|
|
304
304
|
});
|
|
305
305
|
}
|
|
306
306
|
|
|
@@ -359,6 +359,9 @@ const createWidgetDecorationsField = (registry: XmlWidgetRegistry = {}, notifier
|
|
|
359
359
|
// Check for reset effect.
|
|
360
360
|
for (const effect of tr.effects) {
|
|
361
361
|
if (effect.is(xmlTagResetEffect)) {
|
|
362
|
+
if (tr.docChanged) {
|
|
363
|
+
return buildDecorations(tr.state, { from: 0, to: tr.state.doc.length }, registry, notifier);
|
|
364
|
+
}
|
|
362
365
|
return { from: 0, decorations: Decoration.none };
|
|
363
366
|
}
|
|
364
367
|
}
|
package/src/styles/index.ts
CHANGED
package/src/styles/theme.ts
CHANGED
|
@@ -5,7 +5,60 @@
|
|
|
5
5
|
import { type Extension } from '@codemirror/state';
|
|
6
6
|
import { EditorView } from '@codemirror/view';
|
|
7
7
|
|
|
8
|
-
import {
|
|
8
|
+
import { mx } from '@dxos/ui-theme';
|
|
9
|
+
|
|
10
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
11
|
+
|
|
12
|
+
// https://tailwindcss.com/docs/font-weight
|
|
13
|
+
const headings: Record<HeadingLevel, { className: string; fontSize: string; lineHeight: string }> = {
|
|
14
|
+
1: {
|
|
15
|
+
className: 'text-4xl',
|
|
16
|
+
fontSize: 'var(--text-4xl)',
|
|
17
|
+
lineHeight: 'var(--text-4xl--line-height)',
|
|
18
|
+
},
|
|
19
|
+
2: {
|
|
20
|
+
className: 'text-3xl',
|
|
21
|
+
fontSize: 'var(--text-3xl)',
|
|
22
|
+
lineHeight: 'var(--text-3xl--line-height)',
|
|
23
|
+
},
|
|
24
|
+
3: {
|
|
25
|
+
className: 'text-2xl',
|
|
26
|
+
fontSize: 'var(--text-2xl)',
|
|
27
|
+
lineHeight: 'var(--text-2xl--line-height)',
|
|
28
|
+
},
|
|
29
|
+
4: {
|
|
30
|
+
className: 'text-xl',
|
|
31
|
+
fontSize: 'var(--text-xl)',
|
|
32
|
+
lineHeight: 'var(--text-xl--line-height)',
|
|
33
|
+
},
|
|
34
|
+
5: {
|
|
35
|
+
className: 'text-lg',
|
|
36
|
+
fontSize: 'var(--text-lg)',
|
|
37
|
+
lineHeight: 'var(--text-lg--line-height)',
|
|
38
|
+
},
|
|
39
|
+
6: {
|
|
40
|
+
className: 'text-base',
|
|
41
|
+
fontSize: 'var(--text-base)',
|
|
42
|
+
lineHeight: 'var(--text-base--line-height)',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const markdownTheme = {
|
|
47
|
+
code: 'font-mono no-underline! text-cm-code',
|
|
48
|
+
codeMark: 'font-mono text-cm-code-mark',
|
|
49
|
+
mark: 'opacity-50',
|
|
50
|
+
heading: (level: HeadingLevel) => ({
|
|
51
|
+
className: mx(headings[level].className, 'font-light text-cm-heading'),
|
|
52
|
+
color: 'var(--color-cm-heading) !important',
|
|
53
|
+
lineHeight: headings[level].lineHeight,
|
|
54
|
+
fontSize: headings[level].fontSize,
|
|
55
|
+
fontWeight: '100 !important',
|
|
56
|
+
}),
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Font families matching --font-body and --font-mono in theme.css.
|
|
60
|
+
export const fontBody = 'Inter Variable, ui-sans-serif, system-ui, sans-serif';
|
|
61
|
+
export const fontMono = 'JetBrains Mono Variable, ui-monospace, Cascadia Code, Source Code Pro, monospace';
|
|
9
62
|
|
|
10
63
|
/**
|
|
11
64
|
* Global base theme.
|
|
@@ -43,6 +96,9 @@ import { fontBody, fontMono } from './tokens';
|
|
|
43
96
|
* </div>
|
|
44
97
|
*/
|
|
45
98
|
export const baseTheme = EditorView.baseTheme({
|
|
99
|
+
/**
|
|
100
|
+
* Outer frame.
|
|
101
|
+
*/
|
|
46
102
|
'&': {},
|
|
47
103
|
'&.cm-focused': {
|
|
48
104
|
outline: 'none',
|
|
@@ -52,7 +108,18 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
52
108
|
* Scroller
|
|
53
109
|
*/
|
|
54
110
|
'.cm-scroller': {
|
|
55
|
-
|
|
111
|
+
overflowAnchor: 'none',
|
|
112
|
+
},
|
|
113
|
+
'.cm-scroller::-webkit-scrollbar': {
|
|
114
|
+
width: '8px',
|
|
115
|
+
},
|
|
116
|
+
'.cm-scroller::-webkit-scrollbar-track': {},
|
|
117
|
+
'.cm-scroller::-webkit-scrollbar-thumb': {
|
|
118
|
+
background: 'transparent',
|
|
119
|
+
transition: 'background 0.15s',
|
|
120
|
+
},
|
|
121
|
+
'&:hover .cm-scroller::-webkit-scrollbar-thumb': {
|
|
122
|
+
background: 'var(--color-scrollbar-thumb)',
|
|
56
123
|
},
|
|
57
124
|
|
|
58
125
|
/**
|
|
@@ -76,8 +143,8 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
76
143
|
'.cm-gutter': {},
|
|
77
144
|
'.cm-gutter.cm-lineNumbers': {
|
|
78
145
|
paddingRight: '4px',
|
|
79
|
-
borderRight: '1px solid var(--
|
|
80
|
-
color: 'var(--
|
|
146
|
+
borderRight: '1px solid var(--color-subdued-separator)',
|
|
147
|
+
color: 'var(--color-subdued)',
|
|
81
148
|
},
|
|
82
149
|
'.cm-gutter.cm-lineNumbers .cm-gutterElement': {
|
|
83
150
|
minWidth: '40px',
|
|
@@ -98,27 +165,27 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
98
165
|
paddingInline: 0,
|
|
99
166
|
},
|
|
100
167
|
'.cm-activeLine': {
|
|
101
|
-
background: 'var(--
|
|
168
|
+
background: 'var(--color-cm-active-line)',
|
|
102
169
|
},
|
|
103
170
|
|
|
104
171
|
/**
|
|
105
172
|
* Cursor (layer).
|
|
106
173
|
*/
|
|
107
174
|
'.cm-cursor, .cm-dropCursor': {
|
|
108
|
-
borderLeft: '2px solid var(--
|
|
175
|
+
borderLeft: '2px solid var(--color-cm-cursor)',
|
|
109
176
|
},
|
|
110
177
|
'.cm-placeholder': {
|
|
111
|
-
color: 'var(--
|
|
178
|
+
color: 'var(--color-placeholder)',
|
|
112
179
|
},
|
|
113
180
|
|
|
114
181
|
/**
|
|
115
182
|
* Selection (layer).
|
|
116
183
|
*/
|
|
117
184
|
'.cm-selectionBackground': {
|
|
118
|
-
background: 'var(--
|
|
185
|
+
background: 'var(--color-cm-selection)',
|
|
119
186
|
},
|
|
120
187
|
'&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
|
|
121
|
-
background: 'var(--
|
|
188
|
+
background: 'var(--color-cm-focused-selection)',
|
|
122
189
|
},
|
|
123
190
|
|
|
124
191
|
/**
|
|
@@ -129,8 +196,8 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
129
196
|
margin: '0 -3px',
|
|
130
197
|
padding: '3px',
|
|
131
198
|
borderRadius: '3px',
|
|
132
|
-
background: 'var(--
|
|
133
|
-
color: 'var(--
|
|
199
|
+
background: 'var(--color-cm-highlight-surface)',
|
|
200
|
+
color: 'var(--color-cm-highlight)',
|
|
134
201
|
},
|
|
135
202
|
'.cm-searchMatch-selected': {
|
|
136
203
|
textDecoration: 'underline',
|
|
@@ -142,21 +209,30 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
142
209
|
'.cm-link': {
|
|
143
210
|
textDecorationLine: 'underline',
|
|
144
211
|
textDecorationThickness: '1px',
|
|
145
|
-
textDecorationColor: 'var(--
|
|
212
|
+
textDecorationColor: 'var(--color-separator)',
|
|
146
213
|
textUnderlineOffset: '2px',
|
|
147
214
|
borderRadius: '.125rem',
|
|
148
215
|
},
|
|
149
216
|
'.cm-link > span': {
|
|
150
|
-
color: 'var(--
|
|
217
|
+
color: 'var(--color-accent-text)',
|
|
218
|
+
},
|
|
219
|
+
'.cm-link > span:hover': {
|
|
220
|
+
color: 'var(--color-accent-text-hover)',
|
|
151
221
|
},
|
|
152
222
|
|
|
153
223
|
/**
|
|
154
224
|
* Tooltip.
|
|
155
225
|
*/
|
|
156
226
|
'.cm-tooltip': {
|
|
157
|
-
background: 'var(--
|
|
227
|
+
background: 'var(--color-modal-surface)',
|
|
158
228
|
},
|
|
159
229
|
'.cm-tooltip-below': {},
|
|
230
|
+
'.cm-tooltip-hover': {
|
|
231
|
+
background: 'var(--color-modal-surface)',
|
|
232
|
+
border: '1px solid var(--color-separator)',
|
|
233
|
+
borderRadius: '4px',
|
|
234
|
+
overflow: 'hidden',
|
|
235
|
+
},
|
|
160
236
|
|
|
161
237
|
/**
|
|
162
238
|
* Autocomplete.
|
|
@@ -165,7 +241,7 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
165
241
|
'.cm-tooltip.cm-tooltip-autocomplete': {
|
|
166
242
|
marginTop: '6px',
|
|
167
243
|
marginLeft: '-10px',
|
|
168
|
-
border: '2px solid var(--
|
|
244
|
+
border: '2px solid var(--color-separator)',
|
|
169
245
|
borderRadius: '4px',
|
|
170
246
|
},
|
|
171
247
|
'.cm-tooltip.cm-tooltip-autocomplete > ul': {
|
|
@@ -175,12 +251,12 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
175
251
|
padding: '4px',
|
|
176
252
|
},
|
|
177
253
|
'.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]': {
|
|
178
|
-
background: 'var(--
|
|
179
|
-
color: 'var(--
|
|
254
|
+
background: 'var(--color-active-surface)',
|
|
255
|
+
color: 'var(--color-base-surface-text)',
|
|
180
256
|
},
|
|
181
257
|
'.cm-tooltip.cm-tooltip-autocomplete > ul > completion-section': {
|
|
182
258
|
paddingLeft: '4px !important',
|
|
183
|
-
color: 'var(--
|
|
259
|
+
color: 'var(--color-base-surface-text)',
|
|
184
260
|
},
|
|
185
261
|
|
|
186
262
|
/**
|
|
@@ -190,17 +266,17 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
190
266
|
width: '360px !important',
|
|
191
267
|
margin: '-10px 1px 0 1px',
|
|
192
268
|
padding: '8px !important',
|
|
193
|
-
borderColor: 'var(--
|
|
269
|
+
borderColor: 'var(--color-separator)',
|
|
194
270
|
},
|
|
195
271
|
'.cm-completionIcon': {
|
|
196
272
|
display: 'none',
|
|
197
273
|
},
|
|
198
274
|
'.cm-completionLabel': {
|
|
199
|
-
color: 'var(--
|
|
275
|
+
color: 'var(--color-description)',
|
|
200
276
|
padding: '0 4px',
|
|
201
277
|
},
|
|
202
278
|
'.cm-completionMatchedText': {
|
|
203
|
-
color: 'var(--
|
|
279
|
+
color: 'var(--color-base-surface-text)',
|
|
204
280
|
textDecoration: 'none !important',
|
|
205
281
|
},
|
|
206
282
|
|
|
@@ -225,7 +301,7 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
225
301
|
backgroundColor: 'var(--surface-bg)',
|
|
226
302
|
},
|
|
227
303
|
'.cm-panel input, .cm-panel button, .cm-panel label': {
|
|
228
|
-
color: 'var(--
|
|
304
|
+
color: 'var(--color-subdued)',
|
|
229
305
|
fontSize: '14px',
|
|
230
306
|
all: 'unset',
|
|
231
307
|
margin: '3px !important',
|
|
@@ -233,10 +309,10 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
233
309
|
outline: '1px solid transparent',
|
|
234
310
|
},
|
|
235
311
|
'.cm-panel input, .cm-panel button': {
|
|
236
|
-
backgroundColor: 'var(--
|
|
312
|
+
backgroundColor: 'var(--color-input-surface)',
|
|
237
313
|
},
|
|
238
314
|
'.cm-panel input:focus, .cm-panel button:focus': {
|
|
239
|
-
outline: '1px solid var(--
|
|
315
|
+
outline: '1px solid var(--color-neutral-focus-indicator)',
|
|
240
316
|
},
|
|
241
317
|
'.cm-panel label': {
|
|
242
318
|
display: 'inline-flex',
|
|
@@ -249,26 +325,27 @@ export const baseTheme = EditorView.baseTheme({
|
|
|
249
325
|
height: '8px',
|
|
250
326
|
marginRight: '6px !important',
|
|
251
327
|
padding: '2px !important',
|
|
252
|
-
color: 'var(--
|
|
328
|
+
color: 'var(--color-neutral-focus-indicator)',
|
|
253
329
|
},
|
|
254
330
|
'.cm-panel button': {
|
|
255
331
|
'&:hover': {
|
|
256
|
-
|
|
332
|
+
// TODO(burdon): Replace with layer and @apply bg-accent-surface-hover
|
|
333
|
+
backgroundColor: 'var(--color-accent-surface-hover) !important',
|
|
257
334
|
},
|
|
258
335
|
'&:active': {
|
|
259
|
-
backgroundColor: 'var(--
|
|
336
|
+
backgroundColor: 'var(--color-accent-surface-hover)',
|
|
260
337
|
},
|
|
261
338
|
},
|
|
262
339
|
'.cm-panel.cm-search': {
|
|
263
340
|
padding: '4px',
|
|
264
|
-
borderTop: '1px solid var(--
|
|
341
|
+
borderTop: '1px solid var(--color-separator)',
|
|
265
342
|
},
|
|
266
343
|
});
|
|
267
344
|
|
|
268
345
|
export const editorGutter: Extension = EditorView.theme({
|
|
269
346
|
'.cm-gutters': {
|
|
270
347
|
// NOTE: Non-transparent background required to cover content if scrolling horizontally.
|
|
271
|
-
background: 'var(--
|
|
348
|
+
background: 'var(--color-base-surface) !important',
|
|
272
349
|
paddingRight: '1rem',
|
|
273
350
|
},
|
|
274
351
|
});
|
package/src/util/cursor.ts
CHANGED
|
@@ -26,7 +26,7 @@ export const overlap = (a: Range, b: Range): boolean => a.from <= b.to && a.to >
|
|
|
26
26
|
* while positive - with the next one.
|
|
27
27
|
*/
|
|
28
28
|
export interface CursorConverter {
|
|
29
|
-
toCursor(position: number, assoc?: -1 | 1
|
|
29
|
+
toCursor(position: number, assoc?: -1 | 1): string;
|
|
30
30
|
fromCursor(cursor: string): number;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { EditorView } from '@codemirror/view';
|
|
2
|
-
export declare const scrollToBottomEffect: import("@codemirror/state").StateEffectType<ScrollBehavior | undefined>;
|
|
3
|
-
export type AutoScrollOptions = {
|
|
4
|
-
/** Auto-scroll when reaches the bottom. */
|
|
5
|
-
autoScroll?: boolean;
|
|
6
|
-
/** Threshold in px to trigger scroll from bottom. */
|
|
7
|
-
threshold?: number;
|
|
8
|
-
/** Throttle time in ms. */
|
|
9
|
-
throttleDelay?: number;
|
|
10
|
-
/** Callback when auto-scrolling. */
|
|
11
|
-
onAutoScroll?: (props: {
|
|
12
|
-
view: EditorView;
|
|
13
|
-
distanceFromBottom: number;
|
|
14
|
-
}) => boolean | void;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
|
|
18
|
-
*/
|
|
19
|
-
export declare const autoScroll: ({ autoScroll, threshold, throttleDelay, onAutoScroll, }?: Partial<AutoScrollOptions>) => import("@codemirror/state").Extension[];
|
|
20
|
-
//# sourceMappingURL=autoscroll.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"autoscroll.d.ts","sourceRoot":"","sources":["../../../../src/extensions/autoscroll.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAQ1D,eAAO,MAAM,oBAAoB,yEAAmD,CAAC;AAErF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2CAA2C;IAC3C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2BAA2B;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oCAAoC;IACpC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,UAAU,CAAC;QAAC,kBAAkB,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,GAAG,IAAI,CAAC;CAC5F,CAAC;AAEF;;GAEG;AAEH,eAAO,MAAM,UAAU,GAAI,0DAKxB,OAAO,CAAC,iBAAiB,CAAM,4CAiIjC,CAAC"}
|