@dxos/react-ui-editor 0.6.13-main.ed424a1 → 0.6.13
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 +346 -241
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/types/src/TextEditor.stories.d.ts +1 -4
- package/dist/types/src/TextEditor.stories.d.ts.map +1 -1
- package/dist/types/src/defaults.d.ts.map +1 -1
- package/dist/types/src/extensions/autocomplete.d.ts +1 -2
- package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
- package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
- package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
- package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
- package/dist/types/src/extensions/awareness/awareness.d.ts +2 -2
- package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
- package/dist/types/src/extensions/command/state.d.ts +2 -2
- package/dist/types/src/extensions/command/state.d.ts.map +1 -1
- package/dist/types/src/extensions/comments.d.ts +1 -1
- package/dist/types/src/extensions/comments.d.ts.map +1 -1
- package/dist/types/src/{state → extensions}/cursor.d.ts +2 -2
- package/dist/types/src/extensions/cursor.d.ts.map +1 -0
- package/dist/types/src/extensions/debug.d.ts +2 -2
- package/dist/types/src/extensions/debug.d.ts.map +1 -1
- package/dist/types/src/extensions/doc.d.ts +6 -0
- package/dist/types/src/extensions/doc.d.ts.map +1 -0
- package/dist/types/src/extensions/folding.d.ts.map +1 -1
- package/dist/types/src/extensions/index.d.ts +4 -0
- package/dist/types/src/extensions/index.d.ts.map +1 -1
- package/dist/types/src/extensions/listener.d.ts +0 -1
- package/dist/types/src/extensions/listener.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
- package/dist/types/src/extensions/markdown/formatting.test.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 +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/modes.d.ts +3 -3
- package/dist/types/src/extensions/modes.d.ts.map +1 -1
- package/dist/types/src/{state → extensions}/state.d.ts +2 -2
- package/dist/types/src/extensions/state.d.ts.map +1 -0
- package/dist/types/src/extensions/types.d.ts.map +1 -0
- package/dist/types/src/extensions/util/overlap.d.ts +1 -1
- package/dist/types/src/extensions/util/overlap.d.ts.map +1 -1
- package/dist/types/src/extensions/util/react.d.ts +1 -1
- package/dist/types/src/extensions/util/react.d.ts.map +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts +1 -1
- package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/styles/markdown.d.ts +2 -1
- package/dist/types/src/styles/markdown.d.ts.map +1 -1
- package/dist/types/src/styles/theme.d.ts.map +1 -1
- package/dist/types/src/util.d.ts +0 -6
- package/dist/types/src/util.d.ts.map +1 -1
- package/package.json +38 -59
- package/src/TextEditor.stories.tsx +20 -25
- package/src/defaults.ts +2 -0
- package/src/extensions/annotations.ts +1 -1
- package/src/extensions/autocomplete.ts +8 -9
- package/src/extensions/automerge/{automerge.test.tsx → automerge.spec.tsx} +0 -1
- package/src/extensions/automerge/automerge.stories.tsx +1 -1
- package/src/extensions/automerge/automerge.test.ts +13 -0
- package/src/extensions/automerge/automerge.ts +2 -2
- package/src/extensions/automerge/cursor.ts +1 -1
- package/src/extensions/awareness/awareness.ts +5 -3
- package/src/extensions/command/hint.ts +1 -1
- package/src/extensions/command/state.ts +4 -3
- package/src/extensions/comments.ts +45 -44
- package/src/{state → extensions}/cursor.ts +6 -3
- package/src/extensions/debug.ts +2 -2
- package/src/extensions/doc.ts +17 -0
- package/src/extensions/folding.tsx +2 -4
- package/src/extensions/index.ts +4 -0
- package/src/extensions/listener.ts +0 -1
- package/src/extensions/markdown/changes.test.ts +3 -1
- package/src/extensions/markdown/decorate.ts +6 -49
- package/src/extensions/markdown/formatting.test.ts +3 -1
- package/src/extensions/markdown/highlight.ts +5 -0
- package/src/extensions/markdown/link.ts +2 -3
- package/src/extensions/markdown/parser.test.ts +2 -1
- package/src/extensions/markdown/styles.ts +0 -10
- package/src/extensions/markdown/table.ts +3 -3
- package/src/extensions/modes.ts +5 -6
- package/src/{state → extensions}/state.ts +3 -7
- package/src/extensions/util/overlap.ts +1 -1
- package/src/extensions/util/react.tsx +1 -5
- package/src/hooks/useTextEditor.ts +32 -36
- package/src/index.ts +0 -1
- package/src/styles/markdown.ts +3 -1
- package/src/styles/theme.ts +1 -3
- package/src/util.ts +0 -10
- package/dist/lib/browser/chunk-CIQSMP7K.mjs +0 -148
- package/dist/lib/browser/chunk-CIQSMP7K.mjs.map +0 -7
- package/dist/lib/browser/state/index.mjs +0 -17
- package/dist/lib/browser/state/index.mjs.map +0 -7
- package/dist/lib/node/chunk-GZWIENFM.cjs +0 -169
- package/dist/lib/node/chunk-GZWIENFM.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -5493
- package/dist/lib/node/index.cjs.map +0 -7
- package/dist/lib/node/meta.json +0 -1
- package/dist/lib/node/state/index.cjs +0 -39
- package/dist/lib/node/state/index.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-GP5RCZ3X.mjs +0 -150
- package/dist/lib/node-esm/chunk-GP5RCZ3X.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -5484
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/state/index.mjs +0 -18
- package/dist/lib/node-esm/state/index.mjs.map +0 -7
- package/dist/types/src/state/cursor.d.ts.map +0 -1
- package/dist/types/src/state/doc.d.ts +0 -5
- package/dist/types/src/state/doc.d.ts.map +0 -1
- package/dist/types/src/state/index.d.ts +0 -6
- package/dist/types/src/state/index.d.ts.map +0 -1
- package/dist/types/src/state/state.d.ts.map +0 -1
- package/dist/types/src/state/types.d.ts.map +0 -1
- package/dist/types/src/state/util.d.ts +0 -3
- package/dist/types/src/state/util.d.ts.map +0 -1
- package/src/state/doc.ts +0 -10
- package/src/state/index.ts +0 -11
- package/src/state/util.ts +0 -13
- /package/dist/types/src/{state → extensions}/types.d.ts +0 -0
- /package/src/{state → extensions}/types.ts +0 -0
@@ -2,7 +2,7 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { Annotation, type Extension, RangeSet, type Range } from '@codemirror/state';
|
5
|
+
import { Annotation, Facet, type Extension, RangeSet, type Range } from '@codemirror/state';
|
6
6
|
import {
|
7
7
|
Decoration,
|
8
8
|
type DecorationSet,
|
@@ -16,7 +16,7 @@ import {
|
|
16
16
|
import { Event } from '@dxos/async';
|
17
17
|
import { Context } from '@dxos/context';
|
18
18
|
|
19
|
-
import {
|
19
|
+
import { Cursor, type CursorConverter } from '../cursor';
|
20
20
|
|
21
21
|
export interface AwarenessProvider {
|
22
22
|
remoteStateChange: Event<void>;
|
@@ -38,7 +38,9 @@ const dummyProvider: AwarenessProvider = {
|
|
38
38
|
update: () => {},
|
39
39
|
};
|
40
40
|
|
41
|
-
export const awarenessProvider =
|
41
|
+
export const awarenessProvider = Facet.define<AwarenessProvider, AwarenessProvider>({
|
42
|
+
combine: (providers) => providers[0] ?? dummyProvider,
|
43
|
+
});
|
42
44
|
|
43
45
|
// TODO(dmaretskyi): Specify the users that actually changed. Currently, we recalculate positions for every user.
|
44
46
|
const RemoteSelectionChangedAnnotation = Annotation.define();
|
@@ -7,7 +7,7 @@ import { Decoration, EditorView, ViewPlugin, type ViewUpdate, WidgetType } from
|
|
7
7
|
|
8
8
|
import { type CommandOptions } from './command';
|
9
9
|
import { commandState } from './state';
|
10
|
-
import { clientRectsFor, flattenRect } from '../util';
|
10
|
+
import { clientRectsFor, flattenRect } from '../util/dom';
|
11
11
|
|
12
12
|
class CommandHint extends WidgetType {
|
13
13
|
constructor(readonly content: string | HTMLElement) {
|
@@ -2,7 +2,7 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { StateEffect, StateField } from '@codemirror/state';
|
5
|
+
import { Facet, StateEffect, StateField } from '@codemirror/state';
|
6
6
|
import {
|
7
7
|
type Command,
|
8
8
|
type EditorView,
|
@@ -13,13 +13,14 @@ import {
|
|
13
13
|
} from '@codemirror/view';
|
14
14
|
|
15
15
|
import { type CommandOptions } from './command';
|
16
|
-
import { singleValueFacet } from '../../state';
|
17
16
|
|
18
17
|
type CommandState = {
|
19
18
|
tooltip?: Tooltip | null;
|
20
19
|
};
|
21
20
|
|
22
|
-
export const commandConfig =
|
21
|
+
export const commandConfig = Facet.define<CommandOptions, Required<CommandOptions>>({
|
22
|
+
combine: (providers) => providers[0],
|
23
|
+
});
|
23
24
|
|
24
25
|
export const commandState = StateField.define<CommandState>({
|
25
26
|
create: () => ({}),
|
@@ -5,6 +5,7 @@
|
|
5
5
|
import { invertedEffects } from '@codemirror/commands';
|
6
6
|
import {
|
7
7
|
type Extension,
|
8
|
+
Facet,
|
8
9
|
StateEffect,
|
9
10
|
StateField,
|
10
11
|
type Text,
|
@@ -28,15 +29,18 @@ import { debounce, type UnsubscribeCallback } from '@dxos/async';
|
|
28
29
|
import { log } from '@dxos/log';
|
29
30
|
import { nonNullable } from '@dxos/util';
|
30
31
|
|
32
|
+
import { Cursor } from './cursor';
|
33
|
+
import { type Comment, type Range } from './types';
|
31
34
|
import { overlap } from './util';
|
32
|
-
import { Cursor, documentId, singleValueFacet } from '../state';
|
33
|
-
import { type Comment, type Range } from '../state';
|
34
35
|
import { callbackWrapper } from '../util';
|
35
36
|
|
36
37
|
//
|
37
38
|
// State management.
|
38
39
|
//
|
39
40
|
|
41
|
+
// TODO(wittjosiah): Factor out, not comments-specific.
|
42
|
+
const documentId = Facet.define<string | undefined, string | undefined>({ combine: (values) => values[0] });
|
43
|
+
|
40
44
|
type CommentState = {
|
41
45
|
comment: Comment;
|
42
46
|
range: Range;
|
@@ -365,7 +369,7 @@ export type CommentsOptions = {
|
|
365
369
|
onHover?: (el: Element, shortcut: string) => void;
|
366
370
|
};
|
367
371
|
|
368
|
-
const optionsFacet =
|
372
|
+
const optionsFacet = Facet.define<CommentsOptions, CommentsOptions>({ combine: (providers) => providers[0] });
|
369
373
|
|
370
374
|
/**
|
371
375
|
* Comment threads.
|
@@ -385,7 +389,7 @@ export const comments = (options: CommentsOptions = {}): Extension => {
|
|
385
389
|
|
386
390
|
return [
|
387
391
|
optionsFacet.of(options),
|
388
|
-
|
392
|
+
documentId.of(options.id),
|
389
393
|
commentsState,
|
390
394
|
commentsDecorations,
|
391
395
|
handleCommentClick,
|
@@ -394,43 +398,45 @@ export const comments = (options: CommentsOptions = {}): Extension => {
|
|
394
398
|
//
|
395
399
|
// Keymap.
|
396
400
|
//
|
397
|
-
options.onCreate
|
398
|
-
keymap.of([
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
401
|
+
options.onCreate
|
402
|
+
? keymap.of([
|
403
|
+
{
|
404
|
+
key: shortcut,
|
405
|
+
run: callbackWrapper(createComment),
|
406
|
+
},
|
407
|
+
])
|
408
|
+
: [],
|
404
409
|
|
405
410
|
//
|
406
411
|
// Hover tooltip (for key shortcut hints, etc.)
|
407
412
|
// TODO(burdon): Factor out to generic hints extension for current selection/line.
|
408
413
|
//
|
409
|
-
options.onHover
|
410
|
-
hoverTooltip(
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
414
|
+
options.onHover
|
415
|
+
? hoverTooltip(
|
416
|
+
(view, pos) => {
|
417
|
+
const selection = view.state.selection.main;
|
418
|
+
if (selection && pos >= selection.from && pos <= selection.to) {
|
419
|
+
return {
|
420
|
+
pos: selection.from,
|
421
|
+
end: selection.to,
|
422
|
+
above: true,
|
423
|
+
create: () => {
|
424
|
+
const el = document.createElement('div');
|
425
|
+
options.onHover!(el, shortcut);
|
426
|
+
return { dom: el, offset: { x: 0, y: 8 } };
|
427
|
+
},
|
428
|
+
};
|
429
|
+
}
|
425
430
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
431
|
+
return null;
|
432
|
+
},
|
433
|
+
{
|
434
|
+
// TODO(burdon): Hide on change triggered immediately?
|
435
|
+
// hideOnChange: true,
|
436
|
+
hoverTime: 1_000,
|
437
|
+
},
|
438
|
+
)
|
439
|
+
: [],
|
434
440
|
|
435
441
|
//
|
436
442
|
// Track deleted ranges and update ranges for decorations.
|
@@ -505,8 +511,8 @@ export const comments = (options: CommentsOptions = {}): Extension => {
|
|
505
511
|
}
|
506
512
|
}),
|
507
513
|
|
508
|
-
options.onUpdate
|
509
|
-
]
|
514
|
+
options.onUpdate ? trackPastedComments(options.onUpdate) : [],
|
515
|
+
];
|
510
516
|
};
|
511
517
|
|
512
518
|
//
|
@@ -547,13 +553,9 @@ export const scrollThreadIntoView = (view: EditorView, id: string, center = true
|
|
547
553
|
* Query the editor state for the active formatting at the selection.
|
548
554
|
*/
|
549
555
|
export const selectionOverlapsComment = (state: EditorState): boolean => {
|
550
|
-
// May not be defined if thread plugin not installed.
|
551
|
-
const commentState = state.field(commentsState, false);
|
552
|
-
if (commentState === undefined) {
|
553
|
-
return false;
|
554
|
-
}
|
555
|
-
|
556
556
|
const { selection } = state;
|
557
|
+
const commentState = state.field(commentsState);
|
558
|
+
|
557
559
|
for (const range of selection.ranges) {
|
558
560
|
if (commentState.comments.some(({ range: commentRange }) => overlap(commentRange, range))) {
|
559
561
|
return true;
|
@@ -595,7 +597,6 @@ class ExternalCommentSync implements PluginValue {
|
|
595
597
|
};
|
596
598
|
}
|
597
599
|
|
598
|
-
// TODO(burdon): Needs comment.
|
599
600
|
export const createExternalCommentSync = (
|
600
601
|
id: string,
|
601
602
|
subscribe: (sink: () => void) => UnsubscribeCallback,
|
@@ -2,10 +2,9 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { type EditorState } from '@codemirror/state';
|
5
|
+
import { type EditorState, Facet } from '@codemirror/state';
|
6
6
|
|
7
7
|
import { type Range } from './types';
|
8
|
-
import { singleValueFacet } from './util';
|
9
8
|
|
10
9
|
/**
|
11
10
|
* Converts indexes into the text document into stable peer-independent cursors.
|
@@ -28,7 +27,11 @@ const defaultCursorConverter: CursorConverter = {
|
|
28
27
|
};
|
29
28
|
|
30
29
|
export class Cursor {
|
31
|
-
static readonly converter =
|
30
|
+
static readonly converter = Facet.define<CursorConverter, CursorConverter>({
|
31
|
+
combine: (providers) => {
|
32
|
+
return providers[0] ?? defaultCursorConverter;
|
33
|
+
},
|
34
|
+
});
|
32
35
|
|
33
36
|
static readonly getCursorFromRange = (state: EditorState, range: Range) => {
|
34
37
|
const cursorConverter = state.facet(Cursor.converter);
|
package/src/extensions/debug.ts
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
//
|
4
4
|
|
5
5
|
import { syntaxTree } from '@codemirror/language';
|
6
|
-
import { type EditorState, type
|
6
|
+
import { type EditorState, type RangeSet, StateField, type Transaction } from '@codemirror/state';
|
7
7
|
|
8
8
|
// eslint-disable-next-line no-console
|
9
|
-
export const debugNodeLogger = (log: (...args: any[]) => void = console.log)
|
9
|
+
export const debugNodeLogger = (log: (...args: any[]) => void = console.log) => {
|
10
10
|
const logTokens = (state: EditorState) => syntaxTree(state).iterate({ enter: (node) => log(node.type) });
|
11
11
|
return StateField.define<any>({
|
12
12
|
create: (state) => logTokens(state),
|
@@ -0,0 +1,17 @@
|
|
1
|
+
//
|
2
|
+
// Copyright 2024 DXOS.org
|
3
|
+
//
|
4
|
+
|
5
|
+
import { Facet } from '@codemirror/state';
|
6
|
+
|
7
|
+
import { invariant } from '@dxos/invariant';
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Currently edited document id as FQ string.
|
11
|
+
*/
|
12
|
+
export const documentId = Facet.define<string, string>({
|
13
|
+
combine: (providers) => {
|
14
|
+
invariant(providers.length <= 1);
|
15
|
+
return providers[0];
|
16
|
+
},
|
17
|
+
});
|
@@ -17,7 +17,7 @@ export type FoldingOptions = {};
|
|
17
17
|
/**
|
18
18
|
* https://codemirror.net/examples/gutter
|
19
19
|
*/
|
20
|
-
// TODO(burdon): Remember folding state
|
20
|
+
// TODO(burdon): Remember folding state.
|
21
21
|
export const folding = (_props: FoldingOptions = {}): Extension => [
|
22
22
|
codeFolding({
|
23
23
|
placeholderDOM: () => {
|
@@ -26,10 +26,8 @@ export const folding = (_props: FoldingOptions = {}): Extension => [
|
|
26
26
|
}),
|
27
27
|
foldGutter({
|
28
28
|
markerDOM: (open) => {
|
29
|
-
// TODO(burdon): Use sprite directly.
|
30
|
-
const el = createElement('div', { className: 'flex h-full items-center' });
|
31
29
|
return renderRoot(
|
32
|
-
|
30
|
+
createElement('div', { className: 'flex h-full items-center' }),
|
33
31
|
<Icon icon='ph--caret-right--regular' classNames={[getSize(3), 'mx-3 cursor-pointer', open && 'rotate-90']} />,
|
34
32
|
);
|
35
33
|
},
|
package/src/extensions/index.ts
CHANGED
@@ -9,7 +9,9 @@ export * from './awareness';
|
|
9
9
|
export * from './blast';
|
10
10
|
export * from './command';
|
11
11
|
export * from './comments';
|
12
|
+
export * from './cursor';
|
12
13
|
export * from './debug';
|
14
|
+
export * from './doc';
|
13
15
|
export * from './dnd';
|
14
16
|
export * from './factories';
|
15
17
|
export * from './folding';
|
@@ -17,4 +19,6 @@ export * from './listener';
|
|
17
19
|
export * from './markdown';
|
18
20
|
export * from './mention';
|
19
21
|
export * from './modes';
|
22
|
+
export * from './state';
|
23
|
+
export * from './types';
|
20
24
|
export * from './typewriter';
|
@@ -12,7 +12,6 @@ export type ListenerOptions = {
|
|
12
12
|
|
13
13
|
/**
|
14
14
|
* Event listener.
|
15
|
-
* @deprecated Use EditorView.updateListener and listen for specific update events.
|
16
15
|
*/
|
17
16
|
export const listener = ({ onFocus, onChange }: ListenerOptions): Extension => {
|
18
17
|
const extensions: Extension[] = [];
|
@@ -45,7 +45,7 @@ class HorizontalRuleWidget extends WidgetType {
|
|
45
45
|
class LinkButton extends WidgetType {
|
46
46
|
constructor(
|
47
47
|
private readonly url: string,
|
48
|
-
private readonly render: (el:
|
48
|
+
private readonly render: (el: Element, url: string) => void,
|
49
49
|
) {
|
50
50
|
super();
|
51
51
|
}
|
@@ -54,7 +54,6 @@ class LinkButton extends WidgetType {
|
|
54
54
|
return this.url === other.url;
|
55
55
|
}
|
56
56
|
|
57
|
-
// TODO(burdon): Create icon and link directly without react?
|
58
57
|
override toDOM(view: EditorView) {
|
59
58
|
const el = document.createElement('span');
|
60
59
|
this.render(el, this.url);
|
@@ -128,7 +127,6 @@ class TextWidget extends WidgetType {
|
|
128
127
|
}
|
129
128
|
|
130
129
|
const hide = Decoration.replace({});
|
131
|
-
const blockQuote = Decoration.line({ class: mx('cm-blockquote') });
|
132
130
|
const fencedCodeLine = Decoration.line({ class: mx('cm-code cm-codeblock-line') });
|
133
131
|
const fencedCodeLineFirst = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-first') });
|
134
132
|
const fencedCodeLineLast = Decoration.line({ class: mx('cm-code cm-codeblock-line', 'cm-codeblock-last') });
|
@@ -201,7 +199,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
201
199
|
return listLevels[listLevels.length - 1];
|
202
200
|
};
|
203
201
|
|
204
|
-
//
|
202
|
+
// const count = 0;
|
205
203
|
const enterNode = (node: SyntaxNodeRef) => {
|
206
204
|
// console.log(`[${count++}]`, { node: node.name, from: node.from, to: node.to });
|
207
205
|
switch (node.name) {
|
@@ -331,36 +329,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
331
329
|
break;
|
332
330
|
}
|
333
331
|
|
334
|
-
//
|
335
|
-
// Blockquote > QuoteMark > Paragraph
|
336
|
-
//
|
337
|
-
|
338
|
-
case 'Blockquote': {
|
339
|
-
const editing = editingRange(state, node, focus);
|
340
|
-
const quoteMark = node.node.getChild('QuoteMark');
|
341
|
-
const paragraph = node.node.getChild('Paragraph');
|
342
|
-
if (!editing && quoteMark && paragraph) {
|
343
|
-
atomicDeco.add(quoteMark.from, paragraph.from, hide);
|
344
|
-
}
|
345
|
-
|
346
|
-
for (const block of view.viewportLineBlocks) {
|
347
|
-
if (block.to < node.from) {
|
348
|
-
continue;
|
349
|
-
}
|
350
|
-
if (block.from > node.to) {
|
351
|
-
break;
|
352
|
-
}
|
353
|
-
|
354
|
-
deco.add(block.from, block.from, blockQuote);
|
355
|
-
}
|
356
|
-
|
357
|
-
break;
|
358
|
-
}
|
359
|
-
|
360
|
-
//
|
361
332
|
// CommentBlock
|
362
|
-
//
|
363
|
-
|
364
333
|
case 'CommentBlock': {
|
365
334
|
const editing = editingRange(state, node, focus);
|
366
335
|
for (const block of view.viewportLineBlocks) {
|
@@ -370,27 +339,21 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
370
339
|
if (block.from > node.to) {
|
371
340
|
break;
|
372
341
|
}
|
373
|
-
|
374
|
-
const
|
375
|
-
const isLast = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
|
376
|
-
|
342
|
+
const first = block.from <= node.from;
|
343
|
+
const last = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
|
377
344
|
deco.add(
|
378
345
|
block.from,
|
379
346
|
block.from,
|
380
|
-
|
347
|
+
first ? commentBlockLineFirst : last ? commentBlockLineLast : commentBlockLine,
|
381
348
|
);
|
382
|
-
|
383
|
-
if (!editing && (isFirst || isLast)) {
|
349
|
+
if (!editing && (first || last)) {
|
384
350
|
atomicDeco.add(block.from, block.to, hide);
|
385
351
|
}
|
386
352
|
}
|
387
353
|
break;
|
388
354
|
}
|
389
355
|
|
390
|
-
//
|
391
356
|
// FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
|
392
|
-
//
|
393
|
-
|
394
357
|
case 'FencedCode': {
|
395
358
|
for (const block of view.viewportLineBlocks) {
|
396
359
|
if (block.to < node.from) {
|
@@ -412,10 +375,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
412
375
|
return false;
|
413
376
|
}
|
414
377
|
|
415
|
-
//
|
416
378
|
// Link > [LinkMark, URL]
|
417
|
-
//
|
418
|
-
|
419
379
|
case 'Link': {
|
420
380
|
const marks = node.node.getChildren('LinkMark');
|
421
381
|
const urlNode = node.node.getChild('URL');
|
@@ -452,10 +412,7 @@ const buildDecorations = (view: EditorView, options: DecorateOptions, focus: boo
|
|
452
412
|
break;
|
453
413
|
}
|
454
414
|
|
455
|
-
//
|
456
415
|
// HR
|
457
|
-
//
|
458
|
-
|
459
416
|
case 'HorizontalRule': {
|
460
417
|
if (!editingRange(state, node, focus)) {
|
461
418
|
deco.add(node.from, node.to, horizontalRule);
|
@@ -4,7 +4,9 @@
|
|
4
4
|
|
5
5
|
import { markdownLanguage } from '@codemirror/lang-markdown';
|
6
6
|
import { EditorState, type StateCommand } from '@codemirror/state';
|
7
|
-
import {
|
7
|
+
import { expect } from 'chai';
|
8
|
+
|
9
|
+
import { describe, test } from '@dxos/test';
|
8
10
|
|
9
11
|
import {
|
10
12
|
addBlockquote,
|
@@ -168,6 +168,11 @@ export const markdownHighlightStyle = (_options: HighlightOptions = {}) => {
|
|
168
168
|
class: theme.code,
|
169
169
|
},
|
170
170
|
|
171
|
+
{
|
172
|
+
tag: [markdownTags.QuoteMark],
|
173
|
+
class: theme.blockquote,
|
174
|
+
},
|
175
|
+
|
171
176
|
{
|
172
177
|
tag: [markdownTags.TableCell],
|
173
178
|
class: 'font-mono',
|
@@ -9,8 +9,8 @@ import { type SyntaxNode } from '@lezer/common';
|
|
9
9
|
|
10
10
|
import { tooltipContent } from '@dxos/react-ui-theme';
|
11
11
|
|
12
|
-
export const linkTooltip = (render: (el:
|
13
|
-
|
12
|
+
export const linkTooltip = (render: (el: Element, url: string) => void) =>
|
13
|
+
hoverTooltip((view, pos, side) => {
|
14
14
|
const syntax = syntaxTree(view.state).resolveInner(pos, side);
|
15
15
|
let link = null;
|
16
16
|
for (let i = 0, node: SyntaxNode | null = syntax; !link && node && i < 5; node = node.parent, i++) {
|
@@ -35,4 +35,3 @@ export const linkTooltip = (render: (el: HTMLElement, url: string) => void) => {
|
|
35
35
|
},
|
36
36
|
};
|
37
37
|
});
|
38
|
-
};
|
@@ -5,7 +5,8 @@
|
|
5
5
|
// @ts-ignore
|
6
6
|
import { testTree } from '@lezer/generator/test';
|
7
7
|
import { parser } from '@lezer/markdown';
|
8
|
-
|
8
|
+
|
9
|
+
import { describe, test } from '@dxos/test';
|
9
10
|
|
10
11
|
describe('parser', () => {
|
11
12
|
// test.only('list-mark', () => {
|
@@ -39,16 +39,6 @@ export const formattingStyles = EditorView.theme({
|
|
39
39
|
width: `${orderedListIndentationWidth}px`,
|
40
40
|
},
|
41
41
|
|
42
|
-
/**
|
43
|
-
* Blockquote.
|
44
|
-
*/
|
45
|
-
'& .cm-blockquote': {
|
46
|
-
background: 'var(--dx-cmCodeblock)',
|
47
|
-
borderLeft: '2px solid var(--dx-cmSeparator)',
|
48
|
-
paddingLeft: '1rem',
|
49
|
-
margin: '0',
|
50
|
-
},
|
51
|
-
|
52
42
|
/**
|
53
43
|
* Code and codeblocks.
|
54
44
|
*/
|
@@ -25,9 +25,9 @@ export type TableOptions = {};
|
|
25
25
|
* https://github.github.com/gfm/#tables-extension
|
26
26
|
*/
|
27
27
|
export const table = (options: TableOptions = {}): Extension => {
|
28
|
-
return StateField.define<RangeSet<
|
28
|
+
return StateField.define<RangeSet<any>>({
|
29
29
|
create: (state) => update(state, options),
|
30
|
-
update: (_: RangeSet<
|
30
|
+
update: (_: RangeSet<any>, tr: Transaction) => update(tr.state, options),
|
31
31
|
provide: (field) => EditorView.decorations.from(field),
|
32
32
|
});
|
33
33
|
};
|
@@ -40,7 +40,7 @@ type Table = {
|
|
40
40
|
};
|
41
41
|
|
42
42
|
const update = (state: EditorState, _options: TableOptions) => {
|
43
|
-
const builder = new RangeSetBuilder
|
43
|
+
const builder = new RangeSetBuilder();
|
44
44
|
const cursor = state.selection.main.head;
|
45
45
|
|
46
46
|
const tables: Table[] = [];
|
package/src/extensions/modes.ts
CHANGED
@@ -2,27 +2,26 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { type Extension } from '@codemirror/state';
|
5
|
+
import { type Extension, Facet } from '@codemirror/state';
|
6
6
|
import { keymap } from '@codemirror/view';
|
7
7
|
import { vim } from '@replit/codemirror-vim';
|
8
8
|
import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
|
9
9
|
|
10
|
-
import { singleValueFacet } from '../state';
|
11
|
-
|
12
10
|
export const focusEvent = 'focus.container';
|
13
11
|
|
14
12
|
export const EditorViewModes = ['preview', 'readonly', 'source'] as const;
|
15
13
|
export type EditorViewMode = (typeof EditorViewModes)[number];
|
16
|
-
|
17
14
|
export const EditorInputModes = ['default', 'vim', 'vscode'] as const;
|
18
15
|
export type EditorInputMode = (typeof EditorInputModes)[number];
|
19
16
|
|
20
17
|
export type EditorInputConfig = {
|
21
|
-
type
|
18
|
+
type: string;
|
22
19
|
noTabster?: boolean;
|
23
20
|
};
|
24
21
|
|
25
|
-
export const editorInputMode =
|
22
|
+
export const editorInputMode = Facet.define<EditorInputConfig, EditorInputConfig>({
|
23
|
+
combine: (modes) => modes[0] ?? {},
|
24
|
+
});
|
26
25
|
|
27
26
|
export const InputModeExtensions: { [mode: string]: Extension } = {
|
28
27
|
default: [],
|
@@ -2,7 +2,7 @@
|
|
2
2
|
// Copyright 2024 DXOS.org
|
3
3
|
//
|
4
4
|
|
5
|
-
import { type
|
5
|
+
import { type Extension, Transaction, type TransactionSpec } from '@codemirror/state';
|
6
6
|
import { EditorView, keymap } from '@codemirror/view';
|
7
7
|
|
8
8
|
import { debounce } from '@dxos/async';
|
@@ -42,10 +42,7 @@ export const localStorageStateStoreAdapter: EditorStateOptions = {
|
|
42
42
|
},
|
43
43
|
};
|
44
44
|
|
45
|
-
export const createEditorStateTransaction = (
|
46
|
-
state: EditorState,
|
47
|
-
{ scrollTo, selection }: EditorSelectionState,
|
48
|
-
): TransactionSpec => {
|
45
|
+
export const createEditorStateTransaction = ({ scrollTo, selection }: EditorSelectionState): TransactionSpec => {
|
49
46
|
return {
|
50
47
|
selection,
|
51
48
|
scrollIntoView: !scrollTo,
|
@@ -57,7 +54,6 @@ export const createEditorStateTransaction = (
|
|
57
54
|
/**
|
58
55
|
* Track scrolling and selection state to be restored when switching to document.
|
59
56
|
*/
|
60
|
-
// TODO(burdon): Rename.
|
61
57
|
export const state = ({ getState, setState }: Partial<EditorStateOptions> = {}): Extension => {
|
62
58
|
const setStateDebounced = debounce(setState!, 1_000);
|
63
59
|
|
@@ -90,7 +86,7 @@ export const state = ({ getState, setState }: Partial<EditorStateOptions> = {}):
|
|
90
86
|
run: (view) => {
|
91
87
|
const state = getState(view.state.facet(documentId));
|
92
88
|
if (state) {
|
93
|
-
view.dispatch(createEditorStateTransaction(
|
89
|
+
view.dispatch(createEditorStateTransaction(state));
|
94
90
|
}
|
95
91
|
return true;
|
96
92
|
},
|
@@ -8,8 +8,6 @@ import { createRoot } from 'react-dom/client';
|
|
8
8
|
import { ThemeProvider } from '@dxos/react-ui';
|
9
9
|
import { defaultTx } from '@dxos/react-ui-theme';
|
10
10
|
|
11
|
-
// TODO(burdon): Factor out.
|
12
|
-
|
13
11
|
export type ElementOptions = {
|
14
12
|
className?: string;
|
15
13
|
};
|
@@ -25,9 +23,7 @@ export const createElement = (tag: string, options?: ElementOptions, children?:
|
|
25
23
|
return el;
|
26
24
|
};
|
27
25
|
|
28
|
-
|
29
|
-
// NOTE: CM seems to remove/detach/overwrite portals that are attached to the DOM it control.s
|
30
|
-
export const renderRoot = <T extends Element>(root: T, node: ReactNode): T => {
|
26
|
+
export const renderRoot = (root: HTMLElement, node: ReactNode): HTMLElement => {
|
31
27
|
createRoot(root).render(<ThemeProvider tx={defaultTx}>{node}</ThemeProvider>);
|
32
28
|
return root;
|
33
29
|
};
|