@dxos/react-ui-thread 0.8.4-main.5ea62a8 → 0.8.4-main.60689f5b1c

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