@dxos/react-ui-thread 0.8.4-main.f9ba587 → 0.8.4-main.fcc0d83b33

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 (36) hide show
  1. package/dist/lib/browser/index.mjs +134 -199
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/browser/translations.mjs +25 -0
  5. package/dist/lib/browser/translations.mjs.map +7 -0
  6. package/dist/types/src/Message/Message.d.ts +2 -2
  7. package/dist/types/src/Message/Message.d.ts.map +1 -1
  8. package/dist/types/src/Message/Message.stories.d.ts +18 -16
  9. package/dist/types/src/Message/Message.stories.d.ts.map +1 -1
  10. package/dist/types/src/Thread/Comments.stories.d.ts +19 -36
  11. package/dist/types/src/Thread/Comments.stories.d.ts.map +1 -1
  12. package/dist/types/src/Thread/Thread.d.ts +25 -11
  13. package/dist/types/src/Thread/Thread.d.ts.map +1 -1
  14. package/dist/types/src/Thread/Thread.stories.d.ts +33 -5
  15. package/dist/types/src/Thread/Thread.stories.d.ts.map +1 -1
  16. package/dist/types/src/index.d.ts +1 -3
  17. package/dist/types/src/index.d.ts.map +1 -1
  18. package/dist/types/src/testing.d.ts.map +1 -1
  19. package/dist/types/src/translations.d.ts +12 -12
  20. package/dist/types/src/translations.d.ts.map +1 -1
  21. package/dist/types/src/types.d.ts +1 -0
  22. package/dist/types/src/types.d.ts.map +1 -1
  23. package/dist/types/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +37 -29
  25. package/src/Message/Message.stories.tsx +21 -14
  26. package/src/Message/Message.tsx +19 -17
  27. package/src/Thread/Comments.stories.tsx +84 -53
  28. package/src/Thread/Thread.stories.tsx +22 -17
  29. package/src/Thread/Thread.tsx +25 -24
  30. package/src/index.ts +1 -4
  31. package/src/testing.tsx +9 -9
  32. package/src/translations.ts +11 -11
  33. package/src/types.ts +1 -0
  34. package/dist/lib/node-esm/index.mjs +0 -268
  35. package/dist/lib/node-esm/index.mjs.map +0 -7
  36. package/dist/lib/node-esm/meta.json +0 -1
package/package.json CHANGED
@@ -1,57 +1,65 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-thread",
3
- "version": "0.8.4-main.f9ba587",
3
+ "version": "0.8.4-main.fcc0d83b33",
4
4
  "description": "A set of components to use with threads, including comments and messages.",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/dxos/dxos"
10
+ },
7
11
  "license": "MIT",
8
12
  "author": "DXOS.org",
9
- "sideEffects": true,
13
+ "sideEffects": false,
10
14
  "type": "module",
15
+ "imports": {
16
+ "#translations": "./src/translations.ts"
17
+ },
11
18
  "exports": {
12
19
  ".": {
20
+ "source": "./src/index.ts",
13
21
  "types": "./dist/types/src/index.d.ts",
14
22
  "browser": "./dist/lib/browser/index.mjs"
23
+ },
24
+ "./translations": {
25
+ "source": "./src/translations.ts",
26
+ "types": "./dist/types/src/translations.d.ts",
27
+ "browser": "./dist/lib/browser/translations.mjs",
28
+ "node": "./dist/lib/node-esm/translations.mjs"
15
29
  }
16
30
  },
17
31
  "types": "dist/types/src/index.d.ts",
18
- "typesVersions": {
19
- "*": {}
20
- },
21
32
  "files": [
22
33
  "dist",
23
34
  "src"
24
35
  ],
25
36
  "dependencies": {
26
- "@preact-signals/safe-react": "^0.9.0",
27
37
  "@radix-ui/react-primitive": "2.0.2",
28
- "date-fns": "^3.3.1",
29
- "@dxos/react-ui-editor": "0.8.4-main.f9ba587",
30
- "@dxos/util": "0.8.4-main.f9ba587"
38
+ "date-fns": "^3.6.0",
39
+ "@dxos/react-ui-editor": "0.8.4-main.fcc0d83b33",
40
+ "@dxos/util": "0.8.4-main.fcc0d83b33",
41
+ "@dxos/ui-editor": "0.8.4-main.fcc0d83b33"
31
42
  },
32
43
  "devDependencies": {
33
- "@phosphor-icons/react": "^2.1.5",
34
- "@types/react": "~18.2.0",
35
- "@types/react-dom": "~18.2.0",
36
- "react": "~18.2.0",
37
- "react-dom": "~18.2.0",
38
- "vite": "5.4.7",
39
- "@dxos/echo-db": "0.8.4-main.f9ba587",
40
- "@dxos/echo-schema": "0.8.4-main.f9ba587",
41
- "@dxos/keys": "0.8.4-main.f9ba587",
42
- "@dxos/live-object": "0.8.4-main.f9ba587",
43
- "@dxos/log": "0.8.4-main.f9ba587",
44
- "@dxos/random": "0.8.4-main.f9ba587",
45
- "@dxos/react-ui": "0.8.4-main.f9ba587",
46
- "@dxos/react-ui-theme": "0.8.4-main.f9ba587",
47
- "@dxos/storybook-utils": "0.8.4-main.f9ba587"
44
+ "@types/react": "~19.2.7",
45
+ "@types/react-dom": "~19.2.3",
46
+ "react": "~19.2.3",
47
+ "react-dom": "~19.2.3",
48
+ "vite": "^8.0.10",
49
+ "@dxos/echo-db": "0.8.4-main.fcc0d83b33",
50
+ "@dxos/echo": "0.8.4-main.fcc0d83b33",
51
+ "@dxos/keys": "0.8.4-main.fcc0d83b33",
52
+ "@dxos/random": "0.8.4-main.fcc0d83b33",
53
+ "@dxos/react-ui": "0.8.4-main.fcc0d83b33",
54
+ "@dxos/ui-theme": "0.8.4-main.fcc0d83b33",
55
+ "@dxos/storybook-utils": "0.8.4-main.fcc0d83b33",
56
+ "@dxos/log": "0.8.4-main.fcc0d83b33"
48
57
  },
49
58
  "peerDependencies": {
50
- "@phosphor-icons/react": "^2.1.5",
51
- "react": "~18.2.0",
52
- "react-dom": "~18.2.0",
53
- "@dxos/react-ui": "0.8.4-main.f9ba587",
54
- "@dxos/react-ui-theme": "0.8.4-main.f9ba587"
59
+ "react": "~19.2.3",
60
+ "react-dom": "~19.2.3",
61
+ "@dxos/react-ui": "0.8.4-main.fcc0d83b33",
62
+ "@dxos/ui-theme": "0.8.4-main.fcc0d83b33"
55
63
  },
56
64
  "publishConfig": {
57
65
  "access": "public"
@@ -2,18 +2,18 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
6
  import React, { useState } from 'react';
8
7
 
9
8
  import { PublicKey } from '@dxos/keys';
10
- import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
11
- import { withLayout, withTheme } from '@dxos/storybook-utils';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
+ import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
12
11
 
13
- import { MessageRoot } from './Message';
12
+ import { translations } from '#translations';
13
+
14
+ import { type MessageEntity, MessageStoryText } from '../testing';
14
15
  import { Thread } from '../Thread';
15
- import { MessageStoryText, type MessageEntity } from '../testing';
16
- import { translations } from '../translations';
16
+ import { MessageRoot } from './Message';
17
17
 
18
18
  const DefaultStory = () => {
19
19
  const [identityKey] = useState(PublicKey.random());
@@ -25,7 +25,7 @@ const DefaultStory = () => {
25
25
  });
26
26
 
27
27
  return (
28
- <div className='mli-auto is-96 overflow-y-auto'>
28
+ <div className='mx-auto w-96 overflow-y-auto'>
29
29
  <Thread.Root id='t1'>
30
30
  <MessageRoot {...message} classNames={[hoverableControls, hoverableFocusedWithinControls]}>
31
31
  <MessageStoryText {...message} onDelete={() => console.log('delete')} />
@@ -35,12 +35,19 @@ const DefaultStory = () => {
35
35
  );
36
36
  };
37
37
 
38
- export default {
38
+ const meta = {
39
39
  title: 'ui/react-ui-thread/Message',
40
- component: MessageRoot,
40
+ component: MessageRoot as any,
41
41
  render: DefaultStory,
42
- decorators: [withTheme, withLayout({ fullscreen: true })],
43
- parameters: { translations },
44
- };
42
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
43
+ parameters: {
44
+ layout: 'fullscreen',
45
+ translations,
46
+ },
47
+ } satisfies Meta<typeof DefaultStory>;
48
+
49
+ export default meta;
50
+
51
+ type Story = StoryObj<typeof meta>;
45
52
 
46
- export const Default = {};
53
+ export const Default: Story = {};
@@ -3,23 +3,25 @@
3
3
  //
4
4
 
5
5
  import { formatDistanceToNow } from 'date-fns/formatDistanceToNow';
6
- import React, { type ComponentPropsWithRef, forwardRef, type ComponentPropsWithoutRef } from 'react';
6
+ import React, { type ComponentPropsWithRef, type ComponentPropsWithoutRef, forwardRef } from 'react';
7
7
 
8
- // TODO(burdon): Remove dep.
9
8
  import { Avatar, type ThemedClassName, useTranslation } from '@dxos/react-ui';
10
- import { type UseTextEditorProps, keymap, listener, useTextEditor } from '@dxos/react-ui-editor';
11
- import { focusRing, mx } from '@dxos/react-ui-theme';
12
- import { hexToEmoji, hexToHue, isNotFalsy } from '@dxos/util';
9
+ import { type UseTextEditorProps, useTextEditor } from '@dxos/react-ui-editor';
10
+ import { keymap, listener } from '@dxos/ui-editor';
11
+ import { mx } from '@dxos/ui-theme';
12
+ import { hexToEmoji, hexToHue, isTruthy } from '@dxos/util';
13
+
14
+ import { translationKey } from '#translations';
13
15
 
14
- import { translationKey } from '../translations';
15
16
  import { type MessageMetadata } from '../types';
16
17
 
17
18
  const avatarSize = 7;
18
19
 
19
- export type MessageRootProps = ThemedClassName<ComponentPropsWithRef<'div'>> &
20
- MessageMetadata &
21
- Partial<{ continues: boolean }>;
20
+ export type MessageRootProps = ThemedClassName<
21
+ ComponentPropsWithRef<'div'> & MessageMetadata & Partial<{ continues: boolean }>
22
+ >;
22
23
 
24
+ // TODO(burdon): Show authorName on tooltip.
23
25
  export const MessageRoot = forwardRef<HTMLDivElement, MessageRootProps>(
24
26
  (
25
27
  { authorImgSrc, authorId, authorName, authorAvatarProps, continues = true, children, classNames, ...rootProps },
@@ -35,16 +37,16 @@ export const MessageRoot = forwardRef<HTMLDivElement, MessageRootProps>(
35
37
  className={mx('grid grid-cols-subgrid col-span-2', classNames)}
36
38
  ref={forwardedRef}
37
39
  >
38
- <div role='none' className='flex flex-col items-center gap-2 pbs-2'>
40
+ <div role='none' className='flex flex-col items-center gap-2 pt-1'>
39
41
  <Avatar.Content
40
42
  size={avatarSize}
41
43
  hue={authorAvatarProps?.hue || hexToHue(authorId ?? '0')}
42
44
  fallback={authorAvatarProps?.emoji || hexToEmoji(authorId ?? '0')}
43
45
  {...(authorImgSrc && { imgSrc: authorImgSrc })}
44
46
  />
45
- {continues && <div role='none' className='is-px grow bg-separator' />}
47
+ {continues && <div role='none' className='w-px grow bg-separator' />}
46
48
  </div>
47
- <div role='none' className='plb-1 min-is-0'>
49
+ <div role='none' className='py-1 min-w-0'>
48
50
  {children}
49
51
  </div>
50
52
  </div>
@@ -73,7 +75,7 @@ export type MessageAuthorNameProps = Pick<MessageMetadata, 'authorName'>;
73
75
  export const MessageAuthorName = ({ authorName }: MessageAuthorNameProps) => {
74
76
  const { t } = useTranslation(translationKey);
75
77
  return (
76
- <Avatar.Label classNames='block truncate text-sm text-subdued'>{authorName ?? t('anonymous label')}</Avatar.Label>
78
+ <Avatar.Label classNames='block truncate text-sm text-subdued'>{authorName ?? t('anonymous.label')}</Avatar.Label>
77
79
  );
78
80
  };
79
81
 
@@ -83,7 +85,7 @@ export const MessageTime = ({ timestamp }: MessageTimeProps) => {
83
85
  const { dtLocale } = useTranslation(translationKey);
84
86
  const dt = timestamp ? new Date(timestamp) : undefined;
85
87
  return (
86
- <time className='block text-subdued text-xs pbe-0.5' dateTime={dt?.toISOString()}>
88
+ <time className='block text-subdued text-xs pb-0.5' dateTime={dt?.toISOString()}>
87
89
  {dt ? formatDistanceToNow(dt, { locale: dtLocale, addSuffix: true }) : ''}
88
90
  </time>
89
91
  );
@@ -148,14 +150,14 @@ export const MessageTextbox = ({
148
150
  extensions: [
149
151
  keymap.of(keyBindings({ onSend, onClear })),
150
152
  listener({
151
- onFocus: (focusing) => {
153
+ onFocus: ({ focusing }) => {
152
154
  if (focusing) {
153
155
  onEditorFocus?.();
154
156
  }
155
157
  },
156
158
  }),
157
159
  extensions,
158
- ].filter(isNotFalsy),
160
+ ].filter(isTruthy),
159
161
  ...editorProps,
160
162
  }),
161
163
  [id, extensions],
@@ -166,7 +168,7 @@ export const MessageTextbox = ({
166
168
  <div
167
169
  role='none'
168
170
  ref={parentRef}
169
- className={mx('plb-0.5 mie-1 rounded-sm', focusRing, disabled && 'opacity-50')}
171
+ className={mx('py-0.5 me-1 rounded-xs dx-focus-ring', disabled && 'opacity-50')}
170
172
  {...focusAttributes}
171
173
  />
172
174
  </MessageRoot>
@@ -2,50 +2,66 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
7
- import { Check, Trash } from '@phosphor-icons/react';
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
8
6
  import React, { type FC, useEffect, useMemo, useRef, useState } from 'react';
9
7
 
10
- import { createDocAccessor, createObject } from '@dxos/echo-db';
11
- import { Expando } from '@dxos/echo-schema';
8
+ import { Obj } from '@dxos/echo';
9
+ import { TestSchema } from '@dxos/echo/testing';
12
10
  import { PublicKey } from '@dxos/keys';
13
- import { live } from '@dxos/live-object';
14
11
  import { log } from '@dxos/log';
15
- import { faker } from '@dxos/random';
16
- import { Button, useThemeContext } from '@dxos/react-ui';
12
+ import { random } from '@dxos/random';
13
+ import { Icon, IconButton, useThemeContext } from '@dxos/react-ui';
14
+ import { useTextEditor } from '@dxos/react-ui-editor';
15
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
17
16
  import {
18
- type Comment,
19
17
  type CommentsOptions,
20
- type Range,
21
- automerge,
18
+ type EditorView,
22
19
  comments,
23
20
  createBasicExtensions,
24
21
  createThemeExtensions,
22
+ documentId,
25
23
  listener,
26
24
  scrollThreadIntoView,
27
- useComments,
28
- useTextEditor,
29
- } from '@dxos/react-ui-editor';
30
- import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
31
- import { withTheme } from '@dxos/storybook-utils';
25
+ setComments,
26
+ } from '@dxos/ui-editor';
27
+ import { type Comment, type Range } from '@dxos/ui-editor/types';
28
+ import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
29
+
30
+ import { translations } from '#translations';
32
31
 
33
- import { Thread } from './Thread';
34
32
  import { MessageBody, MessageHeading, MessageRoot, MessageTextbox } from '../Message';
35
33
  import { type MessageEntity } from '../testing';
36
- import { translations } from '../translations';
34
+ import { Thread } from './Thread';
37
35
 
38
- faker.seed(101);
36
+ random.seed(101);
39
37
 
40
38
  const authorId = PublicKey.random().toHex();
41
39
 
40
+ /**
41
+ * @deprecated This hook will be removed in future versions. Use the new comment sync extension instead.
42
+ * Update comments state field.
43
+ */
44
+ const useComments = (view: EditorView | null | undefined, id: string, comments?: Comment[]) => {
45
+ useEffect(() => {
46
+ if (view) {
47
+ // Check same document.
48
+ // NOTE: Hook might be called before editor state is updated.
49
+ if (id === view.state.facet(documentId)) {
50
+ view.dispatch({
51
+ effects: setComments.of({ id, comments: comments ?? [] }),
52
+ });
53
+ }
54
+ }
55
+ });
56
+ };
57
+
42
58
  //
43
59
  // Editor
44
60
  //
45
61
 
46
62
  const Editor: FC<{
47
63
  id?: string;
48
- item: Expando;
64
+ initialValue: string;
49
65
  comments: Comment[];
50
66
  selected?: string;
51
67
  onCreateComment: CommentsOptions['onCreate'];
@@ -54,7 +70,7 @@ const Editor: FC<{
54
70
  onSelectComment: CommentsOptions['onSelect'];
55
71
  }> = ({
56
72
  id = 'test',
57
- item,
73
+ initialValue,
58
74
  selected: selectedValue,
59
75
  comments: commentRanges,
60
76
  onCreateComment,
@@ -68,11 +84,10 @@ const Editor: FC<{
68
84
  const { parentRef, view } = useTextEditor(
69
85
  () => ({
70
86
  id,
71
- initialValue: item.content,
87
+ initialValue: initialValue,
72
88
  extensions: [
73
89
  createBasicExtensions(),
74
90
  createThemeExtensions({ themeMode }),
75
- automerge(createDocAccessor(item, ['content'])),
76
91
  comments({
77
92
  id,
78
93
  onCreate: onCreateComment,
@@ -82,7 +97,7 @@ const Editor: FC<{
82
97
  }),
83
98
  ],
84
99
  }),
85
- [id, item, themeMode],
100
+ [id, initialValue, themeMode],
86
101
  );
87
102
  useComments(view, id, commentRanges);
88
103
  useEffect(() => {
@@ -115,19 +130,21 @@ const StoryThread: FC<{
115
130
  onSelect: () => void;
116
131
  onResolve: () => void;
117
132
  }> = ({ thread, selected, onSelect, onResolve }) => {
118
- const containerRef = useRef<HTMLDivElement>(null);
119
133
  const { themeMode } = useThemeContext();
134
+ const containerRef = useRef<HTMLDivElement>(null);
135
+
120
136
  // TODO(wittjosiah): This is a hack to reset the editor after a message is sent.
121
- const [_count, _setCount] = useState(0);
122
- const rerenderEditor = () => _setCount((count) => count + 1);
137
+ const [state, setState] = useState({});
123
138
  const messageRef = useRef('');
124
139
  const extensions = useMemo(
125
140
  () => [
126
141
  createBasicExtensions({ placeholder: 'Enter comment' }),
127
142
  createThemeExtensions({ themeMode }),
128
- listener({ onChange: (text) => (messageRef.current = text) }),
143
+ listener({
144
+ onChange: ({ text }) => (messageRef.current = text),
145
+ }),
129
146
  ],
130
- [themeMode, _count],
147
+ [themeMode, state],
131
148
  );
132
149
 
133
150
  const [autoFocus, setAutoFocus] = useState(false);
@@ -143,7 +160,7 @@ const StoryThread: FC<{
143
160
 
144
161
  const handleCreateMessage = () => {
145
162
  if (messageRef.current?.length) {
146
- const message = live(Expando, {
163
+ const message = Obj.make(TestSchema.Expando, {
147
164
  timestamp: new Date().toISOString(),
148
165
  sender: { identityKey: authorId },
149
166
  text: messageRef.current,
@@ -152,7 +169,7 @@ const StoryThread: FC<{
152
169
 
153
170
  messageRef.current = '';
154
171
  setAutoFocus(true);
155
- rerenderEditor();
172
+ setState({});
156
173
  }
157
174
  };
158
175
 
@@ -162,7 +179,7 @@ const StoryThread: FC<{
162
179
  <span>id:{thread.id.slice(0, 4)}</span>
163
180
  <span>y:{thread.yPos}</span>
164
181
  <span className='grow' />
165
- {!thread.cursor && <Trash />}
182
+ {!thread.cursor && <Icon icon='ph--trash--regular' />}
166
183
  </div>
167
184
 
168
185
  <Thread.Header>
@@ -186,20 +203,27 @@ const StoryThread: FC<{
186
203
  onSend={handleCreateMessage}
187
204
  />
188
205
  <Thread.Status />
189
- <Button variant='ghost' classNames='px-1' title='Resolve' onClick={onResolve}>
190
- <Check />
191
- </Button>
206
+ <IconButton
207
+ variant='ghost'
208
+ icon='ph--check--regular'
209
+ iconOnly
210
+ classNames='px-1'
211
+ label='Resolve'
212
+ onClick={onResolve}
213
+ />
192
214
  </div>
193
215
  </Thread.Root>
194
216
  );
195
217
  };
196
218
 
197
- const Sidebar: FC<{
219
+ type SidebarProps = {
198
220
  threads: StoryCommentThread[];
199
221
  selected?: string;
200
222
  onSelect: (thread: string) => void;
201
223
  onResolve: (thread: string) => void;
202
- }> = ({ threads, selected, onSelect, onResolve }) => {
224
+ };
225
+
226
+ const Sidebar = ({ threads, selected, onSelect, onResolve }: SidebarProps) => {
203
227
  // Sort by y-position.
204
228
  const sortedThreads = useMemo(() => {
205
229
  const sorted = [...threads];
@@ -227,13 +251,13 @@ const Sidebar: FC<{
227
251
  // Story container.
228
252
  //
229
253
 
230
- type StoryProps = {
254
+ type DefaultStoryProps = {
231
255
  text?: string;
232
256
  autoCreate?: boolean;
233
257
  };
234
258
 
235
- const Story = ({ text, autoCreate }: StoryProps) => {
236
- const [item] = useState(createObject(live(Expando, { content: text ?? '' })));
259
+ const DefaultStory = ({ text, autoCreate }: DefaultStoryProps) => {
260
+ const [item] = useState(() => Obj.make(TestSchema.Expando, { content: text ?? '' }));
237
261
  const [threads, setThreads] = useState<StoryCommentThread[]>([]);
238
262
  const [selected, setSelected] = useState<string>();
239
263
 
@@ -252,12 +276,12 @@ const Story = ({ text, autoCreate }: StoryProps) => {
252
276
  cursor,
253
277
  yPos: location ? Math.floor(location.top) : undefined,
254
278
  messages: autoCreate
255
- ? faker.helpers.multiple(
279
+ ? random.helpers.multiple(
256
280
  () => ({
257
281
  id: PublicKey.random().toHex(),
258
282
  timestamp: new Date().toISOString(),
259
283
  authorId: PublicKey.random().toHex(),
260
- text: faker.lorem.sentence(),
284
+ text: random.lorem.sentence(),
261
285
  }),
262
286
  { count: { min: 1, max: 3 } },
263
287
  )
@@ -325,9 +349,9 @@ const Story = ({ text, autoCreate }: StoryProps) => {
325
349
 
326
350
  return (
327
351
  <main className='fixed inset-0 grid grid-cols-[1fr_24rem]'>
328
- <div role='none' className='max-bs-full overflow-y-auto p-4'>
352
+ <div role='none' className='max-h-full overflow-y-auto p-4'>
329
353
  <Editor
330
- item={item}
354
+ initialValue={item.content}
331
355
  selected={selected}
332
356
  comments={comments}
333
357
  onCreateComment={handleCreateComment}
@@ -337,7 +361,7 @@ const Story = ({ text, autoCreate }: StoryProps) => {
337
361
  />
338
362
  </div>
339
363
 
340
- <div role='none' className='max-bs-full overflow-y-auto p-4'>
364
+ <div role='none' className='max-h-full overflow-y-auto p-4'>
341
365
  <Sidebar
342
366
  threads={visibleThreads}
343
367
  selected={selected}
@@ -349,22 +373,29 @@ const Story = ({ text, autoCreate }: StoryProps) => {
349
373
  );
350
374
  };
351
375
 
352
- export default {
376
+ const meta = {
353
377
  title: 'ui/react-ui-thread/Comments',
354
- component: StoryThread,
355
- decorators: [withTheme],
356
- render: (args: StoryProps) => <Story {...args} />,
357
- parameters: { translations, layout: 'fullscreen' },
358
- };
378
+ component: StoryThread as any,
379
+ render: DefaultStory,
380
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
381
+ parameters: {
382
+ translations,
383
+ layout: 'fullscreen',
384
+ },
385
+ } satisfies Meta<typeof DefaultStory>;
386
+
387
+ export default meta;
388
+
389
+ type Story = StoryObj<typeof meta>;
359
390
 
360
391
  const str = (...lines: string[]) => lines.join('\n');
361
392
 
362
- export const Default = {
393
+ export const Default: Story = {
363
394
  args: {
364
395
  text: str(
365
396
  '# Comments',
366
397
  '',
367
- str(...faker.helpers.multiple(() => [faker.lorem.paragraph({ min: 5, max: 10 }), ''], { count: 20 }).flat()),
398
+ str(...random.helpers.multiple(() => [random.lorem.paragraph({ min: 5, max: 10 }), ''], { count: 20 }).flat()),
368
399
  '',
369
400
  ),
370
401
  },
@@ -2,22 +2,22 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
6
  import React, { useMemo, useRef, useState } from 'react';
8
7
 
9
8
  import { PublicKey } from '@dxos/keys';
10
- import { faker } from '@dxos/random';
11
- import { createBasicExtensions, createThemeExtensions } from '@dxos/react-ui-editor';
12
- import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/react-ui-theme';
13
- import { type Meta, withLayout, withTheme } from '@dxos/storybook-utils';
9
+ import { random } from '@dxos/random';
10
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
+ import { createBasicExtensions, createThemeExtensions } from '@dxos/ui-editor';
12
+ import { hoverableControls, hoverableFocusedWithinControls } from '@dxos/ui-theme';
13
+
14
+ import { translations } from '#translations';
14
15
 
15
- import { Thread } from './Thread';
16
16
  import { MessageRoot, MessageTextbox } from '../Message';
17
- import { MessageStoryText, type MessageEntity } from '../testing';
18
- import { translations } from '../translations';
17
+ import { type MessageEntity, MessageStoryText } from '../testing';
18
+ import { Thread } from './Thread';
19
19
 
20
- faker.seed(1);
20
+ random.seed(1);
21
21
 
22
22
  const DefaultStory = () => {
23
23
  const [pending, setPending] = useState(false);
@@ -28,7 +28,7 @@ const DefaultStory = () => {
28
28
  id: `m${i + 1}`,
29
29
  timestamp: new Date().toISOString(),
30
30
  authorId: [identityKey1.toHex(), identityKey2.toHex()][i % 2],
31
- text: faker.lorem.paragraph(),
31
+ text: random.lorem.paragraph(),
32
32
  })),
33
33
  );
34
34
 
@@ -59,7 +59,7 @@ const DefaultStory = () => {
59
59
  };
60
60
 
61
61
  return (
62
- <div className='mli-auto is-96 overflow-y-auto'>
62
+ <div className='mx-auto w-96 overflow-y-auto'>
63
63
  <Thread.Root id='t1'>
64
64
  {messages.map((message) => (
65
65
  <MessageRoot key={message.id} classNames={[hoverableControls, hoverableFocusedWithinControls]} {...message}>
@@ -81,14 +81,19 @@ const DefaultStory = () => {
81
81
  );
82
82
  };
83
83
 
84
- const meta: Meta<typeof Thread.Root> = {
84
+ const meta = {
85
85
  title: 'ui/react-ui-thread/Thread',
86
86
  component: Thread.Root,
87
87
  render: DefaultStory,
88
- decorators: [withTheme, withLayout({ fullscreen: true })],
89
- parameters: { translations },
90
- };
88
+ decorators: [withTheme(), withLayout({ layout: 'fullscreen' })],
89
+ parameters: {
90
+ layout: 'fullscreen',
91
+ translations,
92
+ },
93
+ } satisfies Meta<typeof Thread.Root>;
91
94
 
92
95
  export default meta;
93
96
 
94
- export const Default = {};
97
+ type Story = StoryObj<typeof meta>;
98
+
99
+ export const Default: Story = {};