@dxos/react-ui-editor 0.3.11-main.6f25445 → 0.3.11-main.73ee990

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 (180) hide show
  1. package/dist/lib/browser/index.mjs +1430 -1335
  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 +16 -13
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +20 -17
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +6 -6
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/comments.stories.d.ts +26 -0
  11. package/dist/types/src/components/TextEditor/comments.stories.d.ts.map +1 -0
  12. package/dist/types/src/components/Toolbar/Toolbar.d.ts +9 -0
  13. package/dist/types/src/components/Toolbar/Toolbar.d.ts.map +1 -0
  14. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts +14 -0
  15. package/dist/types/src/components/Toolbar/Toolbar.stories.d.ts.map +1 -0
  16. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  17. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  18. package/dist/types/src/extensions/automerge/cursor.d.ts +1 -1
  19. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  20. package/dist/types/src/extensions/automerge/defs.d.ts +7 -9
  21. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  22. package/dist/types/src/extensions/automerge/semaphore.d.ts +7 -12
  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 +1 -1
  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 +37 -20
  31. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  32. package/dist/types/src/{util → extensions}/cursor.d.ts +10 -2
  33. package/dist/types/src/extensions/cursor.d.ts.map +1 -0
  34. package/dist/types/src/extensions/index.d.ts +2 -8
  35. package/dist/types/src/extensions/index.d.ts.map +1 -1
  36. package/dist/types/src/extensions/markdown/bundle.d.ts +3 -6
  37. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  38. package/dist/types/src/extensions/markdown/code.d.ts.map +1 -0
  39. package/dist/types/src/extensions/markdown/formatting.d.ts +11 -0
  40. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -0
  41. package/dist/types/src/extensions/markdown/heading.d.ts +6 -0
  42. package/dist/types/src/extensions/markdown/heading.d.ts.map +1 -0
  43. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  44. package/dist/types/src/extensions/markdown/hr.d.ts.map +1 -0
  45. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -0
  46. package/dist/types/src/extensions/markdown/index.d.ts +8 -0
  47. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  48. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -0
  49. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -0
  50. package/dist/types/src/extensions/markdown/tasklist.d.ts.map +1 -0
  51. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  52. package/dist/types/src/extensions/outliner.d.ts +4 -0
  53. package/dist/types/src/extensions/outliner.d.ts.map +1 -0
  54. package/dist/types/src/hooks/awareness-provider.d.ts +7 -4
  55. package/dist/types/src/hooks/awareness-provider.d.ts.map +1 -1
  56. package/dist/types/src/hooks/defs.d.ts +25 -0
  57. package/dist/types/src/hooks/defs.d.ts.map +1 -0
  58. package/dist/types/src/hooks/index.d.ts +3 -2
  59. package/dist/types/src/hooks/index.d.ts.map +1 -1
  60. package/dist/types/src/hooks/useActionHandler.d.ts +4 -0
  61. package/dist/types/src/hooks/useActionHandler.d.ts.map +1 -0
  62. package/dist/types/src/hooks/useTextEditor.d.ts +17 -0
  63. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -0
  64. package/dist/types/src/hooks/useTextModel.d.ts +2 -32
  65. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  66. package/dist/types/src/index.d.ts +3 -0
  67. package/dist/types/src/index.d.ts.map +1 -1
  68. package/dist/types/src/styles/markdown.d.ts +2 -2
  69. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  70. package/dist/types/src/styles/tokens.d.ts +1 -0
  71. package/dist/types/src/styles/tokens.d.ts.map +1 -1
  72. package/dist/types/src/themes/default.d.ts.map +1 -1
  73. package/dist/types/src/util.d.ts +7 -0
  74. package/dist/types/src/util.d.ts.map +1 -0
  75. package/package.json +34 -36
  76. package/src/components/TextEditor/MarkdownEditor.stories.tsx +83 -89
  77. package/src/components/TextEditor/TextEditor.stories.tsx +12 -46
  78. package/src/components/TextEditor/TextEditor.tsx +123 -93
  79. package/src/components/TextEditor/comments.stories.tsx +357 -0
  80. package/src/components/Toolbar/Toolbar.stories.tsx +86 -0
  81. package/src/components/Toolbar/Toolbar.tsx +115 -0
  82. package/src/extensions/autocomplete.ts +23 -19
  83. package/src/extensions/automerge/automerge.ts +40 -47
  84. package/src/extensions/automerge/cursor.ts +2 -1
  85. package/src/extensions/automerge/defs.ts +10 -31
  86. package/src/extensions/automerge/semaphore.ts +23 -20
  87. package/src/extensions/automerge/update-automerge.ts +14 -8
  88. package/src/extensions/awareness.ts +40 -46
  89. package/src/extensions/basic.ts +22 -17
  90. package/src/extensions/comments.ts +278 -267
  91. package/src/extensions/cursor.ts +50 -0
  92. package/src/extensions/index.ts +2 -8
  93. package/src/extensions/markdown/bundle.ts +20 -30
  94. package/src/extensions/markdown/code.ts +142 -0
  95. package/src/extensions/markdown/formatting.ts +159 -0
  96. package/src/extensions/markdown/heading.ts +59 -0
  97. package/src/extensions/markdown/highlight.ts +11 -26
  98. package/src/extensions/markdown/hr.ts +71 -0
  99. package/src/extensions/markdown/image.ts +87 -0
  100. package/src/extensions/markdown/index.ts +8 -0
  101. package/src/extensions/{link.ts → markdown/link.ts} +87 -86
  102. package/src/extensions/{table.ts → markdown/table.ts} +10 -4
  103. package/src/extensions/markdown/tasklist.ts +110 -0
  104. package/src/extensions/mention.ts +9 -2
  105. package/src/extensions/outliner.ts +12 -0
  106. package/src/hooks/awareness-provider.ts +24 -20
  107. package/src/hooks/defs.ts +42 -0
  108. package/src/hooks/index.ts +4 -2
  109. package/src/hooks/useActionHandler.ts +45 -0
  110. package/src/hooks/useTextEditor.ts +29 -0
  111. package/src/hooks/useTextModel.ts +48 -60
  112. package/src/index.ts +3 -0
  113. package/src/styles/markdown.ts +9 -12
  114. package/src/styles/tokens.ts +3 -0
  115. package/src/themes/default.ts +50 -79
  116. package/src/util.ts +41 -0
  117. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts +0 -12
  118. package/dist/types/src/components/TextEditor/codemirror.stories.d.ts.map +0 -1
  119. package/dist/types/src/components/TextEditor/yjs.stories.d.ts +0 -12
  120. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  121. package/dist/types/src/extensions/code.d.ts.map +0 -1
  122. package/dist/types/src/extensions/hr.d.ts.map +0 -1
  123. package/dist/types/src/extensions/image.d.ts.map +0 -1
  124. package/dist/types/src/extensions/link.d.ts.map +0 -1
  125. package/dist/types/src/extensions/mermaid.d.ts +0 -3
  126. package/dist/types/src/extensions/mermaid.d.ts.map +0 -1
  127. package/dist/types/src/extensions/table.d.ts.map +0 -1
  128. package/dist/types/src/extensions/tasklist.d.ts.map +0 -1
  129. package/dist/types/src/extensions/yjs/cursor.d.ts +0 -4
  130. package/dist/types/src/extensions/yjs/cursor.d.ts.map +0 -1
  131. package/dist/types/src/extensions/yjs/index.d.ts +0 -2
  132. package/dist/types/src/extensions/yjs/index.d.ts.map +0 -1
  133. package/dist/types/src/extensions/yjs/yjs.d.ts +0 -4
  134. package/dist/types/src/extensions/yjs/yjs.d.ts.map +0 -1
  135. package/dist/types/src/extensions/yjs/yjs.test.d.ts +0 -2
  136. package/dist/types/src/extensions/yjs/yjs.test.d.ts.map +0 -1
  137. package/dist/types/src/hooks/automerge.d.ts +0 -3
  138. package/dist/types/src/hooks/automerge.d.ts.map +0 -1
  139. package/dist/types/src/hooks/yjs.d.ts +0 -27
  140. package/dist/types/src/hooks/yjs.d.ts.map +0 -1
  141. package/dist/types/src/testing/index.d.ts +0 -3
  142. package/dist/types/src/testing/index.d.ts.map +0 -1
  143. package/dist/types/src/testing/proto/gen/schema.d.ts +0 -15
  144. package/dist/types/src/testing/proto/gen/schema.d.ts.map +0 -1
  145. package/dist/types/src/testing/proto/index.d.ts +0 -2
  146. package/dist/types/src/testing/proto/index.d.ts.map +0 -1
  147. package/dist/types/src/testing/replicator.d.ts +0 -45
  148. package/dist/types/src/testing/replicator.d.ts.map +0 -1
  149. package/dist/types/src/util/cursor.d.ts.map +0 -1
  150. package/dist/types/src/util/index.d.ts +0 -3
  151. package/dist/types/src/util/index.d.ts.map +0 -1
  152. package/dist/types/src/util/util.d.ts +0 -5
  153. package/dist/types/src/util/util.d.ts.map +0 -1
  154. package/src/components/TextEditor/codemirror.stories.ts +0 -115
  155. package/src/components/TextEditor/yjs.stories.tsx +0 -56
  156. package/src/extensions/code.ts +0 -99
  157. package/src/extensions/hr.ts +0 -74
  158. package/src/extensions/image.ts +0 -69
  159. package/src/extensions/mermaid.ts +0 -11
  160. package/src/extensions/tasklist.ts +0 -121
  161. package/src/extensions/yjs/cursor.ts +0 -21
  162. package/src/extensions/yjs/index.ts +0 -5
  163. package/src/extensions/yjs/yjs.test.ts +0 -35
  164. package/src/extensions/yjs/yjs.ts +0 -16
  165. package/src/hooks/automerge.ts +0 -52
  166. package/src/hooks/yjs.ts +0 -178
  167. package/src/testing/index.ts +0 -6
  168. package/src/testing/proto/gen/schema.ts +0 -49
  169. package/src/testing/proto/index.ts +0 -5
  170. package/src/testing/proto/schema.proto +0 -15
  171. package/src/testing/replicator.ts +0 -184
  172. package/src/util/cursor.ts +0 -29
  173. package/src/util/index.ts +0 -6
  174. package/src/util/util.ts +0 -18
  175. /package/dist/types/src/extensions/{code.d.ts → markdown/code.d.ts} +0 -0
  176. /package/dist/types/src/extensions/{hr.d.ts → markdown/hr.d.ts} +0 -0
  177. /package/dist/types/src/extensions/{image.d.ts → markdown/image.d.ts} +0 -0
  178. /package/dist/types/src/extensions/{link.d.ts → markdown/link.d.ts} +0 -0
  179. /package/dist/types/src/extensions/{table.d.ts → markdown/table.d.ts} +0 -0
  180. /package/dist/types/src/extensions/{tasklist.d.ts → markdown/tasklist.d.ts} +0 -0
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { EditorState, type Extension } from '@codemirror/state';
5
+ import { EditorState, type Extension, type StateEffect } from '@codemirror/state';
6
6
  import { EditorView } from '@codemirror/view';
7
7
  import { useFocusableGroup } from '@fluentui/react-tabster';
8
8
  import { vim } from '@replit/codemirror-vim';
@@ -14,18 +14,21 @@ import React, {
14
14
  useCallback,
15
15
  useEffect,
16
16
  useImperativeHandle,
17
+ useRef,
17
18
  useState,
18
19
  } from 'react';
19
20
 
20
- import { generateName } from '@dxos/display-name';
21
+ import { log } from '@dxos/log';
21
22
  import { useThemeContext } from '@dxos/react-ui';
22
- import { getColorForValue, inputSurface, mx } from '@dxos/react-ui-theme';
23
+ import { attentionSurface, mx } from '@dxos/react-ui-theme';
23
24
 
24
- import { basicBundle, markdownBundle, setCommentRange } from '../../extensions';
25
- import { type CommentRange, type EditorModel } from '../../hooks';
25
+ import { basicBundle, markdownBundle } from '../../extensions';
26
+ import { type EditorModel } from '../../hooks';
26
27
  import { type ThemeStyles } from '../../styles';
27
28
  import { defaultTheme, markdownTheme, textTheme } from '../../themes';
29
+ import { logChanges } from '../../util';
28
30
 
31
+ // TODO(burdon): Change to enum?
29
32
  export const EditorModes = ['default', 'vim'] as const;
30
33
  export type EditorMode = (typeof EditorModes)[number];
31
34
 
@@ -34,133 +37,158 @@ export type CursorInfo = {
34
37
  to: number;
35
38
  line: number;
36
39
  lines: number;
40
+ length: number;
37
41
  after?: string;
38
42
  };
39
43
 
40
- export type TextEditorRef = {
41
- root: HTMLDivElement | null;
42
- state?: EditorState;
43
- view?: EditorView;
44
- };
45
-
46
44
  export type TextEditorSlots = {
47
45
  root?: Omit<ComponentProps<'div'>, 'ref'>;
48
46
  editor?: {
49
47
  className?: string;
50
- placeholder?: string;
51
- spellCheck?: boolean;
52
- tabIndex?: number;
53
- theme?: ThemeStyles;
48
+ };
49
+ content?: {
50
+ className?: string;
54
51
  };
55
52
  };
56
53
 
54
+ // TODO(burdon): Spellcheck?
57
55
  export type TextEditorProps = {
58
- model: EditorModel;
56
+ model: EditorModel; // TODO(burdon): Optional (e.g., just provide content if readonly).
59
57
  readonly?: boolean; // TODO(burdon): Move into model.
60
- comments?: CommentRange[]; // TODO(burdon): Move into extension.
61
- extensions?: Extension[];
62
- editorMode?: EditorMode;
58
+ autoFocus?: boolean;
59
+ lineWrapping?: boolean;
60
+ scrollTo?: StateEffect<any>; // TODO(burdon): Restore scroll position: scrollTo EditorView.scrollSnapshot().
61
+ selection?: { anchor: number; head?: number };
62
+ editorMode?: EditorMode; // TODO(burdon): Factor out.
63
+ placeholder?: string;
64
+ theme?: ThemeStyles;
63
65
  slots?: TextEditorSlots;
66
+ extensions?: Extension[];
67
+ debug?: boolean;
64
68
  };
65
69
 
66
70
  /**
67
71
  * Base text editor.
68
- * NOTE: Rather than adding properties, try to create extensions that can be reused.
69
72
  */
70
- export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
71
- ({ model, readonly, comments, extensions = [], editorMode, slots = defaultSlots }, forwardedRef) => {
72
- const { themeMode } = useThemeContext();
73
+ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
74
+ (
75
+ {
76
+ model,
77
+ readonly,
78
+ autoFocus,
79
+ scrollTo,
80
+ selection,
81
+ editorMode,
82
+ theme,
83
+ slots = defaultSlots,
84
+ extensions = [],
85
+ debug,
86
+ },
87
+ forwardedRef,
88
+ ) => {
73
89
  const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
74
- const [root, setRoot] = useState<HTMLDivElement | null>(null);
75
- const [{ state = undefined, view = undefined } = {}, setEditor] = useState<
76
- { state?: EditorState; view?: EditorView } | undefined
77
- >();
78
- useImperativeHandle(forwardedRef, () => ({ root, state, view }), [view, state, root]);
79
-
80
- // TODO(burdon): Factor out as extension.
81
- const { awareness, peer } = model;
82
- useEffect(() => {
83
- if (awareness && peer) {
84
- awareness.setLocalStateField('user', {
85
- name: peer.name ?? generateName(peer.id),
86
- color: getColorForValue({ value: peer.id, type: 'color' }),
87
- colorLight: getColorForValue({ value: peer.id, themeMode, type: 'highlight' }),
88
- });
89
- }
90
- }, [awareness, peer, themeMode]);
90
+ const { themeMode } = useThemeContext();
91
+
92
+ const rootRef = useRef<HTMLDivElement>(null);
93
+ const [view, setView] = useState<EditorView | null>(null);
94
+
95
+ // The view ref can be used to focus the editor.
96
+ // NOTE: This does not cause the parent to re-render, so the ref is not available immediately.
97
+ useImperativeHandle<EditorView | null, EditorView | null>(forwardedRef, () => view, [view]);
91
98
 
92
- // TODO(burdon): Factor out as extension.
99
+ // Set focus.
93
100
  useEffect(() => {
94
- if (view && comments?.length) {
95
- view.dispatch({
96
- effects: setCommentRange.of({ model, comments }),
97
- });
101
+ if (autoFocus) {
102
+ view?.focus();
98
103
  }
99
- }, [view, comments]);
104
+ }, [view, autoFocus]);
100
105
 
106
+ // Create editor state and view.
107
+ // The view is recreated if the model or extensions are changed.
101
108
  useEffect(() => {
102
- if (!root) {
109
+ if (!model || !rootRef.current) {
103
110
  return;
104
111
  }
105
112
 
106
- // TODO(burdon): Remember cursor position.
113
+ //
114
+ // EditorState
107
115
  // https://codemirror.net/docs/ref/#state.EditorStateConfig
116
+ //
108
117
  const state = EditorState.create({
109
118
  doc: model.text(),
119
+ selection,
110
120
  extensions: [
111
- readonly && EditorState.readOnly.of(readonly),
112
-
113
- // TODO(burdon): Factor out VIM mode? (manage via MarkdownPlugin).
114
- editorMode === 'vim' && vim(),
121
+ // TODO(burdon): Doesn't catch errors in keymap functions.
122
+ EditorView.exceptionSink.of((err) => {
123
+ log.catch(err);
124
+ }),
115
125
 
116
126
  // Theme.
117
- // TODO(burdon): Make theme configurable.
127
+ // TODO(burdon): Make configurable.
118
128
  EditorView.baseTheme(defaultTheme),
119
- EditorView.theme(slots?.editor?.theme ?? {}),
129
+ EditorView.theme(theme ?? {}),
120
130
  EditorView.darkTheme.of(themeMode === 'dark'),
131
+ EditorView.editorAttributes.of({ class: slots.editor?.className ?? '' }),
132
+ EditorView.contentAttributes.of({ class: slots.content?.className ?? '' }),
133
+
134
+ // State.
135
+ EditorView.editable.of(!readonly),
136
+ EditorState.readOnly.of(!!readonly),
121
137
 
122
138
  // Storage and replication.
139
+ // NOTE: This must come before user extensions.
123
140
  model.extension,
124
141
 
142
+ // TODO(burdon): Factor out (requires special handling for Escape/focus).
143
+ editorMode === 'vim' && vim(),
144
+
125
145
  // Custom.
126
146
  ...extensions,
127
147
  ].filter(Boolean) as Extension[],
128
148
  });
129
149
 
130
- // NOTE: This repaints the editor.
131
- // If the new state is derived from the old state, it will likely not be visible other than the cursor resetting.
132
- // Ideally this should not happen except when changing between text objects.
133
- view?.destroy();
134
- setEditor({
150
+ //
151
+ // EditorView
152
+ // https://codemirror.net/docs/ref/#view.EditorViewConfig
153
+ //
154
+ const newView = new EditorView({
135
155
  state,
136
- view: new EditorView({
137
- state,
138
- parent: root,
139
- // NOTE: Uncomment to spy on all transactions.
140
- // https://codemirror.net/docs/ref/#view.EditorView.dispatch
141
- // dispatch: (transaction, view) => {
142
- // view.update([transaction]);
143
- // },
144
- }),
156
+ parent: rootRef.current,
157
+ scrollTo,
158
+ // NOTE: Uncomment to debug/monitor all transactions.
159
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
160
+ dispatchTransactions: (trs, view) => {
161
+ if (debug) {
162
+ logChanges(trs);
163
+ }
164
+ view.update(trs);
165
+ },
145
166
  });
146
167
 
168
+ view?.destroy();
169
+ setView(newView);
170
+
147
171
  return () => {
148
- view?.destroy();
149
- setEditor(undefined);
172
+ newView?.destroy();
173
+ setView(null);
150
174
  };
151
- }, [root, model, readonly, editorMode, themeMode]);
175
+ }, [rootRef, model, readonly, editorMode, themeMode]);
152
176
 
177
+ // Handles tab/focus.
178
+ // Pressing Escape focuses the outer div (to support tab navigation); pressing Enter refocuses the editor.
153
179
  const handleKeyUp = useCallback(
154
180
  (event: KeyboardEvent) => {
155
181
  const { key, altKey, shiftKey, metaKey, ctrlKey } = event;
156
182
  switch (key) {
157
183
  case 'Enter': {
158
- view?.contentDOM.focus();
184
+ view?.focus();
159
185
  break;
160
186
  }
161
187
 
162
188
  case 'Escape': {
163
- editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey) && root?.focus();
189
+ if (editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey)) {
190
+ rootRef.current?.focus();
191
+ }
164
192
  break;
165
193
  }
166
194
  }
@@ -171,42 +199,46 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
171
199
  return (
172
200
  <div
173
201
  key={model.id}
174
- ref={setRoot}
202
+ role='none'
175
203
  tabIndex={0}
176
- {...slots?.root}
177
- {...(editorMode !== 'vim' && tabsterDOMAttribute)}
178
204
  onKeyUp={handleKeyUp}
205
+ {...slots.root}
206
+ {...(editorMode !== 'vim' && tabsterDOMAttribute)}
207
+ ref={rootRef}
179
208
  />
180
209
  );
181
210
  },
182
211
  );
183
212
 
184
- export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
185
- ({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
213
+ // TODO(burdon): Single-line/scroll.
214
+ export const TextEditor = forwardRef<EditorView, TextEditorProps>(
215
+ ({ readonly, placeholder, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
186
216
  const { themeMode } = useThemeContext();
187
- const slots = defaultsDeep({}, _slots, defaultTextSlots);
217
+ const updatedSlots = defaultsDeep({}, slots, defaultTextSlots);
188
218
  return (
189
219
  <BaseTextEditor
190
220
  ref={forwardedRef}
191
221
  readonly={readonly}
192
- extensions={[basicBundle({ readonly, themeMode, placeholder: slots?.editor?.placeholder }), ...extensions]}
193
- slots={slots}
222
+ extensions={[basicBundle({ themeMode, placeholder, lineWrapping }), ...extensions]}
223
+ theme={theme}
224
+ slots={updatedSlots}
194
225
  {...props}
195
226
  />
196
227
  );
197
228
  },
198
229
  );
199
230
 
200
- export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
201
- ({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
231
+ export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
232
+ ({ readonly, placeholder, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
202
233
  const { themeMode } = useThemeContext();
203
- const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
234
+ const updatedSlots = defaultsDeep({}, slots, defaultMarkdownSlots);
204
235
  return (
205
236
  <BaseTextEditor
206
237
  ref={forwardedRef}
207
238
  readonly={readonly}
208
- extensions={[markdownBundle({ readonly, themeMode, placeholder: slots?.editor?.placeholder }), ...extensions]}
209
- slots={slots}
239
+ extensions={[markdownBundle({ themeMode, placeholder }), ...extensions]}
240
+ theme={theme}
241
+ slots={updatedSlots}
210
242
  {...props}
211
243
  />
212
244
  );
@@ -215,20 +247,18 @@ export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
215
247
 
216
248
  export const defaultSlots: TextEditorSlots = {
217
249
  root: {
218
- className: mx('p-2', inputSurface),
250
+ // TODO(burdon): Add focusRing by default/as property?
251
+ className: mx('flex flex-col grow overflow-y-auto', attentionSurface),
252
+ },
253
+ editor: {
254
+ className: 'h-full p-2',
219
255
  },
220
256
  };
221
257
 
222
258
  export const defaultTextSlots: TextEditorSlots = {
223
259
  ...defaultSlots,
224
- editor: {
225
- theme: textTheme,
226
- },
227
260
  };
228
261
 
229
262
  export const defaultMarkdownSlots: TextEditorSlots = {
230
263
  ...defaultSlots,
231
- editor: {
232
- theme: markdownTheme,
233
- },
234
264
  };