@dxos/react-ui-editor 0.3.10-next.ef70620 → 0.3.10

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 (154) hide show
  1. package/dist/lib/browser/index.mjs +1827 -799
  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 +59 -0
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -0
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +7 -3
  7. package/dist/types/src/components/TextEditor/TextEditor.d.ts.map +1 -1
  8. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts +12 -4
  9. package/dist/types/src/components/TextEditor/TextEditor.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts +10 -0
  11. package/dist/types/src/components/TextEditor/extensions/autocomplete.d.ts.map +1 -0
  12. package/dist/types/src/components/TextEditor/extensions/{basic/bundle.d.ts → basic.d.ts} +3 -2
  13. package/dist/types/src/components/TextEditor/extensions/basic.d.ts.map +1 -0
  14. package/dist/types/src/components/TextEditor/extensions/blast.d.ts +25 -0
  15. package/dist/types/src/components/TextEditor/extensions/blast.d.ts.map +1 -0
  16. package/dist/types/src/components/TextEditor/extensions/code.d.ts +2 -0
  17. package/dist/types/src/components/TextEditor/extensions/code.d.ts.map +1 -0
  18. package/dist/types/src/components/TextEditor/extensions/comments.d.ts +18 -0
  19. package/dist/types/src/components/TextEditor/extensions/comments.d.ts.map +1 -0
  20. package/dist/types/src/components/TextEditor/extensions/demo.d.ts +11 -0
  21. package/dist/types/src/components/TextEditor/extensions/demo.d.ts.map +1 -0
  22. package/dist/types/src/components/TextEditor/extensions/{hyperlink/hyperlinkWidget.d.ts → experimental.d.ts} +2 -2
  23. package/dist/types/src/components/TextEditor/extensions/experimental.d.ts.map +1 -0
  24. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts +24 -0
  25. package/dist/types/src/components/TextEditor/extensions/highlight.d.ts.map +1 -0
  26. package/dist/types/src/components/TextEditor/extensions/image.d.ts +4 -0
  27. package/dist/types/src/components/TextEditor/extensions/image.d.ts.map +1 -0
  28. package/dist/types/src/components/TextEditor/extensions/index.d.ts +13 -2
  29. package/dist/types/src/components/TextEditor/extensions/index.d.ts.map +1 -1
  30. package/dist/types/src/components/TextEditor/extensions/link.d.ts +11 -0
  31. package/dist/types/src/components/TextEditor/extensions/link.d.ts.map +1 -0
  32. package/dist/types/src/components/TextEditor/extensions/listener.d.ts +9 -0
  33. package/dist/types/src/components/TextEditor/extensions/listener.d.ts.map +1 -0
  34. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts +10 -1
  35. package/dist/types/src/components/TextEditor/extensions/markdown/bundle.d.ts.map +1 -1
  36. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts +37 -0
  37. package/dist/types/src/components/TextEditor/extensions/markdown/highlight.d.ts.map +1 -0
  38. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts +1 -1
  39. package/dist/types/src/components/TextEditor/extensions/markdown/index.d.ts.map +1 -1
  40. package/dist/types/src/components/TextEditor/extensions/mention.d.ts +6 -0
  41. package/dist/types/src/components/TextEditor/extensions/mention.d.ts.map +1 -0
  42. package/dist/types/src/components/TextEditor/extensions/table.d.ts +8 -0
  43. package/dist/types/src/components/TextEditor/extensions/table.d.ts.map +1 -0
  44. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts +3 -0
  45. package/dist/types/src/components/TextEditor/extensions/tasklist.d.ts.map +1 -0
  46. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts +12 -0
  47. package/dist/types/src/components/TextEditor/extensions/tooltip.d.ts.map +1 -0
  48. package/dist/types/src/components/TextEditor/index.d.ts +2 -0
  49. package/dist/types/src/components/TextEditor/index.d.ts.map +1 -1
  50. package/dist/types/src/components/TextEditor/themes/default.d.ts +25 -0
  51. package/dist/types/src/components/TextEditor/themes/default.d.ts.map +1 -0
  52. package/dist/types/src/components/TextEditor/themes/index.d.ts +2 -0
  53. package/dist/types/src/components/TextEditor/themes/index.d.ts.map +1 -0
  54. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.d.ts +3 -0
  55. package/dist/types/src/hooks/automerge/PatchSemaphore.d.ts.map +1 -0
  56. package/dist/types/src/hooks/automerge/automerge.stories.d.ts +16 -0
  57. package/dist/types/src/hooks/automerge/automerge.stories.d.ts.map +1 -0
  58. package/dist/types/src/hooks/automerge/handle.d.ts.map +1 -0
  59. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/index.d.ts +1 -1
  60. package/dist/types/src/hooks/automerge/index.d.ts.map +1 -0
  61. package/dist/types/src/hooks/automerge/plugin.d.ts.map +1 -0
  62. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts +5 -0
  63. package/dist/types/src/hooks/automerge/translation/automerge-to-codemirror.d.ts.map +1 -0
  64. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts +6 -0
  65. package/dist/types/src/hooks/automerge/translation/codemirror-to-automerge.d.ts.map +1 -0
  66. package/dist/types/src/hooks/automerge/translation/index.d.ts +3 -0
  67. package/dist/types/src/hooks/automerge/translation/index.d.ts.map +1 -0
  68. package/dist/types/src/hooks/index.d.ts +0 -1
  69. package/dist/types/src/hooks/index.d.ts.map +1 -1
  70. package/dist/types/src/hooks/useTextModel.d.ts +9 -3
  71. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  72. package/dist/types/src/{components/TextEditor → hooks/yjs}/yjs.stories.d.ts +2 -1
  73. package/dist/types/src/hooks/yjs/yjs.stories.d.ts.map +1 -0
  74. package/dist/types/src/styles/markdown.d.ts +14 -11
  75. package/dist/types/src/styles/markdown.d.ts.map +1 -1
  76. package/dist/types/src/testing/replicator.d.ts.map +1 -1
  77. package/package.json +24 -33
  78. package/src/components/TextEditor/MarkdownEditor.stories.tsx +367 -0
  79. package/src/components/TextEditor/TextEditor.stories.tsx +57 -43
  80. package/src/components/TextEditor/TextEditor.tsx +87 -45
  81. package/src/components/TextEditor/extensions/autocomplete.ts +59 -0
  82. package/src/components/TextEditor/extensions/{basic/bundle.ts → basic.ts} +8 -4
  83. package/src/components/TextEditor/extensions/blast.ts +373 -0
  84. package/src/components/TextEditor/extensions/code.ts +99 -0
  85. package/src/components/TextEditor/extensions/comments.ts +173 -0
  86. package/src/components/TextEditor/extensions/demo.ts +69 -0
  87. package/src/components/TextEditor/extensions/{hyperlink/hyperlinkWidget.tsx → experimental.tsx} +16 -21
  88. package/src/components/TextEditor/extensions/highlight.ts +128 -0
  89. package/src/components/TextEditor/extensions/image.ts +67 -0
  90. package/src/components/TextEditor/extensions/index.ts +13 -2
  91. package/src/components/TextEditor/extensions/{hyperlink/hyperlinkDecoration.ts → link.ts} +75 -38
  92. package/src/components/TextEditor/extensions/{change.ts → listener.ts} +5 -4
  93. package/src/components/TextEditor/extensions/markdown/bundle.ts +56 -49
  94. package/src/components/TextEditor/extensions/markdown/highlight.ts +207 -0
  95. package/src/components/TextEditor/extensions/markdown/index.ts +1 -1
  96. package/src/components/TextEditor/extensions/mention.ts +31 -0
  97. package/src/components/TextEditor/extensions/table.ts +119 -0
  98. package/src/components/TextEditor/extensions/tasklist.ts +121 -0
  99. package/src/components/TextEditor/extensions/{hyperlink/hyperlinkTooltip.tsx → tooltip.tsx} +9 -9
  100. package/src/components/TextEditor/index.ts +3 -0
  101. package/src/components/TextEditor/themes/default.ts +279 -0
  102. package/src/components/TextEditor/themes/index.ts +5 -0
  103. package/src/{automerge/automerge-plugin → hooks/automerge}/PatchSemaphore.ts +12 -9
  104. package/src/{automerge → hooks/automerge}/automerge.stories.tsx +6 -3
  105. package/src/{automerge/automerge-plugin → hooks/automerge}/plugin.ts +1 -0
  106. package/src/{automerge/automerge-plugin/amToCodemirror.ts → hooks/automerge/translation/automerge-to-codemirror.ts} +10 -2
  107. package/src/{automerge/automerge-plugin/codeMirrorToAm.ts → hooks/automerge/translation/codemirror-to-automerge.ts} +7 -4
  108. package/src/hooks/automerge/translation/index.ts +6 -0
  109. package/src/hooks/index.ts +0 -1
  110. package/src/hooks/useTextModel.ts +61 -39
  111. package/src/{components/TextEditor → hooks/yjs}/yjs.stories.tsx +2 -1
  112. package/src/styles/markdown.ts +33 -24
  113. package/src/testing/replicator.ts +7 -6
  114. package/dist/types/src/automerge/automerge-plugin/PatchSemaphore.d.ts.map +0 -1
  115. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts +0 -6
  116. package/dist/types/src/automerge/automerge-plugin/amToCodemirror.d.ts.map +0 -1
  117. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts +0 -7
  118. package/dist/types/src/automerge/automerge-plugin/codeMirrorToAm.d.ts.map +0 -1
  119. package/dist/types/src/automerge/automerge-plugin/handle.d.ts.map +0 -1
  120. package/dist/types/src/automerge/automerge-plugin/index.d.ts.map +0 -1
  121. package/dist/types/src/automerge/automerge-plugin/plugin.d.ts.map +0 -1
  122. package/dist/types/src/automerge/automerge.stories.d.ts +0 -9
  123. package/dist/types/src/automerge/automerge.stories.d.ts.map +0 -1
  124. package/dist/types/src/components/TextEditor/extensions/basic/bundle.d.ts.map +0 -1
  125. package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts +0 -3
  126. package/dist/types/src/components/TextEditor/extensions/basic/index.d.ts.map +0 -1
  127. package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts +0 -10
  128. package/dist/types/src/components/TextEditor/extensions/basic/theme.d.ts.map +0 -1
  129. package/dist/types/src/components/TextEditor/extensions/change.d.ts +0 -7
  130. package/dist/types/src/components/TextEditor/extensions/change.d.ts.map +0 -1
  131. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts +0 -11
  132. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkDecoration.d.ts.map +0 -1
  133. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts +0 -8
  134. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkTooltip.d.ts.map +0 -1
  135. package/dist/types/src/components/TextEditor/extensions/hyperlink/hyperlinkWidget.d.ts.map +0 -1
  136. package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts +0 -4
  137. package/dist/types/src/components/TextEditor/extensions/hyperlink/index.d.ts.map +0 -1
  138. package/dist/types/src/components/TextEditor/extensions/markdown/tags.d.ts +0 -17
  139. package/dist/types/src/components/TextEditor/extensions/markdown/tags.d.ts.map +0 -1
  140. package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts +0 -21
  141. package/dist/types/src/components/TextEditor/extensions/markdown/theme.d.ts.map +0 -1
  142. package/dist/types/src/components/TextEditor/yjs.stories.d.ts.map +0 -1
  143. package/dist/types/src/hooks/useCollaboration.d.ts +0 -9
  144. package/dist/types/src/hooks/useCollaboration.d.ts.map +0 -1
  145. package/src/components/TextEditor/extensions/basic/index.ts +0 -6
  146. package/src/components/TextEditor/extensions/basic/theme.ts +0 -49
  147. package/src/components/TextEditor/extensions/hyperlink/index.ts +0 -7
  148. package/src/components/TextEditor/extensions/markdown/tags.ts +0 -38
  149. package/src/components/TextEditor/extensions/markdown/theme.ts +0 -265
  150. package/src/hooks/useCollaboration.ts +0 -45
  151. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/handle.d.ts +0 -0
  152. package/dist/types/src/{automerge/automerge-plugin → hooks/automerge}/plugin.d.ts +0 -0
  153. package/src/{automerge/automerge-plugin → hooks/automerge}/handle.ts +0 -0
  154. package/src/{automerge/automerge-plugin → hooks/automerge}/index.ts +1 -1
@@ -7,23 +7,24 @@ import { EditorView } from '@codemirror/view';
7
7
  import { useFocusableGroup } from '@fluentui/react-tabster';
8
8
  import { vim } from '@replit/codemirror-vim';
9
9
  import defaultsDeep from 'lodash.defaultsdeep';
10
- import get from 'lodash.get';
11
10
  import React, {
11
+ type ComponentProps,
12
12
  type KeyboardEvent,
13
13
  forwardRef,
14
14
  useCallback,
15
15
  useEffect,
16
16
  useImperativeHandle,
17
17
  useState,
18
- type ComponentProps,
19
18
  } from 'react';
20
19
 
21
- import { isDocAccessor } from '@dxos/echo-schema';
20
+ import { generateName } from '@dxos/display-name';
22
21
  import { useThemeContext } from '@dxos/react-ui';
22
+ import { getColorForValue, inputSurface, mx } from '@dxos/react-ui-theme';
23
23
 
24
- import { basicBundle, basicTheme, markdownBundle, markdownTheme } from './extensions';
25
- import { type EditorModel, useCollaboration } from '../../hooks';
26
- import type { ThemeStyles } from '../../styles';
24
+ import { basicBundle, markdownBundle } from './extensions';
25
+ import { defaultTheme, markdownTheme, textTheme } from './themes';
26
+ import { type EditorModel } from '../../hooks';
27
+ import { type ThemeStyles } from '../../styles';
27
28
 
28
29
  export const EditorModes = ['default', 'vim'] as const;
29
30
  export type EditorMode = (typeof EditorModes)[number];
@@ -37,7 +38,7 @@ export type CursorInfo = {
37
38
  };
38
39
 
39
40
  export type TextEditorRef = {
40
- editor: HTMLDivElement | null;
41
+ root: HTMLDivElement | null;
41
42
  state?: EditorState;
42
43
  view?: EditorView;
43
44
  };
@@ -55,9 +56,10 @@ export type TextEditorSlots = {
55
56
 
56
57
  export type TextEditorProps = {
57
58
  model: EditorModel;
59
+ readonly?: boolean;
60
+ editorMode?: EditorMode;
58
61
  extensions?: Extension[];
59
62
  slots?: TextEditorSlots;
60
- editorMode?: EditorMode;
61
63
  };
62
64
 
63
65
  /**
@@ -65,66 +67,84 @@ export type TextEditorProps = {
65
67
  * NOTE: Rather than adding properties, try to create extensions that can be reused.
66
68
  */
67
69
  export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
68
- ({ model, extensions = [], slots, editorMode }, forwardedRef) => {
70
+ ({ model, readonly, editorMode, extensions = [], slots = defaultSlots }, forwardedRef) => {
69
71
  const { themeMode } = useThemeContext();
70
72
  const tabsterDOMAttribute = useFocusableGroup({ tabBehavior: 'limited' });
71
73
 
72
- const [parent, setParent] = useState<HTMLDivElement | null>(null);
74
+ const [root, setRoot] = useState<HTMLDivElement | null>(null);
73
75
  const [state, setState] = useState<EditorState>();
74
76
  const [view, setView] = useState<EditorView>();
75
- useImperativeHandle(
76
- forwardedRef,
77
- () => ({
78
- editor: parent,
79
- state,
80
- view,
81
- }),
82
- [view, state, parent],
83
- );
77
+ useImperativeHandle(forwardedRef, () => ({ root, state, view }), [view, state, root]);
78
+
79
+ // TODO(burdon): Factor out?
80
+ const { awareness, peer } = model;
81
+ useEffect(() => {
82
+ if (awareness && peer) {
83
+ awareness.setLocalStateField('user', {
84
+ name: peer.name ?? generateName(peer.id),
85
+ color: getColorForValue({ value: peer.id, type: 'color' }),
86
+ colorLight: getColorForValue({ value: peer.id, themeMode, type: 'highlight' }),
87
+ });
88
+ }
89
+ }, [awareness, peer, themeMode]);
84
90
 
85
- // TODO(burdon): Pass in extension?
86
- const collaboration = useCollaboration(model, themeMode);
91
+ // TODO(burdon): Get from model.
92
+ // useHighlights(view, [
93
+ // { id: 'c-1', from: 0, to: 20 },
94
+ // { id: 'c-2', from: 100, to: 120 },
95
+ // ]);
87
96
 
88
97
  useEffect(() => {
89
- if (!parent) {
98
+ if (!root) {
90
99
  return;
91
100
  }
92
101
 
93
- const initialText = isDocAccessor(model.content)
94
- ? get(model.content.handle.docSync(), model.content.path)
95
- : model.content?.toString();
96
-
97
102
  const state = EditorState.create({
98
- doc: initialText,
103
+ doc: model.text(),
99
104
  extensions: [
105
+ readonly && EditorState.readOnly.of(readonly),
106
+
100
107
  // TODO(burdon): Factor out VIM mode?
101
108
  editorMode === 'vim' && vim(),
102
109
 
103
- // Replication.
104
- collaboration,
105
-
106
110
  // Theme.
107
- slots?.editor?.theme && EditorView.theme(slots?.editor?.theme),
111
+ // TODO(burdon): Make optional.
112
+ EditorView.baseTheme(defaultTheme),
113
+ EditorView.theme(slots?.editor?.theme ?? {}),
114
+ // TODO(burdon): themeMode doesn't change in storybooks.
115
+ EditorView.darkTheme.of(themeMode === 'dark'),
116
+
117
+ // Storage and replication.
118
+ model.extension,
108
119
 
109
120
  // Custom.
110
121
  ...extensions,
111
122
  ].filter(Boolean) as Extension[],
112
123
  });
113
124
 
114
- setState(state);
115
-
116
125
  // NOTE: This repaints the editor.
117
126
  // If the new state is derived from the old state, it will likely not be visible other than the cursor resetting.
118
127
  // Ideally this should not be hit except when changing between text objects.
119
128
  view?.destroy();
120
- setView(new EditorView({ state, parent }));
129
+ setView(
130
+ new EditorView({
131
+ state,
132
+ parent: root,
133
+ // NOTE: Uncomment to spy on all transactions.
134
+ // https://codemirror.net/docs/ref/#view.EditorView.dispatch
135
+ // dispatch: (transaction, view) => {
136
+ // view.update([transaction]);
137
+ // },
138
+ }),
139
+ );
140
+ setState(state);
121
141
 
122
142
  return () => {
123
143
  view?.destroy();
124
144
  setView(undefined);
125
145
  setState(undefined);
126
146
  };
127
- }, [parent, model.content, themeMode, editorMode]);
147
+ }, [root, model, themeMode, readonly, editorMode]);
128
148
 
129
149
  const handleKeyUp = useCallback(
130
150
  (event: KeyboardEvent) => {
@@ -136,7 +156,7 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
136
156
  }
137
157
 
138
158
  case 'Escape': {
139
- editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey) && parent?.focus();
159
+ editorMode === 'vim' && (altKey || shiftKey || metaKey || ctrlKey) && root?.focus();
140
160
  break;
141
161
  }
142
162
  }
@@ -147,7 +167,7 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
147
167
  return (
148
168
  <div
149
169
  key={model.id}
150
- ref={setParent}
170
+ ref={setRoot}
151
171
  tabIndex={0}
152
172
  {...slots?.root}
153
173
  {...(editorMode !== 'vim' && tabsterDOMAttribute)}
@@ -158,14 +178,15 @@ export const BaseTextEditor = forwardRef<TextEditorRef, TextEditorProps>(
158
178
  );
159
179
 
160
180
  export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
161
- ({ extensions: _extensions, slots, ...props }, forwardedRef) => {
181
+ ({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
162
182
  const { themeMode } = useThemeContext();
163
- const extensions = [...(_extensions ?? []), basicBundle({ themeMode, placeholder: slots?.editor?.placeholder })];
183
+ const slots = defaultsDeep({}, _slots, defaultTextSlots);
164
184
  return (
165
185
  <BaseTextEditor
166
186
  ref={forwardedRef}
167
- extensions={extensions}
168
- slots={defaultsDeep({}, slots, { editor: { theme: basicTheme } })}
187
+ readonly={readonly}
188
+ extensions={[basicBundle({ readonly, themeMode, placeholder: slots?.editor?.placeholder }), ...extensions]}
189
+ slots={slots}
169
190
  {...props}
170
191
  />
171
192
  );
@@ -173,16 +194,37 @@ export const TextEditor = forwardRef<TextEditorRef, TextEditorProps>(
173
194
  );
174
195
 
175
196
  export const MarkdownEditor = forwardRef<TextEditorRef, TextEditorProps>(
176
- ({ extensions: _extensions, slots, ...props }, forwardedRef) => {
197
+ ({ readonly, extensions = [], slots: _slots, ...props }, forwardedRef) => {
177
198
  const { themeMode } = useThemeContext();
178
- const extensions = [...(_extensions ?? []), markdownBundle({ themeMode })];
199
+ const slots = defaultsDeep({}, _slots, defaultMarkdownSlots);
179
200
  return (
180
201
  <BaseTextEditor
181
202
  ref={forwardedRef}
182
- extensions={extensions}
183
- slots={defaultsDeep({}, slots, { editor: { theme: markdownTheme } })}
203
+ readonly={readonly}
204
+ extensions={[markdownBundle({ readonly, themeMode, placeholder: slots?.editor?.placeholder }), ...extensions]}
205
+ slots={slots}
184
206
  {...props}
185
207
  />
186
208
  );
187
209
  },
188
210
  );
211
+
212
+ export const defaultSlots: TextEditorSlots = {
213
+ root: {
214
+ className: mx('p-2', inputSurface),
215
+ },
216
+ };
217
+
218
+ export const defaultTextSlots: TextEditorSlots = {
219
+ ...defaultSlots,
220
+ editor: {
221
+ theme: textTheme,
222
+ },
223
+ };
224
+
225
+ export const defaultMarkdownSlots: TextEditorSlots = {
226
+ ...defaultSlots,
227
+ editor: {
228
+ theme: markdownTheme,
229
+ },
230
+ };
@@ -0,0 +1,59 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ // https://codemirror.net/examples/autocompletion
6
+ // https://codemirror.net/docs/ref/#autocomplete.autocompletion
7
+ // https://codemirror.net/docs/ref/#autocomplete.Completion
8
+
9
+ import {
10
+ autocompletion,
11
+ completionKeymap,
12
+ type Completion,
13
+ type CompletionContext,
14
+ type CompletionResult,
15
+ } from '@codemirror/autocomplete';
16
+ import { keymap } from '@codemirror/view';
17
+
18
+ export type AutocompleteResult = Completion;
19
+
20
+ export type AutocompleteOptions = {
21
+ onSearch: (text: string) => Completion[];
22
+ };
23
+
24
+ /**
25
+ * Autocomplete extension.
26
+ */
27
+ export const autocomplete = ({ onSearch }: AutocompleteOptions) => {
28
+ return [
29
+ // https://codemirror.net/docs/ref/#view.keymap
30
+ // https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322
31
+ keymap.of(completionKeymap),
32
+
33
+ // https://codemirror.net/examples/autocompletion
34
+ // https://codemirror.net/docs/ref/#autocomplete.autocompletion
35
+ autocompletion({
36
+ // TODO(burdon): Set custom keymap.
37
+ // defaultKeymap: false,
38
+
39
+ // Don't start unless key press.
40
+ activateOnTyping: false,
41
+
42
+ // TODO(burdon): Option to create new page?
43
+ // TODO(burdon): Optional decoration via addToOptions
44
+ override: [
45
+ (context: CompletionContext): CompletionResult | null => {
46
+ const match = context.matchBefore(/\w*/);
47
+ if (!match || (match.from === match.to && !context.explicit)) {
48
+ return null;
49
+ }
50
+
51
+ return {
52
+ from: match.from,
53
+ options: onSearch(match.text.toLowerCase()),
54
+ };
55
+ },
56
+ ],
57
+ }),
58
+ ];
59
+ };
@@ -6,23 +6,27 @@ import { closeBrackets } from '@codemirror/autocomplete';
6
6
  import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from '@codemirror/language';
7
7
  import { type Extension } from '@codemirror/state';
8
8
  import { oneDarkHighlightStyle } from '@codemirror/theme-one-dark';
9
- import { EditorView, placeholder } from '@codemirror/view';
9
+ import { drawSelection, EditorView, placeholder } from '@codemirror/view';
10
10
 
11
11
  import type { ThemeMode } from '@dxos/react-ui';
12
12
 
13
13
  export type BasicBundleOptions = {
14
+ readonly?: boolean;
14
15
  themeMode?: ThemeMode;
15
16
  placeholder?: string;
16
17
  };
17
18
 
18
- export const basicBundle = ({ themeMode, placeholder: _placeholder }: BasicBundleOptions): Extension[] =>
19
+ export const basicBundle = ({ readonly, themeMode, placeholder: _placeholder }: BasicBundleOptions): Extension[] =>
19
20
  [
21
+ EditorView.lineWrapping,
22
+
23
+ // TODO(burdon): Compare with minimalSetup.
24
+ // https://codemirror.net/docs/ref/#codemirror.minimalSetup
20
25
  bracketMatching(),
21
26
  closeBrackets(),
27
+ drawSelection(),
22
28
  _placeholder && placeholder(_placeholder),
23
29
 
24
- EditorView.lineWrapping,
25
-
26
30
  // TODO(burdon): Is this required?
27
31
  themeMode === 'dark' ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle),
28
32
  ].filter(Boolean) as Extension[];