@dxos/react-ui-editor 0.3.11-main.e0dc42b → 0.3.11-main.e19f39d

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