@dxos/react-ui-editor 0.4.4-main.2a45fa8 → 0.4.4-main.2c7ecda

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 (43) hide show
  1. package/dist/lib/browser/index.mjs +187 -139
  2. package/dist/lib/browser/index.mjs.map +3 -3
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts +3 -2
  5. package/dist/types/src/components/TextEditor/MarkdownEditor.stories.d.ts.map +1 -1
  6. package/dist/types/src/components/TextEditor/TextEditor.d.ts +3 -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 +1 -1
  9. package/dist/types/src/components/TextEditor/automerge.stories.d.ts.map +1 -1
  10. package/dist/types/src/components/TextEditor/hooks.stories.d.ts +1 -1
  11. package/dist/types/src/components/TextEditor/hooks.stories.d.ts.map +1 -1
  12. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  13. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  14. package/dist/types/src/extensions/basic.d.ts +2 -1
  15. package/dist/types/src/extensions/basic.d.ts.map +1 -1
  16. package/dist/types/src/extensions/comments.d.ts +15 -7
  17. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  18. package/dist/types/src/extensions/cursor.d.ts +1 -1
  19. package/dist/types/src/extensions/cursor.d.ts.map +1 -1
  20. package/dist/types/src/hooks/useEditorView.d.ts +6 -4
  21. package/dist/types/src/hooks/useEditorView.d.ts.map +1 -1
  22. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  23. package/dist/types/src/hooks/useTextModel.d.ts +3 -0
  24. package/dist/types/src/hooks/useTextModel.d.ts.map +1 -1
  25. package/dist/types/src/styles/layout.d.ts +1 -1
  26. package/dist/types/src/styles/layout.d.ts.map +1 -1
  27. package/package.json +26 -26
  28. package/src/components/TextEditor/MarkdownEditor.stories.tsx +9 -2
  29. package/src/components/TextEditor/TextEditor.tsx +21 -8
  30. package/src/components/TextEditor/automerge.stories.tsx +10 -7
  31. package/src/components/TextEditor/hooks.stories.tsx +44 -10
  32. package/src/components/Toolbar/Toolbar.stories.tsx +4 -4
  33. package/src/extensions/autocomplete.ts +1 -1
  34. package/src/extensions/automerge/cursor.ts +15 -4
  35. package/src/extensions/automerge/defs.ts +0 -1
  36. package/src/extensions/basic.ts +3 -1
  37. package/src/extensions/comments.ts +111 -82
  38. package/src/extensions/cursor.ts +4 -2
  39. package/src/extensions/markdown/image.ts +1 -1
  40. package/src/hooks/useEditorView.ts +13 -11
  41. package/src/hooks/useTextEditor.ts +6 -2
  42. package/src/hooks/useTextModel.ts +7 -7
  43. package/src/styles/layout.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.4.4-main.2a45fa8",
3
+ "version": "0.4.4-main.2c7ecda",
4
4
  "description": "Document editing experience within a DXOS shell.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -36,25 +36,25 @@
36
36
  "lodash.pick": "^4.4.0",
37
37
  "lodash.sortby": "^4.7.0",
38
38
  "style-mod": "^4.1.0",
39
- "@dxos/async": "0.4.4-main.2a45fa8",
40
- "@dxos/automerge": "0.4.4-main.2a45fa8",
41
- "@dxos/context": "0.4.4-main.2a45fa8",
42
- "@dxos/display-name": "0.4.4-main.2a45fa8",
43
- "@dxos/debug": "0.4.4-main.2a45fa8",
44
- "@dxos/invariant": "0.4.4-main.2a45fa8",
45
- "@dxos/echo-schema": "0.4.4-main.2a45fa8",
46
- "@dxos/log": "0.4.4-main.2a45fa8",
47
- "@dxos/protocols": "0.4.4-main.2a45fa8",
48
- "@dxos/react-ui": "0.4.4-main.2a45fa8",
49
- "@dxos/react-async": "0.4.4-main.2a45fa8",
50
- "@dxos/react-ui-theme": "0.4.4-main.2a45fa8",
51
- "@dxos/text-model": "0.4.4-main.2a45fa8",
52
- "@dxos/util": "0.4.4-main.2a45fa8"
39
+ "@dxos/async": "0.4.4-main.2c7ecda",
40
+ "@dxos/automerge": "0.4.4-main.2c7ecda",
41
+ "@dxos/context": "0.4.4-main.2c7ecda",
42
+ "@dxos/debug": "0.4.4-main.2c7ecda",
43
+ "@dxos/display-name": "0.4.4-main.2c7ecda",
44
+ "@dxos/echo-schema": "0.4.4-main.2c7ecda",
45
+ "@dxos/invariant": "0.4.4-main.2c7ecda",
46
+ "@dxos/react-async": "0.4.4-main.2c7ecda",
47
+ "@dxos/log": "0.4.4-main.2c7ecda",
48
+ "@dxos/protocols": "0.4.4-main.2c7ecda",
49
+ "@dxos/react-ui": "0.4.4-main.2c7ecda",
50
+ "@dxos/react-ui-theme": "0.4.4-main.2c7ecda",
51
+ "@dxos/text-model": "0.4.4-main.2c7ecda",
52
+ "@dxos/util": "0.4.4-main.2c7ecda"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@automerge/automerge-repo-network-broadcastchannel": "^1.1.1",
56
56
  "@phosphor-icons/react": "^2.0.5",
57
- "@preact/signals-react": "^1.3.6",
57
+ "@preact/signals-react": "^1.3.8",
58
58
  "@testing-library/dom": "^8.17.1",
59
59
  "@testing-library/react": "^13.4.0",
60
60
  "@testing-library/user-event": "^14.4.3",
@@ -66,7 +66,7 @@
66
66
  "@types/react-dom": "^18.0.6",
67
67
  "@types/react-test-renderer": "^17.0.2",
68
68
  "@vitest/ui": "^1.2.1",
69
- "deepsignal": "1.4.0-shallow.0",
69
+ "deepsignal": "^1.5.0",
70
70
  "happy-dom": "^13.3.1",
71
71
  "jsdom": "^20.0.1",
72
72
  "react": "^18.2.0",
@@ -76,20 +76,20 @@
76
76
  "vite-plugin-top-level-await": "^1.4.1",
77
77
  "vite-plugin-wasm": "^3.3.0",
78
78
  "vitest": "^1.2.1",
79
- "@dxos/config": "0.4.4-main.2a45fa8",
80
- "@dxos/echo-signals": "0.4.4-main.2a45fa8",
81
- "@dxos/keyboard": "0.4.4-main.2a45fa8",
82
- "@dxos/react-client": "0.4.4-main.2a45fa8",
83
- "@dxos/react-ui": "0.4.4-main.2a45fa8",
84
- "@dxos/random": "0.4.4-main.2a45fa8",
85
- "@dxos/storybook-utils": "0.4.4-main.2a45fa8",
86
- "@dxos/echo-typegen": "0.4.4-main.2a45fa8"
79
+ "@dxos/config": "0.4.4-main.2c7ecda",
80
+ "@dxos/echo-signals": "0.4.4-main.2c7ecda",
81
+ "@dxos/keyboard": "0.4.4-main.2c7ecda",
82
+ "@dxos/random": "0.4.4-main.2c7ecda",
83
+ "@dxos/echo-typegen": "0.4.4-main.2c7ecda",
84
+ "@dxos/react-ui": "0.4.4-main.2c7ecda",
85
+ "@dxos/react-client": "0.4.4-main.2c7ecda",
86
+ "@dxos/storybook-utils": "0.4.4-main.2c7ecda"
87
87
  },
88
88
  "peerDependencies": {
89
89
  "@phosphor-icons/react": "^2.0.5",
90
90
  "react": "^18.2.0",
91
91
  "react-dom": "^18.2.0",
92
- "@dxos/react-client": "0.4.4-main.2a45fa8"
92
+ "@dxos/react-client": "0.4.4-main.2c7ecda"
93
93
  },
94
94
  "publishConfig": {
95
95
  "access": "public"
@@ -131,6 +131,12 @@ const document = str(
131
131
  '',
132
132
  '> This is a block quote.',
133
133
  '',
134
+ '> This is a long wrapping block quote. Neque reiciendis ullam quae error labore sit, at, et, nulla, aut at nostrum omnis quas nostrum, at consectetur vitae eos asperiores non omnis ullam in beatae at vitae deserunt asperiores sapiente.',
135
+ '',
136
+ '> This is',
137
+ '> a multi-line',
138
+ '> block quote.',
139
+ '',
134
140
  'This is all about https://dxos.org and related technologies.',
135
141
  '',
136
142
  'This this is **bold**, ~~strikethrough~~, _italic_, and `f(INLINE)`.',
@@ -211,15 +217,16 @@ const onRenderLink: LinkOptions['onRender'] = (el, url) => {
211
217
  };
212
218
 
213
219
  type StoryProps = {
220
+ id?: string;
214
221
  text?: string;
215
222
  comments?: Comment[];
216
223
  } & Pick<TextEditorProps, 'readonly' | 'placeholder' | 'extensions'>;
217
224
 
218
- const Story = ({ text, comments, placeholder = 'New document.', ...props }: StoryProps) => {
225
+ const Story = ({ id = 'test', text, comments, placeholder = 'New document.', ...props }: StoryProps) => {
219
226
  const [item] = useState({ text: new TextObject(text) });
220
227
  const view = useRef<EditorView>(null);
221
228
  const model = useTextModel({ text: item.text });
222
- useComments(view.current, comments);
229
+ useComments(view.current, id, comments);
223
230
  if (!model) {
224
231
  return null;
225
232
  }
@@ -15,6 +15,7 @@ import React, {
15
15
  useImperativeHandle,
16
16
  useRef,
17
17
  useState,
18
+ useMemo,
18
19
  } from 'react';
19
20
 
20
21
  import { log } from '@dxos/log';
@@ -65,7 +66,7 @@ export type TextEditorProps = {
65
66
  * Base text editor.
66
67
  */
67
68
  // TODO(burdon): Replace with useTextEditor.
68
- export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
69
+ export const BaseTextEditor = forwardRef<EditorView | null, TextEditorProps>(
69
70
  (
70
71
  { model, readonly, autoFocus, scrollTo, selection, theme, slots = defaultSlots, extensions = [], debug },
71
72
  forwardedRef,
@@ -172,7 +173,8 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
172
173
  newView?.destroy();
173
174
  setView(null);
174
175
  };
175
- }, [rootRef, model, readonly, themeMode]);
176
+ // TODO(wittjosiah): Does `rootRef` ever change? Only `.current` changes?
177
+ }, [rootRef, extensions, model, readonly, themeMode]);
176
178
 
177
179
  // Focus editor on Enter (e.g., when tabbing to this component).
178
180
  const handleKeyUp = useCallback<KeyboardEventHandler<HTMLDivElement>>(
@@ -202,15 +204,22 @@ export const BaseTextEditor = forwardRef<EditorView, TextEditorProps>(
202
204
  },
203
205
  );
204
206
 
205
- export const TextEditor = forwardRef<EditorView, TextEditorProps>(
206
- ({ readonly, placeholder, lineWrapping, theme = textTheme, slots, extensions = [], ...props }, forwardedRef) => {
207
+ export const TextEditor = forwardRef<EditorView | null, TextEditorProps>(
208
+ (
209
+ { readonly, placeholder, lineWrapping, theme = textTheme, slots, extensions: _extensions, ...props },
210
+ forwardedRef,
211
+ ) => {
207
212
  const { themeMode } = useThemeContext();
208
213
  const updatedSlots = defaultsDeep({}, slots, defaultTextSlots);
214
+ const extensions = useMemo(
215
+ () => [createBasicBundle({ themeMode, placeholder, lineWrapping }), ...(_extensions ?? [])],
216
+ [themeMode, placeholder, lineWrapping, _extensions],
217
+ );
209
218
  return (
210
219
  <BaseTextEditor
211
220
  ref={forwardedRef}
212
221
  readonly={readonly}
213
- extensions={[createBasicBundle({ themeMode, placeholder, lineWrapping }), ...extensions]}
222
+ extensions={extensions}
214
223
  theme={theme}
215
224
  slots={updatedSlots}
216
225
  {...props}
@@ -219,15 +228,19 @@ export const TextEditor = forwardRef<EditorView, TextEditorProps>(
219
228
  },
220
229
  );
221
230
 
222
- export const MarkdownEditor = forwardRef<EditorView, TextEditorProps>(
223
- ({ readonly, placeholder, theme = markdownTheme, slots, extensions = [], ...props }, forwardedRef) => {
231
+ export const MarkdownEditor = forwardRef<EditorView | null, TextEditorProps>(
232
+ ({ readonly, placeholder, theme = markdownTheme, slots, extensions: _extensions, ...props }, forwardedRef) => {
224
233
  const { themeMode } = useThemeContext();
225
234
  const updatedSlots = defaultsDeep({}, slots, defaultMarkdownSlots);
235
+ const extensions = useMemo(
236
+ () => [createMarkdownExtensions({ themeMode, placeholder }), ...(_extensions ?? [])],
237
+ [themeMode, placeholder, _extensions],
238
+ );
226
239
  return (
227
240
  <BaseTextEditor
228
241
  ref={forwardedRef}
229
242
  readonly={readonly}
230
- extensions={[createMarkdownExtensions({ themeMode, placeholder }), ...extensions]}
243
+ extensions={extensions}
231
244
  theme={theme}
232
245
  slots={updatedSlots}
233
246
  {...props}
@@ -8,7 +8,7 @@ import { BroadcastChannelNetworkAdapter } from '@automerge/automerge-repo-networ
8
8
  import '@preact/signals-react';
9
9
  import { EditorView } from '@codemirror/view'; // Register react integration.
10
10
  import get from 'lodash.get';
11
- import React, { useEffect, useState } from 'react';
11
+ import React, { useEffect, useMemo, useState } from 'react';
12
12
 
13
13
  import { type Prop } from '@dxos/automerge/automerge';
14
14
  import { Repo, type DocHandle } from '@dxos/automerge/automerge-repo';
@@ -37,17 +37,20 @@ type EditorProps = {
37
37
  };
38
38
 
39
39
  const Editor = ({ handle, path = ['text'] }: EditorProps) => {
40
- const { parentRef } = useTextEditor({
41
- autoFocus: true,
42
- doc: get(handle.docSync()!, path),
43
- extensions: [
44
- //
40
+ const extensions = useMemo(
41
+ () => [
45
42
  EditorView.baseTheme(defaultTheme),
46
43
  EditorView.editorAttributes.of({ class: 'p-2 bg-white' }),
47
44
  createBasicBundle({ placeholder: 'Type here...' }),
48
45
  automerge({ handle, path }),
49
46
  awareness(),
50
47
  ],
48
+ [handle, path],
49
+ );
50
+ const { parentRef } = useTextEditor({
51
+ autoFocus: true,
52
+ doc: get(handle.docSync()!, path),
53
+ extensions,
51
54
  });
52
55
 
53
56
  return <div ref={parentRef} />;
@@ -133,7 +136,7 @@ export const WithEcho = {
133
136
  }),
134
137
  );
135
138
  }}
136
- Component={EchoStory}
139
+ component={EchoStory}
137
140
  />
138
141
  );
139
142
  },
@@ -4,14 +4,16 @@
4
4
 
5
5
  import '@dxosTheme';
6
6
 
7
- import React from 'react';
7
+ import React, { useMemo, useState } from 'react';
8
8
 
9
- import { useThemeContext } from '@dxos/react-ui';
9
+ import { Toolbar as NaturalToolbar, Select, useThemeContext } from '@dxos/react-ui';
10
10
  import { attentionSurface, mx, textBlockWidth } from '@dxos/react-ui-theme';
11
11
  import { withTheme } from '@dxos/storybook-utils';
12
12
 
13
13
  import { TextEditor } from './TextEditor';
14
14
  import {
15
+ type EditorMode,
16
+ EditorModes,
15
17
  code,
16
18
  createMarkdownExtensions,
17
19
  formatting,
@@ -45,11 +47,10 @@ type StoryProps = {
45
47
  const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
46
48
  const { themeMode } = useThemeContext();
47
49
  const [formattingState, trackFormatting] = useFormattingState();
48
- const { parentRef, view } = useTextEditor({
49
- autoFocus,
50
- doc,
51
- extensions: [
52
- //
50
+ const [editorMode, setEditorMode] = useState<EditorMode>('default');
51
+ const extensions = useMemo(
52
+ () => [
53
+ editorMode ? EditorModes[editorMode] : [],
53
54
  createDataExtensions({ readonly }),
54
55
  createThemeExtensions({
55
56
  themeMode,
@@ -71,16 +72,19 @@ const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
71
72
  tasklist(),
72
73
  trackFormatting,
73
74
  ],
74
- });
75
+ [editorMode, themeMode, placeholder, trackFormatting, readonly],
76
+ );
77
+ const { parentRef, view } = useTextEditor({ autoFocus, doc, extensions });
75
78
 
76
79
  const handleAction = useActionHandler(view);
77
80
 
78
- // FIXME This doesn't update the state on view changes. Also not
79
- // sure if view is even guaranteed to exist at this point.
81
+ // TODO(marijn) This doesn't update the state on view changes.
82
+ // Also not sure if view is even guaranteed to exist at this point.
80
83
  return (
81
84
  <div role='none' className={mx('fixed inset-0', editorWithToolbarLayout)}>
82
85
  <Toolbar.Root onAction={handleAction} state={formattingState} classNames={textBlockWidth}>
83
86
  <Toolbar.Markdown />
87
+ <EditorModeToolbar editorMode={editorMode} setEditorMode={setEditorMode} />
84
88
  </Toolbar.Root>
85
89
  <div role='none' className='overflow-y-auto'>
86
90
  <div
@@ -93,6 +97,36 @@ const Story = ({ autoFocus, placeholder, doc, readonly }: StoryProps) => {
93
97
  );
94
98
  };
95
99
 
100
+ const EditorModeToolbar = ({
101
+ editorMode,
102
+ setEditorMode,
103
+ }: {
104
+ editorMode: EditorMode;
105
+ setEditorMode: (mode: EditorMode) => void;
106
+ }) => {
107
+ return (
108
+ <Select.Root value={editorMode} onValueChange={(value) => setEditorMode(value as EditorMode)}>
109
+ <NaturalToolbar.Button asChild>
110
+ <Select.TriggerButton variant='ghost'>{editorMode}</Select.TriggerButton>
111
+ </NaturalToolbar.Button>
112
+ <Select.Portal>
113
+ <Select.Content>
114
+ <Select.ScrollUpButton />
115
+ <Select.Viewport>
116
+ {['default', 'vim'].map((mode) => (
117
+ <Select.Option key={mode} value={mode}>
118
+ {mode}
119
+ </Select.Option>
120
+ ))}
121
+ </Select.Viewport>
122
+ <Select.ScrollDownButton />
123
+ <Select.Arrow />
124
+ </Select.Content>
125
+ </Select.Portal>
126
+ </Select.Root>
127
+ );
128
+ };
129
+
96
130
  export default {
97
131
  title: 'react-ui-editor/useTextEditor',
98
132
  component: TextEditor,
@@ -31,10 +31,10 @@ import { MarkdownEditor } from '../TextEditor';
31
31
 
32
32
  faker.seed(101);
33
33
 
34
- const Story: FC<{ content: string }> = ({ content }) => {
34
+ const Story: FC<{ id?: string; content: string }> = ({ id = 'test', content }) => {
35
35
  const [item] = useState({ text: new TextObject(content) });
36
36
  const [_comments, setComments] = useState<Comment[]>([]);
37
- const [editorRef, editorView] = useEditorView();
37
+ const [editorRef, viewInvalidated] = useEditorView(id);
38
38
  const model = useTextModel({ text: item.text });
39
39
  const [formattingState, formattingObserver] = useFormattingState();
40
40
  const extensions = useMemo(
@@ -57,8 +57,8 @@ const Story: FC<{ content: string }> = ({ content }) => {
57
57
  [],
58
58
  );
59
59
 
60
- useComments(editorView, _comments);
61
- const handleAction = useActionHandler(editorView);
60
+ useComments(viewInvalidated ? null : editorRef.current, id, _comments);
61
+ const handleAction = useActionHandler(editorRef.current);
62
62
 
63
63
  if (!model) {
64
64
  return null;
@@ -35,7 +35,7 @@ export const autocomplete = ({ onSearch }: AutocompleteOptions) => {
35
35
  // https://codemirror.net/examples/autocompletion
36
36
  // https://codemirror.net/docs/ref/#autocomplete.autocompletion
37
37
  autocompletion({
38
- activateOnTyping: true,
38
+ activateOnTyping: false,
39
39
 
40
40
  // closeOnBlur: false,
41
41
  // defaultKeymap: false,
@@ -6,6 +6,7 @@ import get from 'lodash.get';
6
6
 
7
7
  import type { Prop } from '@dxos/automerge/automerge';
8
8
  import { next as A } from '@dxos/automerge/automerge';
9
+ import { log } from '@dxos/log';
9
10
 
10
11
  import { type IDocHandle } from './defs';
11
12
  import { type CursorConverter } from '../cursor';
@@ -23,8 +24,13 @@ export const cursorConverter = (handle: IDocHandle, path: Prop[]): CursorConvert
23
24
  return 'end';
24
25
  }
25
26
 
26
- // NOTE: Slice is needed because getCursor mutates the array.
27
- return A.getCursor(doc, path.slice(), pos);
27
+ try {
28
+ // NOTE: Slice is needed because getCursor mutates the array.
29
+ return A.getCursor(doc, path.slice(), pos);
30
+ } catch (err) {
31
+ log.catch(err);
32
+ return ''; // In case of invalid request (e.g., wrong document).
33
+ }
28
34
  },
29
35
 
30
36
  fromCursor: (cursor) => {
@@ -46,7 +52,12 @@ export const cursorConverter = (handle: IDocHandle, path: Prop[]): CursorConvert
46
52
  }
47
53
  }
48
54
 
49
- // NOTE: Slice is needed because getCursor mutates the array.
50
- return A.getCursorPosition(doc, path.slice(), cursor);
55
+ try {
56
+ // NOTE: Slice is needed because getCursor mutates the array.
57
+ return A.getCursorPosition(doc, path.slice(), cursor);
58
+ } catch (err) {
59
+ log.catch(err);
60
+ return 0; // In case of invalid request (e.g., wrong document).
61
+ }
51
62
  },
52
63
  });
@@ -35,7 +35,6 @@ export type IDocHandle<T = any> = {
35
35
  docSync(): Doc<T> | undefined;
36
36
  change(callback: ChangeFn<T>, options?: ChangeOptions<T>): void;
37
37
  changeAt(heads: Heads, callback: ChangeFn<T>, options?: ChangeOptions<T>): string[] | undefined;
38
-
39
38
  addListener(event: 'change', listener: () => void): void;
40
39
  removeListener(event: 'change', listener: () => void): void;
41
40
  };
@@ -16,18 +16,20 @@ import { type TextEditorProps } from '../components';
16
16
 
17
17
  export type BasicBundleOptions = {
18
18
  themeMode?: ThemeMode;
19
+ bracketMatching?: boolean;
19
20
  } & Pick<TextEditorProps, 'placeholder' | 'lineWrapping'>;
20
21
 
21
22
  export const createBasicBundle = ({
22
23
  themeMode,
23
24
  placeholder: _placeholder,
24
25
  lineWrapping = true,
26
+ bracketMatching: _bracketMatching = true,
25
27
  }: BasicBundleOptions = {}): Extension[] =>
26
28
  [
27
29
  lineWrapping && EditorView.lineWrapping,
28
30
 
29
31
  // https://codemirror.net/docs/ref/#codemirror.minimalSetup
30
- bracketMatching(),
32
+ _bracketMatching && bracketMatching(),
31
33
  closeBrackets(),
32
34
  drawSelection(),
33
35
  highlightActiveLine(),