@dxos/react-ui-editor 0.6.12-staging.e11e696 → 0.6.12
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 +270 -153
- 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/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/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/package.json +37 -54
- package/src/TextEditor.stories.tsx +17 -22
- 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/state.ts +4 -3
- package/src/extensions/comments.ts +43 -37
- 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/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 +0 -1
- package/src/extensions/util/overlap.ts +1 -1
- package/src/extensions/util/react.tsx +1 -5
- package/src/hooks/useTextEditor.ts +2 -3
- package/src/index.ts +0 -1
- package/src/styles/markdown.ts +3 -1
- package/src/styles/theme.ts +1 -3
- package/dist/lib/browser/chunk-AZWYO7TE.mjs +0 -148
- package/dist/lib/browser/chunk-AZWYO7TE.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-5RSKGJRI.cjs +0 -169
- package/dist/lib/node/chunk-5RSKGJRI.cjs.map +0 -7
- package/dist/lib/node/index.cjs +0 -5480
- 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-RCIWLRIY.mjs +0 -150
- package/dist/lib/node-esm/chunk-RCIWLRIY.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -5472
- 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}/state.d.ts +0 -0
- /package/dist/types/src/{state → extensions}/types.d.ts +0 -0
- /package/src/{state → extensions}/types.ts +0 -0
@@ -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
|
//
|
@@ -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';
|
@@ -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: [],
|
@@ -54,7 +54,6 @@ export const createEditorStateTransaction = ({ scrollTo, selection }: EditorSele
|
|
54
54
|
/**
|
55
55
|
* Track scrolling and selection state to be restored when switching to document.
|
56
56
|
*/
|
57
|
-
// TODO(burdon): Rename.
|
58
57
|
export const state = ({ getState, setState }: Partial<EditorStateOptions> = {}): Extension => {
|
59
58
|
const setStateDebounced = debounce(setState!, 1_000);
|
60
59
|
|
@@ -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
|
};
|
@@ -19,12 +19,10 @@ import {
|
|
19
19
|
import { log } from '@dxos/log';
|
20
20
|
import { getProviderValue, isNotFalsy, type MaybeProvider } from '@dxos/util';
|
21
21
|
|
22
|
-
import { editorInputMode } from '../extensions';
|
23
|
-
import { type EditorSelection, createEditorStateTransaction, documentId } from '../state';
|
22
|
+
import { createEditorStateTransaction, documentId, editorInputMode, type EditorSelection } from '../extensions';
|
24
23
|
import { logChanges } from '../util';
|
25
24
|
|
26
25
|
export type UseTextEditor = {
|
27
|
-
// TODO(burdon): Rename.
|
28
26
|
parentRef: RefObject<HTMLDivElement>;
|
29
27
|
view?: EditorView;
|
30
28
|
focusAttributes: ReturnType<typeof useFocusableGroup> & {
|
@@ -111,6 +109,7 @@ export const useTextEditor = (
|
|
111
109
|
// https://codemirror.net/docs/ref/#view.EditorViewConfig
|
112
110
|
view = new EditorView({
|
113
111
|
parent: parentRef.current,
|
112
|
+
selection: initialSelection,
|
114
113
|
state,
|
115
114
|
// NOTE: Uncomment to debug/monitor all transactions.
|
116
115
|
// https://codemirror.net/docs/ref/#view.EditorView.dispatch
|
package/src/index.ts
CHANGED
package/src/styles/markdown.ts
CHANGED
@@ -17,9 +17,11 @@ const headings: Record<HeadingLevel, string> = {
|
|
17
17
|
};
|
18
18
|
|
19
19
|
export const theme = {
|
20
|
+
mark: 'opacity-50',
|
20
21
|
code: 'font-mono !no-underline text-neutral-700 dark:text-neutral-300',
|
21
22
|
codeMark: 'font-mono text-primary-500',
|
22
|
-
|
23
|
+
// TODO(burdon): Replace with widget.
|
24
|
+
blockquote: 'pl-1 mr-1 border-is-4 border-orange-500 dark:border-orange-500 dark:text-neutral-500',
|
23
25
|
heading: (level: HeadingLevel) => {
|
24
26
|
return mx(headings[level], 'dark:text-primary-400');
|
25
27
|
},
|
package/src/styles/theme.ts
CHANGED
@@ -71,11 +71,9 @@ export const defaultTheme: ThemeStyles = {
|
|
71
71
|
/**
|
72
72
|
* Gutters
|
73
73
|
* NOTE: Gutters should have the same top margin as the content.
|
74
|
-
* NOTE: They can't be transparent since the content needs to scroll below.
|
75
74
|
*/
|
76
75
|
'.cm-gutters': {
|
77
76
|
background: 'var(--surface-bg)',
|
78
|
-
borderRight: 'none',
|
79
77
|
},
|
80
78
|
'.cm-gutter': {},
|
81
79
|
'.cm-gutter.cm-lineNumbers .cm-gutterElement': {
|
@@ -97,7 +95,7 @@ export const defaultTheme: ThemeStyles = {
|
|
97
95
|
paddingInline: 0,
|
98
96
|
},
|
99
97
|
'.cm-activeLine': {
|
100
|
-
background: 'var(--dx-
|
98
|
+
background: 'var(--dx-hoverSurface)',
|
101
99
|
},
|
102
100
|
|
103
101
|
/**
|