@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.
Files changed (61) hide show
  1. package/dist/lib/browser/index.mjs +483 -417
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +483 -417
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/defaults.d.ts +1 -1
  8. package/dist/types/src/defaults.d.ts.map +1 -1
  9. package/dist/types/src/extensions/auto-scroll.d.ts +6 -0
  10. package/dist/types/src/extensions/auto-scroll.d.ts.map +1 -0
  11. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  12. package/dist/types/src/extensions/index.d.ts +2 -2
  13. package/dist/types/src/extensions/index.d.ts.map +1 -1
  14. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  15. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  16. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  17. package/dist/types/src/extensions/preview/preview.d.ts +1 -1
  18. package/dist/types/src/extensions/scroller.d.ts +66 -0
  19. package/dist/types/src/extensions/scroller.d.ts.map +1 -0
  20. package/dist/types/src/styles/index.d.ts +0 -2
  21. package/dist/types/src/styles/index.d.ts.map +1 -1
  22. package/dist/types/src/styles/theme.d.ts +15 -0
  23. package/dist/types/src/styles/theme.d.ts.map +1 -1
  24. package/dist/types/src/util/cursor.d.ts +1 -1
  25. package/dist/types/src/util/cursor.d.ts.map +1 -1
  26. package/dist/types/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +32 -32
  28. package/src/defaults.ts +4 -4
  29. package/src/extensions/annotations.ts +1 -1
  30. package/src/extensions/auto-scroll.ts +126 -0
  31. package/src/extensions/automerge/automerge.test.tsx +2 -2
  32. package/src/extensions/blocks.ts +5 -5
  33. package/src/extensions/comments.ts +5 -5
  34. package/src/extensions/dnd.ts +2 -2
  35. package/src/extensions/factories.ts +6 -7
  36. package/src/extensions/folding.ts +2 -2
  37. package/src/extensions/index.ts +2 -2
  38. package/src/extensions/markdown/decorate.ts +4 -3
  39. package/src/extensions/markdown/highlight.ts +25 -11
  40. package/src/extensions/markdown/link.ts +27 -33
  41. package/src/extensions/markdown/styles.ts +6 -6
  42. package/src/extensions/outliner/outliner.ts +3 -3
  43. package/src/extensions/preview/preview.ts +8 -8
  44. package/src/extensions/scroller.ts +232 -0
  45. package/src/extensions/tags/streamer.ts +1 -1
  46. package/src/extensions/tags/xml-tags.ts +7 -4
  47. package/src/styles/index.ts +0 -2
  48. package/src/styles/theme.ts +106 -29
  49. package/src/util/cursor.ts +1 -1
  50. package/dist/types/src/extensions/autoscroll.d.ts +0 -20
  51. package/dist/types/src/extensions/autoscroll.d.ts.map +0 -1
  52. package/dist/types/src/extensions/scrolling.d.ts +0 -78
  53. package/dist/types/src/extensions/scrolling.d.ts.map +0 -1
  54. package/dist/types/src/styles/markdown.d.ts +0 -8
  55. package/dist/types/src/styles/markdown.d.ts.map +0 -1
  56. package/dist/types/src/styles/tokens.d.ts +0 -3
  57. package/dist/types/src/styles/tokens.d.ts.map +0 -1
  58. package/src/extensions/autoscroll.ts +0 -165
  59. package/src/extensions/scrolling.ts +0 -189
  60. package/src/styles/markdown.ts +0 -26
  61. package/src/styles/tokens.ts +0 -17
@@ -1,78 +0,0 @@
1
- import { EditorView } from '@codemirror/view';
2
- /**
3
- * Configuration options for smooth scrolling behavior.
4
- */
5
- export type SmoothScrollOptions = {
6
- /**
7
- * Additional offset from the target line in pixels.
8
- * Positive values scroll past the line, negative values stop before it.
9
- * @default 0
10
- */
11
- offset?: number;
12
- /**
13
- * Position of the target line in the viewport.
14
- * - 'start': Line appears at the start (top) of the screen
15
- * - 'end': Line appears at the end (bottom) of the screen
16
- * @default 'start'
17
- */
18
- position?: 'start' | 'end';
19
- /**
20
- * Whether to use smooth scrolling.
21
- * @default 'smooth'
22
- */
23
- behavior?: ScrollBehavior;
24
- };
25
- /**
26
- * Parameters for the scroll to line effect.
27
- */
28
- export type ScrollToLineProps = {
29
- /**
30
- * The line number to scroll to (1-based).
31
- */
32
- line: number;
33
- /**
34
- * Optional configuration to override default scroll behavior.
35
- */
36
- options?: SmoothScrollOptions;
37
- };
38
- /**
39
- * StateEffect for triggering smooth scroll to a specific line.
40
- */
41
- export declare const scrollToLineEffect: import("@codemirror/state").StateEffectType<ScrollToLineProps>;
42
- /**
43
- * Extension that provides smooth scrolling to specific lines in the editor.
44
- *
45
- * @example
46
- * ```typescript
47
- * // Add to editor extensions.
48
- * const extensions = [
49
- * smoothScroll()
50
- * ];
51
- *
52
- * // Trigger scroll to line 42.
53
- * view.dispatch({
54
- * effects: scrollToLineEffect.of({ line: 42 })
55
- * });
56
- *
57
- * // Scroll with custom options.
58
- * view.dispatch({
59
- * effects: scrollToLineEffect.of({ line: 100, options: { offset: -50 } })
60
- * });
61
- *
62
- * // Scroll so line appears at end (bottom) of screen.
63
- * view.dispatch({
64
- * effects: scrollToLineEffect.of({ line: 50, options: { position: 'end' } })
65
- * });
66
- * ```
67
- */
68
- export declare const smoothScroll: ({ offset, position }?: Partial<SmoothScrollOptions>) => import("@codemirror/state").Extension[];
69
- /**
70
- * Helper function to scroll to a specific line.
71
- * This is a convenience function that can be used directly with an EditorView.
72
- *
73
- * @param view - The CodeMirror EditorView instance
74
- * @param line - The line number to scroll to (1-based)
75
- * @param options - Optional scroll configuration
76
- */
77
- export declare const scrollToLine: (view: EditorView, line: number, options?: SmoothScrollOptions) => void;
78
- //# sourceMappingURL=scrolling.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"scrolling.d.ts","sourceRoot":"","sources":["../../../../src/extensions/scrolling.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAc,MAAM,kBAAkB,CAAC;AAE1D;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC;IAC3B;;;OAGG;IACH,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,OAAO,CAAC,EAAE,mBAAmB,CAAC;CAC/B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,gEAA0C,CAAC;AAE1E;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,YAAY,GAAI,uBAAoC,OAAO,CAAC,mBAAmB,CAAM,4CAkGjG,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,MAAM,UAAU,EAAE,MAAM,MAAM,EAAE,UAAU,mBAAmB,SAIzF,CAAC"}
@@ -1,8 +0,0 @@
1
- export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
2
- export declare const markdownTheme: {
3
- code: string;
4
- codeMark: string;
5
- mark: string;
6
- heading: (level: HeadingLevel) => string;
7
- };
8
- //# sourceMappingURL=markdown.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../../src/styles/markdown.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAYjD,eAAO,MAAM,aAAa;;;;qBAIP,YAAY;CAG9B,CAAC"}
@@ -1,3 +0,0 @@
1
- export declare const fontBody: string;
2
- export declare const fontMono: string;
3
- //# sourceMappingURL=tokens.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../../src/styles/tokens.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,QAAQ,QAA8B,CAAC;AACpD,eAAO,MAAM,QAAQ,QAA8B,CAAC"}
@@ -1,165 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { StateEffect } from '@codemirror/state';
6
- import { EditorView, ViewPlugin } from '@codemirror/view';
7
-
8
- import { debounce } from '@dxos/async';
9
- import { Domino } from '@dxos/ui';
10
-
11
- import { scrollToLineEffect } from './scrolling';
12
-
13
- // TODO(burdon): Reconcile with scrollToLineEffect (scrolling).
14
- export const scrollToBottomEffect = StateEffect.define<ScrollBehavior | undefined>();
15
-
16
- export type AutoScrollOptions = {
17
- /** Auto-scroll when reaches the bottom. */
18
- autoScroll?: boolean;
19
- /** Threshold in px to trigger scroll from bottom. */
20
- threshold?: number;
21
- /** Throttle time in ms. */
22
- throttleDelay?: number;
23
- /** Callback when auto-scrolling. */
24
- onAutoScroll?: (props: { view: EditorView; distanceFromBottom: number }) => boolean | void;
25
- };
26
-
27
- /**
28
- * Extension that supports pinning the scroll position and automatically scrolls to the bottom when content is added.
29
- */
30
- // TODO(burdon): Reconcile with transcript-extension.
31
- export const autoScroll = ({
32
- autoScroll = true,
33
- threshold = 100,
34
- throttleDelay = 1_000,
35
- onAutoScroll,
36
- }: Partial<AutoScrollOptions> = {}) => {
37
- let buttonContainer: HTMLDivElement | undefined;
38
- let hideTimeout: NodeJS.Timeout | undefined;
39
- let lastScrollTop = 0;
40
- let isPinned = true;
41
-
42
- const setPinned = (pin: boolean) => {
43
- isPinned = pin;
44
- buttonContainer?.classList.toggle('opacity-0', pin);
45
- };
46
-
47
- // Temporarily hide the scrollbar while auto-scrolling.
48
- const hideScrollbar = (view: EditorView) => {
49
- view.scrollDOM.classList.add('cm-hide-scrollbar');
50
- clearTimeout(hideTimeout);
51
- hideTimeout = setTimeout(() => {
52
- view.scrollDOM.classList.remove('cm-hide-scrollbar');
53
- }, 1_000);
54
- };
55
-
56
- // Throttled scroll to bottom.
57
- const scrollToBottom = (view: EditorView, behavior?: ScrollBehavior) => {
58
- setPinned(true);
59
- hideScrollbar(view);
60
- const line = view.state.doc.lineAt(view.state.doc.length);
61
- view.dispatch({
62
- selection: { anchor: line.to, head: line.to },
63
- effects: scrollToLineEffect.of({
64
- line: line.number,
65
- options: { position: 'end', offset: threshold, behavior },
66
- }),
67
- });
68
- };
69
-
70
- // Throttled check for distance from bottom (for downward scrolls only).
71
- const checkDistance = debounce((view: EditorView) => {
72
- const scrollerRect = view.scrollDOM.getBoundingClientRect();
73
- const coords = view.coordsAtPos(view.state.doc.length);
74
- const distanceFromBottom = coords ? coords.bottom - scrollerRect.bottom : 0;
75
- setPinned(distanceFromBottom < 0);
76
- }, 1_000);
77
-
78
- // Debounce scroll updates so rapid edits don't cause clunky scrolling.
79
- const triggerUpdate = debounce((view: EditorView) => scrollToBottom(view), throttleDelay);
80
-
81
- return [
82
- // Update listener for logging when scrolling is needed.
83
- EditorView.updateListener.of(({ view, transactions, heightChanged }) => {
84
- // TODO(burdon): Remove and use scrollToLineEffect instead.
85
- transactions.forEach((transaction) => {
86
- for (const effect of transaction.effects) {
87
- if (effect.is(scrollToBottomEffect)) {
88
- scrollToBottom(view, effect.value);
89
- }
90
- }
91
- });
92
-
93
- // Maybe scroll if doc changed and pinned.
94
- // NOTE: Geometry changed is triggered when widgets change height (e.g., toggle tool block).
95
- if (heightChanged && isPinned) {
96
- const coords = view.coordsAtPos(view.state.doc.length);
97
- const scrollerRect = view.scrollDOM.getBoundingClientRect();
98
- const distanceFromBottom = coords ? scrollerRect.bottom - coords.bottom : 0;
99
- if (autoScroll && distanceFromBottom < threshold) {
100
- const shouldScroll = onAutoScroll?.({ view, distanceFromBottom }) ?? true;
101
- if (shouldScroll) {
102
- triggerUpdate(view);
103
- }
104
- } else if (distanceFromBottom < 0) {
105
- setPinned(false);
106
- }
107
- }
108
- }),
109
-
110
- // Detect user scroll.
111
- EditorView.domEventHandlers({
112
- scroll: (event, view) => {
113
- const currentScrollTop = view.scrollDOM.scrollTop;
114
- const scrollingUp = currentScrollTop < lastScrollTop;
115
- lastScrollTop = currentScrollTop;
116
-
117
- // If user scrolls up, immediately unpin auto-scroll.
118
- if (scrollingUp) {
119
- setPinned(false);
120
- } else {
121
- checkDistance(view);
122
- }
123
- },
124
- }),
125
-
126
- // Scroll button.
127
- ViewPlugin.fromClass(
128
- class {
129
- constructor(view: EditorView) {
130
- const icon = Domino.of('dx-icon' as any).attributes({ icon: 'ph--arrow-down--regular' });
131
- const button = Domino.of('button')
132
- .classNames('dx-button bg-accentSurface')
133
- .attributes({ 'data-density': 'fine' })
134
- .children(icon)
135
- .on('click', () => {
136
- scrollToBottom(view);
137
- });
138
- buttonContainer = Domino.of('div')
139
- .classNames('cm-scroll-button transition-opacity duration-300 opacity-0')
140
- .children(button).root as HTMLDivElement;
141
-
142
- view.scrollDOM.parentElement!.appendChild(buttonContainer);
143
- }
144
- },
145
- ),
146
-
147
- // Styles.
148
- EditorView.theme({
149
- '.cm-scroller': {
150
- scrollbarWidth: 'thin',
151
- },
152
- '.cm-scroller.cm-hide-scrollbar': {
153
- scrollbarWidth: 'none',
154
- },
155
- '.cm-scroller.cm-hide-scrollbar::-webkit-scrollbar': {
156
- display: 'none',
157
- },
158
- '.cm-scroll-button': {
159
- position: 'absolute',
160
- bottom: '0.5rem',
161
- right: '1rem',
162
- },
163
- }),
164
- ];
165
- };
@@ -1,189 +0,0 @@
1
- //
2
- // Copyright 2025 DXOS.org
3
- //
4
-
5
- import { StateEffect } from '@codemirror/state';
6
- import { EditorView, ViewPlugin } from '@codemirror/view';
7
-
8
- /**
9
- * Configuration options for smooth scrolling behavior.
10
- */
11
- export type SmoothScrollOptions = {
12
- /**
13
- * Additional offset from the target line in pixels.
14
- * Positive values scroll past the line, negative values stop before it.
15
- * @default 0
16
- */
17
- offset?: number;
18
- /**
19
- * Position of the target line in the viewport.
20
- * - 'start': Line appears at the start (top) of the screen
21
- * - 'end': Line appears at the end (bottom) of the screen
22
- * @default 'start'
23
- */
24
- position?: 'start' | 'end';
25
- /**
26
- * Whether to use smooth scrolling.
27
- * @default 'smooth'
28
- */
29
- behavior?: ScrollBehavior;
30
- };
31
-
32
- /**
33
- * Parameters for the scroll to line effect.
34
- */
35
- export type ScrollToLineProps = {
36
- /**
37
- * The line number to scroll to (1-based).
38
- */
39
- line: number;
40
- /**
41
- * Optional configuration to override default scroll behavior.
42
- */
43
- options?: SmoothScrollOptions;
44
- };
45
-
46
- /**
47
- * StateEffect for triggering smooth scroll to a specific line.
48
- */
49
- export const scrollToLineEffect = StateEffect.define<ScrollToLineProps>();
50
-
51
- /**
52
- * Extension that provides smooth scrolling to specific lines in the editor.
53
- *
54
- * @example
55
- * ```typescript
56
- * // Add to editor extensions.
57
- * const extensions = [
58
- * smoothScroll()
59
- * ];
60
- *
61
- * // Trigger scroll to line 42.
62
- * view.dispatch({
63
- * effects: scrollToLineEffect.of({ line: 42 })
64
- * });
65
- *
66
- * // Scroll with custom options.
67
- * view.dispatch({
68
- * effects: scrollToLineEffect.of({ line: 100, options: { offset: -50 } })
69
- * });
70
- *
71
- * // Scroll so line appears at end (bottom) of screen.
72
- * view.dispatch({
73
- * effects: scrollToLineEffect.of({ line: 50, options: { position: 'end' } })
74
- * });
75
- * ```
76
- */
77
- export const smoothScroll = ({ offset = 0, position = 'start' }: Partial<SmoothScrollOptions> = {}) => {
78
- // ViewPlugin to manage scroll animations.
79
- const scrollPlugin = ViewPlugin.fromClass(
80
- class SmoothScrollPlugin {
81
- constructor(private readonly view: EditorView) {}
82
-
83
- // No-op.
84
- destroy() {}
85
-
86
- /**
87
- * Perform smooth scroll to the specified line.
88
- */
89
- scrollToLine(lineNumber: number, options: SmoothScrollOptions) {
90
- const { offset: animOffset = 0, position: animPosition, behavior } = options;
91
- const doc = this.view.state.doc;
92
- const scroller = this.view.scrollDOM;
93
-
94
- // Convert 1-based line number to 0-based.
95
- const targetLine = Math.max(0, lineNumber - 1);
96
- if (behavior === 'instant') {
97
- requestAnimationFrame(() => {
98
- this.view.dispatch({
99
- selection: { anchor: doc.line(targetLine + 1).from },
100
- scrollIntoView: true,
101
- });
102
- });
103
- return;
104
- }
105
-
106
- // Get the position of the target line.
107
- if (targetLine >= doc.lines) {
108
- // Line doesn't exist, scroll to end.
109
- const targetScrollTop = scroller.scrollHeight - scroller.clientHeight + (animOffset || 0);
110
- this.animateScroll(scroller, targetScrollTop);
111
- return;
112
- }
113
-
114
- const lineStart = doc.line(targetLine + 1).from;
115
- const coords = this.view.coordsAtPos(lineStart);
116
- if (!coords) {
117
- return;
118
- }
119
-
120
- // Calculate target scroll position based on position option.
121
- const currentScrollTop = scroller.scrollTop;
122
- const scrollerRect = scroller.getBoundingClientRect();
123
- const maxScrollTop = scroller.scrollHeight - scroller.clientHeight;
124
-
125
- let targetScrollTop: number;
126
- if (animPosition === 'end') {
127
- // Position line at end (bottom) of viewport.
128
- // Calculate how far down we need to scroll so the line's bottom aligns with viewport bottom.
129
- targetScrollTop = currentScrollTop + coords.bottom - scrollerRect.bottom + animOffset;
130
- } else {
131
- // Default: position line at start (top) of viewport.
132
- targetScrollTop = currentScrollTop + coords.top - scrollerRect.top + animOffset;
133
- }
134
-
135
- // Clamp to valid scroll range.
136
- const clampedScrollTop = Math.max(0, Math.min(targetScrollTop, maxScrollTop));
137
- this.animateScroll(scroller, clampedScrollTop);
138
- }
139
-
140
- /**
141
- * Animate scroll using browser's built-in smooth scrolling.
142
- */
143
- private animateScroll(element: HTMLElement, targetScrollTop: number) {
144
- if (Math.abs(targetScrollTop - element.scrollTop) < 1) {
145
- return;
146
- }
147
-
148
- // Use browser's built-in smooth scrolling.
149
- element.scrollTo({
150
- top: targetScrollTop,
151
- behavior: 'smooth',
152
- });
153
- }
154
- },
155
- );
156
-
157
- return [
158
- scrollPlugin,
159
-
160
- // Update listener to handle scroll effects.
161
- EditorView.updateListener.of((update) => {
162
- update.transactions.forEach((transaction) => {
163
- for (const effect of transaction.effects) {
164
- if (effect.is(scrollToLineEffect)) {
165
- const { line, options = {} } = effect.value;
166
- const plugin = update.view.plugin(scrollPlugin);
167
- if (plugin) {
168
- plugin.scrollToLine(line, { offset, position, ...options });
169
- }
170
- }
171
- }
172
- });
173
- }),
174
- ];
175
- };
176
-
177
- /**
178
- * Helper function to scroll to a specific line.
179
- * This is a convenience function that can be used directly with an EditorView.
180
- *
181
- * @param view - The CodeMirror EditorView instance
182
- * @param line - The line number to scroll to (1-based)
183
- * @param options - Optional scroll configuration
184
- */
185
- export const scrollToLine = (view: EditorView, line: number, options?: SmoothScrollOptions) => {
186
- view.dispatch({
187
- effects: scrollToLineEffect.of({ line, options }),
188
- });
189
- };
@@ -1,26 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { mx } from '@dxos/ui-theme';
6
-
7
- export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
8
-
9
- // https://tailwindcss.com/docs/font-weight
10
- const headings: Record<HeadingLevel, string> = {
11
- 1: 'text-4xl',
12
- 2: 'text-3xl',
13
- 3: 'text-2xl',
14
- 4: 'text-xl',
15
- 5: 'text-lg',
16
- 6: '', // TODO(burdon): Should be text-base, but that's a color in our system.
17
- };
18
-
19
- export const markdownTheme = {
20
- code: 'font-mono !no-underline text-neutral-700 dark:text-neutral-300',
21
- codeMark: 'font-mono text-primary-500',
22
- mark: 'opacity-50',
23
- heading: (level: HeadingLevel) => {
24
- return mx(headings[level], 'dark:text-neutral-400');
25
- },
26
- };
@@ -1,17 +0,0 @@
1
- //
2
- // Copyright 2023 DXOS.org
3
- //
4
-
5
- import { tokens } from '@dxos/ui-theme';
6
- import { get } from '@dxos/util';
7
-
8
- /**
9
- * Returns the tailwind token value.
10
- */
11
- const getToken = (path: string, defaultValue?: string | string[]): string => {
12
- const value = get(tokens, path, defaultValue);
13
- return value?.toString() ?? '';
14
- };
15
-
16
- export const fontBody = getToken('fontFamily.body');
17
- export const fontMono = getToken('fontFamily.mono');