@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.
Files changed (116) hide show
  1. package/dist/lib/browser/index.mjs +270 -153
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/TextEditor.stories.d.ts +1 -4
  5. package/dist/types/src/TextEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/defaults.d.ts.map +1 -1
  7. package/dist/types/src/extensions/autocomplete.d.ts +1 -2
  8. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  9. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  10. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  11. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  13. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  14. package/dist/types/src/extensions/awareness/awareness.d.ts +2 -2
  15. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  16. package/dist/types/src/extensions/command/state.d.ts +2 -2
  17. package/dist/types/src/extensions/command/state.d.ts.map +1 -1
  18. package/dist/types/src/extensions/comments.d.ts +1 -1
  19. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  20. package/dist/types/src/{state → extensions}/cursor.d.ts +2 -2
  21. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  22. package/dist/types/src/extensions/debug.d.ts +2 -2
  23. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  24. package/dist/types/src/extensions/doc.d.ts +6 -0
  25. package/dist/types/src/extensions/doc.d.ts.map +1 -0
  26. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  27. package/dist/types/src/extensions/index.d.ts +4 -0
  28. package/dist/types/src/extensions/index.d.ts.map +1 -1
  29. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  30. package/dist/types/src/extensions/markdown/formatting.test.d.ts.map +1 -1
  31. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  32. package/dist/types/src/extensions/markdown/link.d.ts +1 -1
  33. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  34. package/dist/types/src/extensions/markdown/styles.d.ts.map +1 -1
  35. package/dist/types/src/extensions/modes.d.ts +3 -3
  36. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  37. package/dist/types/src/extensions/state.d.ts.map +1 -0
  38. package/dist/types/src/extensions/types.d.ts.map +1 -0
  39. package/dist/types/src/extensions/util/overlap.d.ts +1 -1
  40. package/dist/types/src/extensions/util/overlap.d.ts.map +1 -1
  41. package/dist/types/src/extensions/util/react.d.ts +1 -1
  42. package/dist/types/src/extensions/util/react.d.ts.map +1 -1
  43. package/dist/types/src/hooks/useTextEditor.d.ts +1 -1
  44. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  45. package/dist/types/src/index.d.ts +0 -1
  46. package/dist/types/src/index.d.ts.map +1 -1
  47. package/dist/types/src/styles/markdown.d.ts +2 -1
  48. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  49. package/dist/types/src/styles/theme.d.ts.map +1 -1
  50. package/package.json +37 -54
  51. package/src/TextEditor.stories.tsx +17 -22
  52. package/src/defaults.ts +2 -0
  53. package/src/extensions/annotations.ts +1 -1
  54. package/src/extensions/autocomplete.ts +8 -9
  55. package/src/extensions/automerge/{automerge.test.tsx → automerge.spec.tsx} +0 -1
  56. package/src/extensions/automerge/automerge.stories.tsx +1 -1
  57. package/src/extensions/automerge/automerge.test.ts +13 -0
  58. package/src/extensions/automerge/automerge.ts +2 -2
  59. package/src/extensions/automerge/cursor.ts +1 -1
  60. package/src/extensions/awareness/awareness.ts +5 -3
  61. package/src/extensions/command/state.ts +4 -3
  62. package/src/extensions/comments.ts +43 -37
  63. package/src/{state → extensions}/cursor.ts +6 -3
  64. package/src/extensions/debug.ts +2 -2
  65. package/src/extensions/doc.ts +17 -0
  66. package/src/extensions/folding.tsx +2 -4
  67. package/src/extensions/index.ts +4 -0
  68. package/src/extensions/markdown/changes.test.ts +3 -1
  69. package/src/extensions/markdown/decorate.ts +6 -49
  70. package/src/extensions/markdown/formatting.test.ts +3 -1
  71. package/src/extensions/markdown/highlight.ts +5 -0
  72. package/src/extensions/markdown/link.ts +2 -3
  73. package/src/extensions/markdown/parser.test.ts +2 -1
  74. package/src/extensions/markdown/styles.ts +0 -10
  75. package/src/extensions/markdown/table.ts +3 -3
  76. package/src/extensions/modes.ts +5 -6
  77. package/src/{state → extensions}/state.ts +0 -1
  78. package/src/extensions/util/overlap.ts +1 -1
  79. package/src/extensions/util/react.tsx +1 -5
  80. package/src/hooks/useTextEditor.ts +2 -3
  81. package/src/index.ts +0 -1
  82. package/src/styles/markdown.ts +3 -1
  83. package/src/styles/theme.ts +1 -3
  84. package/dist/lib/browser/chunk-AZWYO7TE.mjs +0 -148
  85. package/dist/lib/browser/chunk-AZWYO7TE.mjs.map +0 -7
  86. package/dist/lib/browser/state/index.mjs +0 -17
  87. package/dist/lib/browser/state/index.mjs.map +0 -7
  88. package/dist/lib/node/chunk-5RSKGJRI.cjs +0 -169
  89. package/dist/lib/node/chunk-5RSKGJRI.cjs.map +0 -7
  90. package/dist/lib/node/index.cjs +0 -5480
  91. package/dist/lib/node/index.cjs.map +0 -7
  92. package/dist/lib/node/meta.json +0 -1
  93. package/dist/lib/node/state/index.cjs +0 -39
  94. package/dist/lib/node/state/index.cjs.map +0 -7
  95. package/dist/lib/node-esm/chunk-RCIWLRIY.mjs +0 -150
  96. package/dist/lib/node-esm/chunk-RCIWLRIY.mjs.map +0 -7
  97. package/dist/lib/node-esm/index.mjs +0 -5472
  98. package/dist/lib/node-esm/index.mjs.map +0 -7
  99. package/dist/lib/node-esm/meta.json +0 -1
  100. package/dist/lib/node-esm/state/index.mjs +0 -18
  101. package/dist/lib/node-esm/state/index.mjs.map +0 -7
  102. package/dist/types/src/state/cursor.d.ts.map +0 -1
  103. package/dist/types/src/state/doc.d.ts +0 -5
  104. package/dist/types/src/state/doc.d.ts.map +0 -1
  105. package/dist/types/src/state/index.d.ts +0 -6
  106. package/dist/types/src/state/index.d.ts.map +0 -1
  107. package/dist/types/src/state/state.d.ts.map +0 -1
  108. package/dist/types/src/state/types.d.ts.map +0 -1
  109. package/dist/types/src/state/util.d.ts +0 -3
  110. package/dist/types/src/state/util.d.ts.map +0 -1
  111. package/src/state/doc.ts +0 -10
  112. package/src/state/index.ts +0 -11
  113. package/src/state/util.ts +0 -13
  114. /package/dist/types/src/{state → extensions}/state.d.ts +0 -0
  115. /package/dist/types/src/{state → extensions}/types.d.ts +0 -0
  116. /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 = singleValueFacet<CommentsOptions>();
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
- options.id ? documentId.of(options.id) : undefined,
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
- key: shortcut,
401
- run: callbackWrapper(createComment),
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
- (view, pos) => {
412
- const selection = view.state.selection.main;
413
- if (selection && pos >= selection.from && pos <= selection.to) {
414
- return {
415
- pos: selection.from,
416
- end: selection.to,
417
- above: true,
418
- create: () => {
419
- const el = document.createElement('div');
420
- options.onHover!(el, shortcut);
421
- return { dom: el, offset: { x: 0, y: 8 } };
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
- return null;
427
- },
428
- {
429
- // TODO(burdon): Hide on change triggered immediately?
430
- // hideOnChange: true,
431
- hoverTime: 1_000,
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 && trackPastedComments(options.onUpdate),
509
- ].filter(nonNullable);
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 = singleValueFacet(defaultCursorConverter);
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);
@@ -3,10 +3,10 @@
3
3
  //
4
4
 
5
5
  import { syntaxTree } from '@codemirror/language';
6
- import { type EditorState, type Extension, type RangeSet, StateField, type Transaction } from '@codemirror/state';
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): Extension => {
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 (to 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
- el,
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
  },
@@ -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';
@@ -2,7 +2,9 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { describe, expect, test } from 'vitest';
5
+ import { expect } from 'chai';
6
+
7
+ import { describe, test } from '@dxos/test';
6
8
 
7
9
  import { createLinkLabel } from './changes';
8
10
 
@@ -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: HTMLElement, url: string) => void,
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
- // let count = 0;
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 isFirst = block.from <= node.from;
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
- isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine,
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 { describe, expect, test } from 'vitest';
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: HTMLElement, url: string) => void) => {
13
- return hoverTooltip((view, pos, side) => {
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
- import { describe, test } from 'vitest';
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<Decoration>>({
28
+ return StateField.define<RangeSet<any>>({
29
29
  create: (state) => update(state, options),
30
- update: (_: RangeSet<Decoration>, tr: Transaction) => update(tr.state, options),
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<Decoration>();
43
+ const builder = new RangeSetBuilder();
44
44
  const cursor = state.selection.main.head;
45
45
 
46
46
  const tables: Table[] = [];
@@ -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?: string;
18
+ type: string;
22
19
  noTabster?: boolean;
23
20
  };
24
21
 
25
- export const editorInputMode = singleValueFacet<EditorInputConfig>({});
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
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { type Range } from '../../state';
5
+ import { type Range } from '../types';
6
6
 
7
7
  /**
8
8
  * Determines if two ranges overlap.
@@ -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
- // TODO(burdon): Remove react rendering; use DOM directly.
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
@@ -14,7 +14,6 @@ export * from './components';
14
14
  export * from './defaults';
15
15
  export * from './extensions';
16
16
  export * from './hooks';
17
- export * from './state';
18
17
  export * from './util';
19
18
 
20
19
  export { translations };
@@ -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
- mark: 'opacity-50',
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
  },
@@ -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-cmActiveLine)',
98
+ background: 'var(--dx-hoverSurface)',
101
99
  },
102
100
 
103
101
  /**