@dxos/react-ui-editor 0.3.11-main.08776d7 → 0.3.11-main.0999ea0

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 (160) hide show
  1. package/dist/lib/browser/index.mjs +1211 -1005
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +12 -17
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +13 -7
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +1 -1
  9. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -0
  11. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  14. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  15. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  16. package/dist/types/src/extensions/automerge/automerge.spec.d.ts +2 -0
  17. package/dist/types/src/extensions/automerge/automerge.spec.d.ts.map +1 -0
  18. package/dist/types/src/extensions/automerge/automerge.test.d.ts +2 -0
  19. package/dist/types/src/extensions/automerge/automerge.test.d.ts.map +1 -0
  20. package/dist/types/src/extensions/automerge/defs.d.ts +6 -6
  21. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/semaphore.d.ts +6 -6
  23. package/dist/types/src/extensions/automerge/semaphore.d.ts.map +1 -1
  24. package/dist/types/src/extensions/automerge/update-automerge.d.ts +2 -2
  25. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  26. package/dist/types/src/extensions/awareness.d.ts +2 -2
  27. package/dist/types/src/extensions/awareness.d.ts.map +1 -1
  28. package/dist/types/src/extensions/basic.d.ts +4 -5
  29. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  30. package/dist/types/src/extensions/comments.d.ts +11 -3
  31. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  32. package/dist/types/src/extensions/index.d.ts +0 -8
  33. package/dist/types/src/extensions/index.d.ts.map +1 -1
  34. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
  35. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  37. package/dist/types/src/extensions/markdown/formatting.d.ts +11 -0
  38. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  39. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  40. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  41. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  42. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  43. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  44. package/dist/types/src/extensions/markdown/index.d.ts +8 -0
  45. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  46. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  47. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  48. package/dist/types/src/extensions/markdown/tasklist.d.ts +3 -0
  49. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  50. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  51. package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
  52. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
  53. package/dist/types/src/hooks/defs.d.ts +1 -4
  54. package/dist/types/src/hooks/defs.d.ts.map +1 -1
  55. package/dist/types/src/hooks/index.d.ts +2 -2
  56. package/dist/types/src/hooks/index.d.ts.map +1 -1
  57. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  58. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  59. package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
  60. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  61. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  62. package/dist/types/src/styles/markdown.d.ts +2 -2
  63. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  64. package/dist/types/src/themes/default.d.ts.map +1 -1
  65. package/dist/types/src/util.d.ts +2 -0
  66. package/dist/types/src/util.d.ts.map +1 -1
  67. package/package.json +45 -36
  68. package/src/components/TextEditor/MarkdownEditor.stories.tsx +61 -72
  69. package/src/components/TextEditor/TextEditor.tsx +101 -77
  70. package/src/components/TextEditor/comments.stories.tsx +16 -21
  71. package/src/components/Toolbar/Toolbar.stories.tsx +86 -0
  72. package/src/components/Toolbar/Toolbar.tsx +115 -0
  73. package/src/extensions/autocomplete.ts +6 -3
  74. package/src/extensions/automerge/automerge.spec.tsx +76 -0
  75. package/src/extensions/automerge/automerge.test.ts +13 -0
  76. package/src/extensions/automerge/automerge.ts +53 -13
  77. package/src/extensions/automerge/cursor.ts +3 -3
  78. package/src/extensions/automerge/defs.ts +9 -29
  79. package/src/extensions/automerge/semaphore.ts +24 -28
  80. package/src/extensions/automerge/update-automerge.ts +14 -8
  81. package/src/extensions/automerge/update-codemirror.ts +5 -5
  82. package/src/extensions/awareness.ts +17 -17
  83. package/src/extensions/basic.ts +22 -17
  84. package/src/extensions/comments.ts +149 -92
  85. package/src/extensions/index.ts +0 -8
  86. package/src/extensions/markdown/bundle.ts +20 -30
  87. package/src/extensions/markdown/code.ts +142 -0
  88. package/src/extensions/markdown/formatting.ts +159 -0
  89. package/src/extensions/markdown/heading.ts +59 -0
  90. package/src/extensions/markdown/highlight.ts +9 -15
  91. package/src/extensions/markdown/hr.ts +71 -0
  92. package/src/extensions/markdown/index.ts +8 -0
  93. package/src/extensions/{link.ts → markdown/link.ts} +5 -2
  94. package/src/extensions/{tasklist.ts → markdown/tasklist.ts} +48 -34
  95. package/src/extensions/mention.ts +9 -2
  96. package/src/hooks/awareness-provider.ts +24 -20
  97. package/src/hooks/defs.ts +1 -6
  98. package/src/hooks/index.ts +2 -2
  99. package/src/hooks/useActionHandler.ts +45 -0
  100. package/src/hooks/useTextEditor.ts +29 -0
  101. package/src/hooks/useTextModel.ts +48 -11
  102. package/src/styles/markdown.ts +9 -10
  103. package/src/themes/default.ts +1 -3
  104. package/src/util.ts +26 -3
  105. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  106. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  107. package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -12
  108. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  109. package/dist/types/src/extensions/code.d.ts.map +0 -1
  110. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  111. package/dist/types/src/extensions/image.d.ts.map +0 -1
  112. package/dist/types/src/extensions/link.d.ts.map +0 -1
  113. package/dist/types/src/extensions/sync.d.ts +0 -7
  114. package/dist/types/src/extensions/sync.d.ts.map +0 -1
  115. package/dist/types/src/extensions/table.d.ts.map +0 -1
  116. package/dist/types/src/extensions/tasklist.d.ts +0 -7
  117. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  118. package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
  119. package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
  120. package/dist/types/src/extensions/yjs/index.d.ts +0 -2
  121. package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
  122. package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
  123. package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
  124. package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
  125. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
  126. package/dist/types/src/hooks/automerge.d.ts +0 -4
  127. package/dist/types/src/hooks/automerge.d.ts.map +0 -1
  128. package/dist/types/src/hooks/yjs.d.ts +0 -28
  129. package/dist/types/src/hooks/yjs.d.ts.map +0 -1
  130. package/dist/types/src/testing/index.d.ts +0 -3
  131. package/dist/types/src/testing/index.d.ts.map +0 -1
  132. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  133. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  134. package/dist/types/src/testing/proto/index.d.ts +0 -2
  135. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  136. package/dist/types/src/testing/replicator.d.ts +0 -45
  137. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  138. package/src/components/TextEditor/codemirror.stories.ts +0 -115
  139. package/src/components/TextEditor/yjs.stories.tsx +0 -56
  140. package/src/extensions/code.ts +0 -104
  141. package/src/extensions/hr.ts +0 -72
  142. package/src/extensions/sync.ts +0 -15
  143. package/src/extensions/yjs/cursor.ts +0 -22
  144. package/src/extensions/yjs/index.ts +0 -5
  145. package/src/extensions/yjs/yjs.test.ts +0 -35
  146. package/src/extensions/yjs/yjs.ts +0 -16
  147. package/src/hooks/automerge.ts +0 -53
  148. package/src/hooks/yjs.ts +0 -157
  149. package/src/testing/index.ts +0 -6
  150. package/src/testing/proto/gen/schema.ts +0 -49
  151. package/src/testing/proto/index.ts +0 -5
  152. package/src/testing/proto/schema.proto +0 -15
  153. package/src/testing/replicator.ts +0 -184
  154. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  155. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  156. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  157. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  158. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  159. /package/src/extensions/{image.ts → markdown/image.ts} +0 -0
  160. /package/src/extensions/{table.ts → markdown/table.ts} +0 -0
@@ -7,79 +7,75 @@
7
7
  import { type StateField } from '@codemirror/state';
8
8
  import { type EditorView } from '@codemirror/view';
9
9
 
10
- import { next as automerge } from '@dxos/automerge/automerge';
10
+ import { next as A } from '@dxos/automerge/automerge';
11
11
 
12
12
  import {
13
13
  getLastHeads,
14
14
  getPath,
15
- isReconcileTx,
16
- reconcileAnnotationType,
15
+ isReconcile,
16
+ reconcileAnnotation,
17
17
  updateHeads,
18
18
  type IDocHandle,
19
- type Value,
19
+ type State,
20
20
  } from './defs';
21
21
  import { updateAutomerge } from './update-automerge';
22
22
  import { updateCodeMirror } from './update-codemirror';
23
- import { type Syncer } from '../sync';
24
23
 
25
24
  /**
26
- * Implements three-way merge (on each mutation/keystroke).
25
+ * Implements three-way merge (on each mutation).
27
26
  */
28
- export class PatchSemaphore implements Syncer {
27
+ export class PatchSemaphore {
29
28
  private _inReconcile = false;
30
29
 
31
30
  // prettier-ignore
32
31
  constructor(
33
32
  private readonly _handle: IDocHandle,
34
- // TODO(burdon): Rename.
35
- private readonly _field: StateField<Value>
33
+ private readonly _state: StateField<State>
36
34
  ) {}
37
35
 
38
- // NOTE: Cannot destruct view.state.
39
36
  reconcile(view: EditorView) {
40
- if (this._inReconcile) {
41
- return;
37
+ if (!this._inReconcile) {
38
+ this._inReconcile = true;
39
+ this.doReconcile(view);
40
+ this._inReconcile = false;
42
41
  }
43
- this._inReconcile = true;
42
+ }
44
43
 
45
- const path = getPath(view.state, this._field);
44
+ private doReconcile(view: EditorView) {
45
+ const path = getPath(view.state, this._state);
46
46
 
47
47
  // Get the heads before the unreconciled transactions are applied.
48
- const oldHeads = getLastHeads(view.state, this._field);
48
+ const oldHeads = getLastHeads(view.state, this._state);
49
49
  let selection = view.state.selection;
50
50
 
51
51
  // First undo all the unreconciled transactions.
52
- const transactions = view.state.field(this._field).unreconciledTransactions.filter((tx) => !isReconcileTx(tx));
52
+ const transactions = view.state.field(this._state).unreconciledTransactions.filter((tx) => !isReconcile(tx));
53
53
  const toInvert = transactions.slice().reverse();
54
- for (const tx of toInvert) {
55
- const inverted = tx.changes.invert(tx.startState.doc);
54
+ for (const tr of toInvert) {
55
+ const inverted = tr.changes.invert(tr.startState.doc);
56
56
  selection = selection.map(inverted);
57
57
  view.dispatch({
58
58
  changes: inverted,
59
- annotations: reconcileAnnotationType.of(true),
59
+ annotations: reconcileAnnotation.of(true),
60
60
  });
61
61
  }
62
62
 
63
63
  // Apply the unreconciled transactions to the document.
64
- let newHeads = updateAutomerge(this._field, this._handle, transactions, view.state);
65
-
66
64
  // NOTE: null and undefined each come from automerge and repo respectively.
65
+ let newHeads = updateAutomerge(this._state, this._handle, transactions, view.state);
67
66
  if (newHeads === null || newHeads === undefined) {
68
67
  // TODO(alexjg): this is the call that's resetting the editor state on click.
69
- newHeads = automerge.getHeads(this._handle.docSync()!);
68
+ newHeads = A.getHeads(this._handle.docSync()!);
70
69
  }
71
70
 
72
71
  // Now get the diff between the updated state of the document and the heads and apply that to the codemirror doc.
73
- const diff = automerge.equals(oldHeads, newHeads)
74
- ? []
75
- : automerge.diff(this._handle.docSync()!, oldHeads, newHeads);
72
+ const diff = A.equals(oldHeads, newHeads) ? [] : A.diff(this._handle.docSync()!, oldHeads, newHeads);
76
73
  updateCodeMirror(view, selection, path, diff);
77
74
 
75
+ // Update automerge state.
78
76
  view.dispatch({
79
77
  effects: updateHeads(newHeads),
80
- annotations: reconcileAnnotationType.of({}),
78
+ annotations: reconcileAnnotation.of(false),
81
79
  });
82
-
83
- this._inReconcile = false;
84
80
  }
85
81
  }
@@ -4,17 +4,17 @@
4
4
  // Ref: https://github.com/automerge/automerge-codemirror
5
5
  //
6
6
 
7
- import { type EditorState, type StateField, type Text, type Transaction } from '@codemirror/state';
7
+ import { type EditorState, type StateField, type Transaction, type Text } from '@codemirror/state';
8
8
 
9
- import { next as am, type Heads } from '@dxos/automerge/automerge';
9
+ import { next as A, type Heads } from '@dxos/automerge/automerge';
10
10
 
11
- import { type IDocHandle, type Value } from './defs';
11
+ import { type IDocHandle, type State } from './defs';
12
12
 
13
13
  export const updateAutomerge = (
14
- field: StateField<Value>,
14
+ field: StateField<State>,
15
15
  handle: IDocHandle,
16
16
  transactions: Transaction[],
17
- state: EditorState,
17
+ state: EditorState, // TODO(burdon): Just pass in the state field value?
18
18
  ): Heads | undefined => {
19
19
  const { lastHeads, path } = state.field(field);
20
20
 
@@ -31,12 +31,18 @@ export const updateAutomerge = (
31
31
  return undefined;
32
32
  }
33
33
 
34
- const newHeads = handle.changeAt(lastHeads, (doc: am.Doc<unknown>) => {
34
+ const newHeads = handle.changeAt(lastHeads, (doc: A.Doc<unknown>) => {
35
+ const invertedTransactions: { from: number; del: number; insert: Text }[] = [];
35
36
  for (const tr of transactions) {
36
- tr.changes.iterChanges((fromA: number, toA: number, _fromB: number, _toB: number, inserted: Text) => {
37
- am.splice(doc, path, fromA, toA - fromA, inserted.toString());
37
+ tr.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
38
+ invertedTransactions.push({ from: fromA, del: toA - fromA, insert });
38
39
  });
39
40
  }
41
+
42
+ // TODO(burdon): Hack to apply in reverse order to properly apply range.
43
+ invertedTransactions.reverse().forEach(({ from, del, insert }) => {
44
+ A.splice(doc, path.slice(), from, del, insert.toString());
45
+ });
40
46
  });
41
47
 
42
48
  return newHeads ?? undefined;
@@ -16,7 +16,7 @@ import {
16
16
  type SpliceTextPatch,
17
17
  } from '@dxos/automerge/automerge';
18
18
 
19
- import { reconcileAnnotationType } from './defs';
19
+ import { reconcileAnnotation } from './defs';
20
20
 
21
21
  export const updateCodeMirror = (view: EditorView, selection: EditorSelection, target: Prop[], patches: Patch[]) => {
22
22
  for (const patch of patches) {
@@ -26,14 +26,14 @@ export const updateCodeMirror = (view: EditorView, selection: EditorSelection, t
26
26
  selection = selection.map(changeSet, 1);
27
27
  view.dispatch({
28
28
  changes: changeSet,
29
- annotations: reconcileAnnotationType.of({}),
29
+ annotations: reconcileAnnotation.of(false),
30
30
  });
31
31
  }
32
32
  }
33
33
 
34
34
  view.dispatch({
35
35
  selection,
36
- annotations: reconcileAnnotationType.of({}),
36
+ annotations: reconcileAnnotation.of(false),
37
37
  });
38
38
  };
39
39
 
@@ -57,7 +57,7 @@ const handleInsert = (target: Prop[], patch: InsertPatch): Array<ChangeSpec> =>
57
57
  return [];
58
58
  }
59
59
 
60
- const text = patch.values.map((v) => (v ? v.toString() : '')).join('');
60
+ const text = patch.values.map((value) => (value ? value.toString() : '')).join('');
61
61
  return [{ from: index, to: index, insert: text }];
62
62
  };
63
63
 
@@ -88,7 +88,7 @@ const handlePut = (target: Prop[], patch: PutPatch, state: EditorState): Array<C
88
88
 
89
89
  const length = state.doc.length;
90
90
  if (typeof patch.value !== 'string') {
91
- return []; // TODO(dmaretskyi): How to handle non string values?
91
+ return []; // TODO(dmaretskyi): How to handle non-string values?
92
92
  }
93
93
 
94
94
  return [{ from: 0, to: length, insert: patch.value as any }];
@@ -24,8 +24,8 @@ export interface AwarenessProvider {
24
24
  open(): void;
25
25
  close(): void;
26
26
 
27
- update(position: AwarenessPosition | undefined): void;
28
27
  getRemoteStates(): AwarenessState[];
28
+ update(position: AwarenessPosition | undefined): void;
29
29
  }
30
30
 
31
31
  const dummyProvider: AwarenessProvider = {
@@ -34,11 +34,11 @@ const dummyProvider: AwarenessProvider = {
34
34
  open: () => {},
35
35
  close: () => {},
36
36
 
37
- update: () => {},
38
37
  getRemoteStates: () => [],
38
+ update: () => {},
39
39
  };
40
40
 
41
- export const AwarenessProvider = Facet.define<AwarenessProvider, AwarenessProvider>({
41
+ export const awarenessProvider = Facet.define<AwarenessProvider, AwarenessProvider>({
42
42
  combine: (providers) => providers[0] ?? dummyProvider,
43
43
  });
44
44
 
@@ -66,10 +66,9 @@ export type AwarenessState = {
66
66
  /**
67
67
  * Extension provides presence information about other peers.
68
68
  */
69
- // TODO(burdon): Why provide default?
70
69
  export const awareness = (provider = dummyProvider): Extension => {
71
70
  return [
72
- AwarenessProvider.of(provider),
71
+ awarenessProvider.of(provider),
73
72
  ViewPlugin.fromClass(RemoteSelectionsDecorator, {
74
73
  decorations: (value) => value.decorations,
75
74
  }),
@@ -92,7 +91,7 @@ export class RemoteSelectionsDecorator implements PluginValue {
92
91
 
93
92
  constructor(view: EditorView) {
94
93
  this._cursorConverter = view.state.facet(Cursor.converter);
95
- this._provider = view.state.facet(AwarenessProvider);
94
+ this._provider = view.state.facet(awarenessProvider);
96
95
  this._provider.open();
97
96
  this._provider.remoteStateChange.on(this._ctx, () => {
98
97
  view.dispatch({ annotations: [RemoteSelectionChangedAnnotation.of([])] });
@@ -251,7 +250,6 @@ class RemoteCaretWidget extends WidgetType {
251
250
  }
252
251
  }
253
252
 
254
- // TODO(burdon): Mixed case?
255
253
  const styles = EditorView.baseTheme({
256
254
  '.cm-collab-selection': {},
257
255
  '.cm-collab-selectionLine': {
@@ -266,38 +264,40 @@ const styles = EditorView.baseTheme({
266
264
  marginRight: '-1px',
267
265
  boxSizing: 'border-box',
268
266
  display: 'inline',
267
+ cursor: 'pointer',
269
268
  },
270
269
  '.cm-collab-selectionCaretDot': {
271
270
  borderRadius: '50%',
272
271
  position: 'absolute',
273
- width: '.4em',
274
- height: '.4em',
275
- top: '-.2em',
276
- left: '-.2em',
272
+ width: '.5em',
273
+ height: '.5em',
274
+ top: '-.25em',
275
+ left: '-.25em',
277
276
  backgroundColor: 'inherit',
278
277
  transition: 'transform .3s ease-in-out',
279
278
  boxSizing: 'border-box',
280
279
  },
281
280
  '.cm-collab-selectionCaret:hover > .cm-collab-selectionCaretDot': {
282
- transformOrigin: 'bottom center',
283
281
  transform: 'scale(0)',
282
+ transformOrigin: 'center',
284
283
  },
285
284
  '.cm-collab-selectionInfo': {
286
285
  position: 'absolute',
287
- top: '-1.05em',
288
- left: '-1px',
286
+ transform: 'translate(-50%, 0)',
287
+ top: '-20px',
288
+ left: 0,
289
289
  fontSize: '.75em',
290
- fontFamily: 'serif',
290
+ fontFamily: 'sans-serif',
291
291
  fontStyle: 'normal',
292
292
  fontWeight: 'normal',
293
293
  lineHeight: 'normal',
294
294
  userSelect: 'none',
295
295
  color: 'white',
296
- paddingLeft: '2px',
297
- paddingRight: '2px',
296
+ padding: '2px',
298
297
  zIndex: 101,
299
298
  transition: 'opacity .3s ease-in-out',
300
299
  backgroundColor: 'inherit',
300
+ borderRadius: '2px',
301
301
  // These should be separate.
302
302
  opacity: 0,
303
303
  transitionDelay: '0s',
@@ -3,30 +3,35 @@
3
3
  //
4
4
 
5
5
  import { closeBrackets } from '@codemirror/autocomplete';
6
+ import { history } from '@codemirror/commands';
6
7
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
7
8
  import { type Extension } from '@codemirror/state';
8
9
  import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
9
- import { drawSelection, EditorView, placeholder } from '@codemirror/view';
10
+ import { drawSelection, EditorView, highlightActiveLine, placeholder } from '@codemirror/view';
10
11
 
11
- import type { ThemeMode } from '@dxos/react-ui';
12
+ import { type ThemeMode } from '@dxos/react-ui';
13
+
14
+ import { type TextEditorProps } from '../components';
12
15
 
13
16
  export type BasicBundleOptions = {
14
- readonly?: boolean;
15
17
  themeMode?: ThemeMode;
16
- placeholder?: string;
17
- };
18
+ } & Pick<TextEditorProps, 'placeholder' | 'lineWrapping'>;
18
19
 
19
- export const basicBundle = ({ readonly, themeMode, placeholder: _placeholder }: BasicBundleOptions): Extension[] =>
20
- [
21
- EditorView.lineWrapping,
20
+ export const basicBundle = ({
21
+ themeMode,
22
+ placeholder: _placeholder,
23
+ lineWrapping = true,
24
+ }: BasicBundleOptions): Extension[] => [
25
+ lineWrapping ? EditorView.lineWrapping : [],
22
26
 
23
- // TODO(burdon): Compare with minimalSetup.
24
- // https://codemirror.net/docs/ref/#codemirror.minimalSetup
25
- bracketMatching(),
26
- closeBrackets(),
27
- drawSelection(),
28
- _placeholder && placeholder(_placeholder),
27
+ // https://codemirror.net/docs/ref/#codemirror.minimalSetup
28
+ bracketMatching(),
29
+ closeBrackets(),
30
+ drawSelection(),
31
+ highlightActiveLine(),
32
+ history(),
33
+ _placeholder ? placeholder(_placeholder) : [],
29
34
 
30
- // TODO(burdon): Is this required?
31
- themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
32
- ].filter(Boolean) as Extension[];
35
+ // https://github.com/codemirror/theme-one-dark
36
+ themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
37
+ ];