@dxos/react-ui-editor 0.8.3-staging.0fa589b → 0.8.4-main.84f28bd

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 (85) hide show
  1. package/dist/lib/browser/chunk-22UMM3QJ.mjs +22 -0
  2. package/dist/lib/browser/chunk-22UMM3QJ.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +566 -525
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/browser/testing/index.mjs +1 -1
  7. package/dist/lib/browser/types/index.mjs +13 -0
  8. package/dist/lib/browser/types/index.mjs.map +7 -0
  9. package/dist/lib/node-esm/chunk-YXYQPV6R.mjs +24 -0
  10. package/dist/lib/node-esm/chunk-YXYQPV6R.mjs.map +7 -0
  11. package/dist/lib/node-esm/index.mjs +566 -526
  12. package/dist/lib/node-esm/index.mjs.map +4 -4
  13. package/dist/lib/node-esm/meta.json +1 -1
  14. package/dist/lib/node-esm/testing/index.mjs +1 -1
  15. package/dist/lib/node-esm/types/index.mjs +14 -0
  16. package/dist/lib/node-esm/types/index.mjs.map +7 -0
  17. package/dist/types/src/components/Editor/Editor.d.ts +19 -0
  18. package/dist/types/src/components/Editor/Editor.d.ts.map +1 -0
  19. package/dist/types/src/components/Editor/index.d.ts +2 -0
  20. package/dist/types/src/components/Editor/index.d.ts.map +1 -0
  21. package/dist/types/src/components/EditorToolbar/util.d.ts +4 -3
  22. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  23. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +1 -1
  24. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  25. package/dist/types/src/components/Popover/RefDropdownMenu.d.ts.map +1 -1
  26. package/dist/types/src/components/index.d.ts +1 -0
  27. package/dist/types/src/components/index.d.ts.map +1 -1
  28. package/dist/types/src/extensions/automerge/automerge.stories.d.ts +28 -28
  29. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  30. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  31. package/dist/types/src/extensions/command/index.d.ts +1 -1
  32. package/dist/types/src/extensions/command/index.d.ts.map +1 -1
  33. package/dist/types/src/extensions/command/typeahead.d.ts +7 -2
  34. package/dist/types/src/extensions/command/typeahead.d.ts.map +1 -1
  35. package/dist/types/src/extensions/factories.d.ts +18 -0
  36. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  37. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  38. package/dist/types/src/extensions/modes.d.ts +0 -7
  39. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  40. package/dist/types/src/index.d.ts +1 -1
  41. package/dist/types/src/index.d.ts.map +1 -1
  42. package/dist/types/src/stories/CommandMenu.stories.d.ts +1 -1
  43. package/dist/types/src/stories/CommandMenu.stories.d.ts.map +1 -1
  44. package/dist/types/src/stories/EditorToolbar.stories.d.ts +1 -1
  45. package/dist/types/src/stories/EditorToolbar.stories.d.ts.map +1 -1
  46. package/dist/types/src/stories/components/EditorStory.d.ts +2 -2
  47. package/dist/types/src/translations.d.ts +28 -29
  48. package/dist/types/src/translations.d.ts.map +1 -1
  49. package/dist/types/src/types/index.d.ts +2 -0
  50. package/dist/types/src/types/index.d.ts.map +1 -0
  51. package/dist/types/src/types/types.d.ts +21 -0
  52. package/dist/types/src/types/types.d.ts.map +1 -0
  53. package/dist/types/tsconfig.tsbuildinfo +1 -1
  54. package/package.json +44 -41
  55. package/src/components/Editor/Editor.tsx +39 -0
  56. package/src/components/Editor/index.ts +5 -0
  57. package/src/components/EditorToolbar/util.ts +5 -4
  58. package/src/components/EditorToolbar/view-mode.ts +1 -1
  59. package/src/components/Popover/RefDropdownMenu.tsx +9 -3
  60. package/src/components/index.ts +1 -0
  61. package/src/extensions/automerge/automerge.stories.tsx +4 -2
  62. package/src/extensions/blast.ts +3 -16
  63. package/src/extensions/command/index.ts +1 -1
  64. package/src/extensions/command/typeahead.ts +26 -13
  65. package/src/extensions/factories.ts +24 -5
  66. package/src/extensions/markdown/formatting.test.ts +1 -1
  67. package/src/extensions/markdown/formatting.ts +14 -12
  68. package/src/extensions/modes.ts +0 -9
  69. package/src/index.ts +1 -1
  70. package/src/stories/CommandMenu.stories.tsx +1 -1
  71. package/src/stories/EditorToolbar.stories.tsx +3 -4
  72. package/src/stories/Preview.stories.tsx +4 -4
  73. package/src/stories/TextEditor.stories.tsx +1 -1
  74. package/src/styles/theme.ts +1 -1
  75. package/src/translations.ts +4 -2
  76. package/src/types/index.ts +5 -0
  77. package/src/types/types.ts +32 -0
  78. package/dist/lib/node/index.cjs +0 -7754
  79. package/dist/lib/node/index.cjs.map +0 -7
  80. package/dist/lib/node/meta.json +0 -1
  81. package/dist/lib/node/testing/index.cjs +0 -29
  82. package/dist/lib/node/testing/index.cjs.map +0 -7
  83. package/dist/types/src/types.d.ts +0 -14
  84. package/dist/types/src/types.d.ts.map +0 -1
  85. package/src/types.ts +0 -23
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-editor",
3
- "version": "0.8.3-staging.0fa589b",
3
+ "version": "0.8.4-main.84f28bd",
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",
@@ -14,6 +14,11 @@
14
14
  "browser": "./dist/lib/browser/index.mjs",
15
15
  "node": "./dist/lib/node-esm/index.mjs"
16
16
  },
17
+ "./types": {
18
+ "types": "./dist/types/src/types/index.d.ts",
19
+ "browser": "./dist/lib/browser/types/index.mjs",
20
+ "node": "./dist/lib/node-esm/types/index.mjs"
21
+ },
17
22
  "./testing": {
18
23
  "types": "./dist/types/src/testing/index.d.ts",
19
24
  "browser": "./dist/lib/browser/testing/index.mjs",
@@ -29,7 +34,7 @@
29
34
  "src"
30
35
  ],
31
36
  "dependencies": {
32
- "@automerge/automerge": "3.0.0-beta.4",
37
+ "@automerge/automerge": "3.0.0",
33
38
  "@codemirror/autocomplete": "^6.18.1",
34
39
  "@codemirror/commands": "^6.6.2",
35
40
  "@codemirror/lang-javascript": "^6.2.2",
@@ -59,29 +64,28 @@
59
64
  "lodash.merge": "^4.6.2",
60
65
  "lodash.sortby": "^4.7.0",
61
66
  "style-mod": "^4.1.0",
62
- "@dxos/app-graph": "0.8.3-staging.0fa589b",
63
- "@dxos/async": "0.8.3-staging.0fa589b",
64
- "@dxos/context": "0.8.3-staging.0fa589b",
65
- "@dxos/debug": "0.8.3-staging.0fa589b",
66
- "@dxos/display-name": "0.8.3-staging.0fa589b",
67
- "@dxos/lit-ui": "0.8.3-staging.0fa589b",
68
- "@dxos/echo-schema": "0.8.3-staging.0fa589b",
69
- "@dxos/invariant": "0.8.3-staging.0fa589b",
70
- "@dxos/log": "0.8.3-staging.0fa589b",
71
- "@dxos/live-object": "0.8.3-staging.0fa589b",
72
- "@dxos/protocols": "0.8.3-staging.0fa589b",
73
- "@dxos/react-hooks": "0.8.3-staging.0fa589b",
74
- "@dxos/react-ui-menu": "0.8.3-staging.0fa589b",
75
- "@dxos/react-ui-stack": "0.8.3-staging.0fa589b",
76
- "@dxos/util": "0.8.3-staging.0fa589b"
67
+ "@dxos/app-graph": "0.8.4-main.84f28bd",
68
+ "@dxos/context": "0.8.4-main.84f28bd",
69
+ "@dxos/async": "0.8.4-main.84f28bd",
70
+ "@dxos/debug": "0.8.4-main.84f28bd",
71
+ "@dxos/display-name": "0.8.4-main.84f28bd",
72
+ "@dxos/echo-schema": "0.8.4-main.84f28bd",
73
+ "@dxos/invariant": "0.8.4-main.84f28bd",
74
+ "@dxos/live-object": "0.8.4-main.84f28bd",
75
+ "@dxos/lit-ui": "0.8.4-main.84f28bd",
76
+ "@dxos/log": "0.8.4-main.84f28bd",
77
+ "@dxos/protocols": "0.8.4-main.84f28bd",
78
+ "@dxos/react-hooks": "0.8.4-main.84f28bd",
79
+ "@dxos/react-ui-menu": "0.8.4-main.84f28bd",
80
+ "@dxos/react-ui-stack": "0.8.4-main.84f28bd",
81
+ "@dxos/util": "0.8.4-main.84f28bd"
77
82
  },
78
83
  "devDependencies": {
79
- "@automerge/automerge": "3.0.0-beta.4",
80
- "@automerge/automerge-repo": "2.0.1",
81
- "@automerge/automerge-repo-network-broadcastchannel": "2.0.1",
82
- "@effect-rx/rx-react": "^0.34.1",
83
- "@effect/platform": "0.80.12",
84
- "@phosphor-icons/react": "^2.1.5",
84
+ "@automerge/automerge": "3.0.0",
85
+ "@automerge/automerge-repo": "2.0.8",
86
+ "@automerge/automerge-repo-network-broadcastchannel": "2.0.8",
87
+ "@effect-rx/rx-react": "0.38.0",
88
+ "@effect/platform": "0.88.0",
85
89
  "@types/chai": "^4.2.15",
86
90
  "@types/chai-dom": "^1.11.0",
87
91
  "@types/lodash.defaultsdeep": "^4.6.6",
@@ -92,7 +96,7 @@
92
96
  "@types/react-test-renderer": "^17.0.2",
93
97
  "chai": "^4.4.1",
94
98
  "chai-dom": "^1.11.0",
95
- "effect": "3.14.21",
99
+ "effect": "3.16.13",
96
100
  "happy-dom": "^13.3.1",
97
101
  "jsdom": "^24.0.0",
98
102
  "mocha": "^10.6.0",
@@ -102,29 +106,28 @@
102
106
  "vite": "5.4.7",
103
107
  "vite-plugin-top-level-await": "^1.4.1",
104
108
  "vite-plugin-wasm": "^3.3.0",
105
- "@dxos/config": "0.8.3-staging.0fa589b",
106
- "@dxos/echo": "0.8.3-staging.0fa589b",
107
- "@dxos/echo-signals": "0.8.3-staging.0fa589b",
108
- "@dxos/keyboard": "0.8.3-staging.0fa589b",
109
- "@dxos/random": "0.8.3-staging.0fa589b",
110
- "@dxos/react-client": "0.8.3-staging.0fa589b",
111
- "@dxos/react-ui": "0.8.3-staging.0fa589b",
112
- "@dxos/react-ui-syntax-highlighter": "0.8.3-staging.0fa589b",
113
- "@dxos/react-ui-theme": "0.8.3-staging.0fa589b",
114
- "@dxos/schema": "0.8.3-staging.0fa589b",
115
- "@dxos/react-ui-attention": "0.8.3-staging.0fa589b",
116
- "@dxos/storybook-utils": "0.8.3-staging.0fa589b"
109
+ "@dxos/config": "0.8.4-main.84f28bd",
110
+ "@dxos/echo": "0.8.4-main.84f28bd",
111
+ "@dxos/echo-signals": "0.8.4-main.84f28bd",
112
+ "@dxos/keyboard": "0.8.4-main.84f28bd",
113
+ "@dxos/random": "0.8.4-main.84f28bd",
114
+ "@dxos/react-client": "0.8.4-main.84f28bd",
115
+ "@dxos/react-ui": "0.8.4-main.84f28bd",
116
+ "@dxos/react-ui-attention": "0.8.4-main.84f28bd",
117
+ "@dxos/react-ui-syntax-highlighter": "0.8.4-main.84f28bd",
118
+ "@dxos/react-ui-theme": "0.8.4-main.84f28bd",
119
+ "@dxos/schema": "0.8.4-main.84f28bd",
120
+ "@dxos/storybook-utils": "0.8.4-main.84f28bd"
117
121
  },
118
122
  "peerDependencies": {
119
123
  "@effect-rx/rx-react": "^0.34.1",
120
- "@effect/platform": "0.80.12",
121
- "@phosphor-icons/react": "^2.1.5",
124
+ "@effect/platform": "^0.80.12",
122
125
  "effect": "^3.13.3",
123
126
  "react": "~18.2.0",
124
127
  "react-dom": "~18.2.0",
125
- "@dxos/react-client": "0.8.3-staging.0fa589b",
126
- "@dxos/react-ui": "0.8.3-staging.0fa589b",
127
- "@dxos/react-ui-theme": "0.8.3-staging.0fa589b"
128
+ "@dxos/react-client": "0.8.4-main.84f28bd",
129
+ "@dxos/react-ui": "0.8.4-main.84f28bd",
130
+ "@dxos/react-ui-theme": "0.8.4-main.84f28bd"
128
131
  },
129
132
  "publishConfig": {
130
133
  "access": "public"
@@ -0,0 +1,39 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type EditorView } from '@codemirror/view';
6
+ import React, { forwardRef, useImperativeHandle } from 'react';
7
+
8
+ import { type ThemedClassName, useThemeContext } from '@dxos/react-ui';
9
+ import { mx } from '@dxos/react-ui-theme';
10
+ import { type DataType } from '@dxos/schema';
11
+
12
+ import { useTextEditor, type UseTextEditorProps } from '../../hooks';
13
+
14
+ export type EditorProps = ThemedClassName<
15
+ {
16
+ id: string;
17
+ text: DataType.Text;
18
+ } & Omit<UseTextEditorProps, 'id'>
19
+ >;
20
+
21
+ /**
22
+ * Minimal text editor.
23
+ */
24
+ export const Editor = forwardRef<EditorView | undefined, EditorProps>(
25
+ ({ classNames, id, text, ...props }, forwardedRef) => {
26
+ const { themeMode } = useThemeContext();
27
+ const { parentRef, focusAttributes, view } = useTextEditor(
28
+ () => ({
29
+ id,
30
+ initialValue: text.content,
31
+ ...props,
32
+ }),
33
+ [id, text, themeMode],
34
+ );
35
+
36
+ useImperativeHandle(forwardedRef, () => view, [view]);
37
+ return <div ref={parentRef} className={mx(classNames)} {...focusAttributes} />;
38
+ },
39
+ );
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './Editor';
@@ -10,17 +10,18 @@ import { type Action } from '@dxos/app-graph';
10
10
  import { live, type Live } from '@dxos/live-object';
11
11
  import { type ThemedClassName } from '@dxos/react-ui';
12
12
  import {
13
- type MenuSeparator,
13
+ type ActionGraphProps,
14
+ type MenuActionProperties,
14
15
  type MenuItemGroup,
16
+ type MenuSeparator,
15
17
  type ToolbarMenuActionGroupProperties,
16
18
  createMenuAction,
17
19
  createMenuItemGroup,
18
- type ActionGraphProps,
19
- type MenuActionProperties,
20
20
  } from '@dxos/react-ui-menu';
21
21
 
22
- import type { EditorAction, EditorViewMode, Formatting } from '../../extensions';
22
+ import type { EditorAction, Formatting } from '../../extensions';
23
23
  import { translationKey } from '../../translations';
24
+ import { type EditorViewMode } from '../../types';
24
25
 
25
26
  export type EditorToolbarState = Formatting & Partial<{ viewMode: EditorViewMode }>;
26
27
 
@@ -6,8 +6,8 @@ import { type NodeArg } from '@dxos/app-graph';
6
6
  import { type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';
7
7
 
8
8
  import { createEditorAction, createEditorActionGroup, type EditorToolbarState } from './util';
9
- import { type EditorViewMode } from '../../extensions';
10
9
  import { translationKey } from '../../translations';
10
+ import { type EditorViewMode } from '../../types';
11
11
 
12
12
  const createViewModeGroupAction = (value: string) =>
13
13
  createEditorActionGroup(
@@ -11,17 +11,23 @@ import { DropdownMenu } from '@dxos/react-ui';
11
11
 
12
12
  import { type PreviewLinkRef, type PreviewLinkTarget, type PreviewLookup } from '../../extensions';
13
13
 
14
- // TODO(burdon): Reconcile with RefPopover?
14
+ // TODO(burdon): Reconcile this file with RefPopover?
15
15
 
16
16
  const customEventOptions = { capture: true, passive: false };
17
17
 
18
18
  // Create a context for the dxn value.
19
- type RefDropdownMenuValue = Partial<{ link: PreviewLinkRef; target: PreviewLinkTarget; pending: boolean }>;
19
+ type RefDropdownMenuValue = Partial<{
20
+ link: PreviewLinkRef;
21
+ target: PreviewLinkTarget;
22
+ pending: boolean;
23
+ }>;
20
24
 
21
25
  const REF_DROPDOWN_MENU = 'RefDropdownMenu';
22
26
  const [RefDropdownMenuContextProvider, useRefDropdownMenu] = createContext<RefDropdownMenuValue>(REF_DROPDOWN_MENU, {});
23
27
 
24
- type RefDropdownMenuProviderProps = PropsWithChildren<{ onLookup?: PreviewLookup }>;
28
+ type RefDropdownMenuProviderProps = PropsWithChildren<{
29
+ onLookup?: PreviewLookup;
30
+ }>;
25
31
 
26
32
  const RefDropdownMenuProvider = ({ children, onLookup }: RefDropdownMenuProviderProps) => {
27
33
  const trigger = useRef<DxRefTag | null>(null);
@@ -2,5 +2,6 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
+ export * from './Editor';
5
6
  export * from './EditorToolbar';
6
7
  export * from './Popover';
@@ -19,7 +19,7 @@ import { withLayout, withTheme } from '@dxos/storybook-utils';
19
19
 
20
20
  import { editorSlots } from '../../defaults';
21
21
  import { useTextEditor } from '../../hooks';
22
- import translations from '../../translations';
22
+ import { translations } from '../../translations';
23
23
  import { createBasicExtensions, createDataExtensions, createThemeExtensions } from '../factories';
24
24
 
25
25
  const initialContent = 'Hello world!';
@@ -93,7 +93,9 @@ export default {
93
93
  component: Editor,
94
94
  decorators: [withTheme, withLayout({ fullscreen: true })],
95
95
  render: () => <Story />,
96
- parameters: { translations },
96
+ parameters: {
97
+ translations,
98
+ },
97
99
  };
98
100
 
99
101
  const EchoStory = ({ spaceKey }: ClientRepeatedComponentProps) => {
@@ -9,6 +9,7 @@ import { type Extension } from '@codemirror/state';
9
9
  import { EditorView, keymap } from '@codemirror/view';
10
10
  import defaultsDeep from 'lodash.defaultsdeep';
11
11
 
12
+ import { throttle } from '@dxos/async';
12
13
  import { invariant } from '@dxos/invariant';
13
14
 
14
15
  export type BlastOptions = {
@@ -214,12 +215,12 @@ class Blaster {
214
215
  requestAnimationFrame(this.loop.bind(this));
215
216
  }
216
217
 
217
- shake = throttle<{ time: number }>(({ time }) => {
218
+ shake = throttle(({ time }: { time: number }) => {
218
219
  this._shakeTime = this._shakeTimeMax || time;
219
220
  this._shakeTimeMax = time;
220
221
  }, 100);
221
222
 
222
- spawn = throttle<{ element: Element; point: { x: number; y: number } }>(({ element, point }) => {
223
+ spawn = throttle(({ element, point }: { element: Element; point: { x: number; y: number } }) => {
223
224
  const color = getRGBComponents(element, this._options.color);
224
225
  const numParticles = random(this._options.particleNumRange.min, this._options.particleNumRange.max);
225
226
  const dir = this._lastPoint.x === point.x ? 0 : this._lastPoint.x < point.x ? 1 : -1;
@@ -336,20 +337,6 @@ class Effect2 extends Effect {
336
337
  // Utils
337
338
  //
338
339
 
339
- function throttle<T>(callback: (arg: T) => void, limit: number): (arg: T) => void {
340
- let wait = false;
341
- return function (...args: any[]) {
342
- if (!wait) {
343
- // @ts-ignore
344
- callback.apply(this, args);
345
- wait = true;
346
- setTimeout(() => {
347
- wait = false;
348
- }, limit);
349
- }
350
- };
351
- }
352
-
353
340
  const getRGBComponents = (node: Element, color: BlastOptions['color']): Particle['color'] => {
354
341
  if (typeof color === 'function') {
355
342
  return color();
@@ -6,5 +6,5 @@ export * from './action';
6
6
  export * from './command';
7
7
  export * from './command-menu';
8
8
  export * from './floating-menu';
9
- export * from './useCommandMenu';
10
9
  export * from './typeahead';
10
+ export * from './useCommandMenu';
@@ -87,30 +87,43 @@ export const typeahead = ({ onComplete }: TypeaheadOptions = {}): Extension => {
87
87
  ];
88
88
  };
89
89
 
90
+ type CompletionOptions = {
91
+ default?: string;
92
+ minLength?: number;
93
+ };
94
+
90
95
  /**
91
96
  * Util to match current line to a static list of completions.
92
97
  */
93
98
  export const staticCompletion =
94
- (completions: string[], defaultCompletion?: string) =>
99
+ (completions: string[], options: CompletionOptions = {}) =>
95
100
  ({ line }: TypeaheadContext) => {
96
- if (line.length === 0 && defaultCompletion) {
97
- return defaultCompletion;
101
+ if (line.length === 0 && options.default) {
102
+ return options.default;
98
103
  }
99
104
 
100
- const words = line.split(/\s+/).filter(Boolean);
101
- if (words.length) {
102
- const word = words.at(-1)!;
103
- for (const completion of completions) {
104
- const match = matchCompletion(completion, word);
105
- if (match) {
106
- return match;
105
+ const parts = line.split(/\s+/).filter(Boolean);
106
+ if (parts.length) {
107
+ const str = parts.at(-1)!;
108
+ if (str.length >= (options.minLength ?? 0)) {
109
+ for (const completion of completions) {
110
+ const match = matchCompletion(completion, str);
111
+ if (match) {
112
+ return match;
113
+ }
107
114
  }
108
115
  }
109
116
  }
110
117
  };
111
118
 
112
- export const matchCompletion = (completion: string, word: string): string | undefined => {
113
- if (completion.length > word.length && completion.startsWith(word)) {
114
- return completion.slice(word.length);
119
+ export const matchCompletion = (completion: string, str: string, minLength = 0): string | undefined => {
120
+ if (
121
+ str.length >= minLength &&
122
+ completion.length > str.length &&
123
+ completion.startsWith(str)
124
+ // TODO(burdon): If case insensitive, need to replace existing chars.
125
+ // completion.toLowerCase().startsWith(str.toLowerCase())
126
+ ) {
127
+ return completion.slice(str.length);
115
128
  }
116
129
  };
@@ -83,7 +83,7 @@ const defaultBasicOptions: BasicExtensionsOptions = {
83
83
  keymap: 'standard',
84
84
  lineWrapping: true,
85
85
  search: true,
86
- };
86
+ } as const;
87
87
 
88
88
  const keymaps: { [key: string]: readonly KeyBinding[] } = {
89
89
  // https://codemirror.net/docs/ref/#commands.standardKeymap
@@ -93,7 +93,7 @@ const keymaps: { [key: string]: readonly KeyBinding[] } = {
93
93
  };
94
94
 
95
95
  export const createBasicExtensions = (_props?: BasicExtensionsOptions): Extension => {
96
- const props: BasicExtensionsOptions = defaultsDeep({}, _props, defaultBasicOptions);
96
+ const props = defaultsDeep({}, _props, defaultBasicOptions);
97
97
  return [
98
98
  // NOTE: Doesn't catch errors in keymap functions.
99
99
  EditorView.exceptionSink.of((err) => {
@@ -157,17 +157,28 @@ export type ThemeExtensionsOptions = {
157
157
  scroll?: {
158
158
  className?: string;
159
159
  };
160
+ scroller?: {
161
+ className?: string;
162
+ };
160
163
  content?: {
161
164
  className?: string;
162
165
  };
163
166
  };
164
167
  };
165
168
 
166
- const defaultThemeSlots = {
169
+ export const grow = {
167
170
  editor: {
168
- className: 'w-full bs-full',
171
+ className: 'is-full bs-full',
169
172
  },
170
- };
173
+ } as const;
174
+
175
+ export const fullWidth = {
176
+ editor: {
177
+ className: 'is-full',
178
+ },
179
+ } as const;
180
+
181
+ export const defaultThemeSlots = grow;
171
182
 
172
183
  /**
173
184
  * https://codemirror.net/examples/styling
@@ -195,6 +206,14 @@ export const createThemeExtensions = ({
195
206
  }
196
207
  },
197
208
  ),
209
+ slots.scroller?.className &&
210
+ ViewPlugin.fromClass(
211
+ class {
212
+ constructor(view: EditorView) {
213
+ view.dom.querySelector('.cm-scroller')?.classList.add(...slots.scroller.className.split(' '));
214
+ }
215
+ },
216
+ ),
198
217
  ].filter(isNotFalsy);
199
218
  };
200
219
 
@@ -182,7 +182,7 @@ describe('removeStyle', () => {
182
182
 
183
183
  testCommand('can remove code style', 'a `{variable}`', code, 'a {variable}');
184
184
 
185
- // TODO(dmaretskyi): Flaky on CI: https://cloud.nx.app/runs/0byxg4Uq5G/task/react-ui-editor%3Atest
185
+ // TODO(dmaretskyi): Flaky on CI.
186
186
  // testCommand(
187
187
  // 'can remove emphasis across multiple blocks',
188
188
  // '{*one*\n\n# *two*\n\n> 1. *three} four*\n',
@@ -13,10 +13,11 @@ import {
13
13
  type StateCommand,
14
14
  type Text,
15
15
  } from '@codemirror/state';
16
- import { EditorView, keymap } from '@codemirror/view';
16
+ import { EditorView, keymap, type ViewUpdate } from '@codemirror/view';
17
17
  import { type SyntaxNodeRef, type SyntaxNode } from '@lezer/common';
18
- import { useMemo } from 'react';
18
+ import { useCallback, useMemo } from 'react';
19
19
 
20
+ import { debounceAndThrottle } from '@dxos/async';
20
21
  import { type Live } from '@dxos/live-object';
21
22
 
22
23
  import { type EditorToolbarState } from '../../components';
@@ -1251,15 +1252,16 @@ export const getFormatting = (state: EditorState): Formatting => {
1251
1252
  * Hook provides an extension to compute the current formatting state.
1252
1253
  */
1253
1254
  export const useFormattingState = (state: Live<EditorToolbarState>): Extension => {
1254
- return useMemo(
1255
- () =>
1256
- EditorView.updateListener.of((update) => {
1257
- if (update.docChanged || update.selectionSet) {
1258
- Object.entries(getFormatting(update.state)).forEach(([key, active]) => {
1259
- state[key as keyof Formatting] = active as any;
1260
- });
1261
- }
1262
- }),
1263
- [],
1255
+ const handleUpdate = useCallback(
1256
+ debounceAndThrottle((update: ViewUpdate) => {
1257
+ if (update.docChanged || update.selectionSet) {
1258
+ Object.entries(getFormatting(update.state)).forEach(([key, active]) => {
1259
+ state[key as keyof Formatting] = active as any;
1260
+ });
1261
+ }
1262
+ }, 100),
1263
+ [state],
1264
1264
  );
1265
+
1266
+ return useMemo(() => EditorView.updateListener.of(handleUpdate), [handleUpdate]);
1265
1267
  };
@@ -6,18 +6,9 @@ import { type Extension } from '@codemirror/state';
6
6
  import { keymap } from '@codemirror/view';
7
7
  import { vim } from '@replit/codemirror-vim';
8
8
  import { vscodeKeymap } from '@replit/codemirror-vscode-keymap';
9
- import { Schema } from 'effect';
10
9
 
11
10
  import { singleValueFacet } from '../util';
12
11
 
13
- export const EditorViewModes = ['preview', 'readonly', 'source'] as const;
14
- export const EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
15
- export type EditorViewMode = Schema.Schema.Type<typeof EditorViewMode>;
16
-
17
- export const EditorInputModes = ['default', 'vim', 'vscode'] as const;
18
- export const EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
19
- export type EditorInputMode = Schema.Schema.Type<typeof EditorInputMode>;
20
-
21
12
  export type EditorInputConfig = {
22
13
  type?: string;
23
14
  noTabster?: boolean;
package/src/index.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import translations from './translations';
5
+ import { translations } from './translations';
6
6
 
7
7
  export { type Extension, EditorState } from '@codemirror/state';
8
8
  export { EditorView, keymap } from '@codemirror/view';
@@ -5,7 +5,7 @@
5
5
  import '@dxos-theme';
6
6
 
7
7
  import { type EditorView } from '@codemirror/view';
8
- import { type StoryObj } from '@storybook/react';
8
+ import { type StoryObj } from '@storybook/react-vite';
9
9
  import React, { useCallback, useRef } from 'react';
10
10
 
11
11
  import { Obj, Query } from '@dxos/echo';
@@ -4,7 +4,7 @@
4
4
 
5
5
  import '@dxos-theme';
6
6
 
7
- import { type StoryObj } from '@storybook/react';
7
+ import { type StoryObj } from '@storybook/react-vite';
8
8
  import React, { useCallback, useState } from 'react';
9
9
 
10
10
  import { invariant } from '@dxos/invariant';
@@ -15,8 +15,6 @@ import { type Meta, withLayout, withTheme } from '@dxos/storybook-utils';
15
15
  import { EditorToolbar, useEditorToolbarState } from '../components';
16
16
  import { editorWidth } from '../defaults';
17
17
  import {
18
- type EditorInputMode,
19
- type EditorViewMode,
20
18
  InputModeExtensions,
21
19
  createMarkdownExtensions,
22
20
  createBasicExtensions,
@@ -26,7 +24,8 @@ import {
26
24
  useFormattingState,
27
25
  } from '../extensions';
28
26
  import { useTextEditor, type UseTextEditorProps } from '../hooks';
29
- import translations from '../translations';
27
+ import { translations } from '../translations';
28
+ import { type EditorInputMode, type EditorViewMode } from '../types';
30
29
 
31
30
  type StoryProps = { placeholder?: string } & UseTextEditorProps;
32
31
 
@@ -48,10 +48,10 @@ const PreviewCard = () => {
48
48
  <Popover.Portal>
49
49
  <Popover.Content onOpenAutoFocus={(event) => event.preventDefault()}>
50
50
  <Popover.Viewport>
51
- <Card.Container role='popover'>
51
+ <Card.SurfaceRoot role='popover'>
52
52
  <Card.Heading>{target?.label}</Card.Heading>
53
53
  {target && <Card.Text classNames='line-clamp-3'>{target.text}</Card.Text>}
54
- </Card.Container>
54
+ </Card.SurfaceRoot>
55
55
  </Popover.Viewport>
56
56
  <Popover.Arrow />
57
57
  </Popover.Content>
@@ -126,7 +126,7 @@ const PreviewBlock = ({ link, el, view }: { link: PreviewLinkRef; el: HTMLElemen
126
126
  }, [handleAction, link, target]);
127
127
 
128
128
  return createPortal(
129
- <Card.Content classNames={hoverableControls}>
129
+ <Card.StaticRoot classNames={hoverableControls}>
130
130
  <div className='flex items-start'>
131
131
  {!view?.state.readOnly && (
132
132
  <Card.Toolbar classNames='is-min p-[--dx-cardSpacingInline]'>
@@ -159,7 +159,7 @@ const PreviewBlock = ({ link, el, view }: { link: PreviewLinkRef; el: HTMLElemen
159
159
  </Card.Heading>
160
160
  </div>
161
161
  {target && <Card.Text classNames='line-clamp-3 mbs-0'>{target.text}</Card.Text>}
162
- </Card.Content>,
162
+ </Card.StaticRoot>,
163
163
  el,
164
164
  );
165
165
  };
@@ -217,7 +217,7 @@ export const Typeahead = {
217
217
  extensions={[
218
218
  decorateMarkdown({ renderLinkButton }),
219
219
  typeahead({
220
- onComplete: staticCompletion(completions, completions[0]),
220
+ onComplete: staticCompletion(completions, { minLength: 2 }),
221
221
  }),
222
222
  ]}
223
223
  />
@@ -110,7 +110,7 @@ export const defaultTheme: ThemeStyles = {
110
110
  borderLeft: '2px solid var(--dx-cmCursor)',
111
111
  },
112
112
  '.cm-placeholder': {
113
- color: 'var(--dx-subdued)',
113
+ color: 'var(--dx-placeholder)',
114
114
  },
115
115
 
116
116
  /**
@@ -2,9 +2,11 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
+ import { type Resource } from '@dxos/react-ui';
6
+
5
7
  export const translationKey = 'react-ui-editor';
6
8
 
7
- export default [
9
+ export const translations = [
8
10
  {
9
11
  'en-US': {
10
12
  [translationKey]: {
@@ -35,4 +37,4 @@ export default [
35
37
  },
36
38
  },
37
39
  },
38
- ];
40
+ ] as const satisfies Resource[];
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ export * from './types';