@dxos/react-ui-thread 0.8.4-main.b97322e → 0.8.4-main.bc2380dfbc

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