@dxos/react-ui-thread 0.14.0 → 0.16.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/react-ui-thread",
3
- "version": "0.14.0",
3
+ "version": "0.16.0",
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",
@@ -41,15 +41,16 @@
41
41
  "@codemirror/state": "^6.7.6",
42
42
  "@codemirror/view": "^6.43.13",
43
43
  "date-fns": "^3.6.0",
44
- "@dxos/echo": "0.14.0",
45
- "@dxos/react-hooks": "0.14.0",
46
- "@dxos/react-ui-dnd": "0.14.0",
47
- "@dxos/react-ui-editor": "0.14.0",
48
- "@dxos/react-ui-mosaic": "0.14.0",
49
- "@dxos/types": "0.14.0",
50
- "@dxos/ui-editor": "0.14.0",
51
- "@dxos/ui-types": "0.14.0",
52
- "@dxos/util": "0.14.0"
44
+ "@dxos/echo-react": "0.16.0",
45
+ "@dxos/echo": "0.16.0",
46
+ "@dxos/react-hooks": "0.16.0",
47
+ "@dxos/react-ui-dnd": "0.16.0",
48
+ "@dxos/react-ui-editor": "0.16.0",
49
+ "@dxos/types": "0.16.0",
50
+ "@dxos/react-ui-mosaic": "0.16.0",
51
+ "@dxos/ui-editor": "0.16.0",
52
+ "@dxos/ui-types": "0.16.0",
53
+ "@dxos/util": "0.16.0"
53
54
  },
54
55
  "devDependencies": {
55
56
  "@storybook/react-vite": "^10.6.0",
@@ -57,17 +58,17 @@
57
58
  "@types/react-dom": "~19.2.3",
58
59
  "react": "~19.2.7",
59
60
  "react-dom": "~19.2.7",
60
- "vite": "^8.2.2",
61
- "@dxos/random": "0.14.0",
62
- "@dxos/react-ui": "0.14.0",
63
- "@dxos/schema": "0.14.0",
64
- "@dxos/ui-theme": "0.14.0"
61
+ "vite": "^8.3.4",
62
+ "@dxos/random": "0.16.0",
63
+ "@dxos/react-ui": "0.16.0",
64
+ "@dxos/schema": "0.16.0",
65
+ "@dxos/ui-theme": "0.16.0"
65
66
  },
66
67
  "peerDependencies": {
67
68
  "react": "~19.2.7",
68
69
  "react-dom": "~19.2.7",
69
- "@dxos/react-ui": "^0.14.0",
70
- "@dxos/ui-theme": "^0.14.0"
70
+ "@dxos/react-ui": "^0.16.0",
71
+ "@dxos/ui-theme": "^0.16.0"
71
72
  },
72
73
  "publishConfig": {
73
74
  "access": "public"
@@ -17,6 +17,7 @@ import React, {
17
17
  } from 'react';
18
18
 
19
19
  import { Obj, Ref } from '@dxos/echo';
20
+ import { useObject } from '@dxos/echo-react';
20
21
  import { type UseTextEditorProps, useTextEditor } from '@dxos/react-ui-editor';
21
22
  import * as Avatar from '@dxos/react-ui/Avatar';
22
23
  import * as Button from '@dxos/react-ui/Button';
@@ -157,18 +158,19 @@ export type MessageBodyProps = {
157
158
  */
158
159
  const MessageBody = ({ message, isAuthor, editing, onSave }: MessageBodyProps) => {
159
160
  const { components } = useThreadContext('Message.Body');
160
- const textBlockIndex = message.blocks.findIndex((block) => block._tag === 'text');
161
- const textBlock = textBlockIndex !== -1 ? (message.blocks[textBlockIndex] as ContentBlock.Text) : undefined;
162
- const proposalBlock = message.blocks.find((block) => block._tag === 'proposal') as ContentBlock.Proposal | undefined;
163
- const changeBlock = message.blocks.find((block) => block._tag === 'change') as ContentBlock.Change | undefined;
164
- const references = message.blocks
161
+ const [{ blocks }] = useObject(message);
162
+ const textBlockIndex = blocks.findIndex((block) => block._tag === 'text');
163
+ const textBlock = textBlockIndex !== -1 ? (blocks[textBlockIndex] as ContentBlock.Text) : undefined;
164
+ const proposalBlock = blocks.find((block) => block._tag === 'proposal') as ContentBlock.Proposal | undefined;
165
+ const changeBlock = blocks.find((block) => block._tag === 'change') as ContentBlock.Change | undefined;
166
+ const references = blocks
165
167
  .filter((block) => block._tag === 'reference')
166
168
  .map((block) => (block as ContentBlock.Reference).reference);
167
169
  const Object = components.Object;
168
170
 
169
171
  return (
170
172
  <>
171
- {textBlock && <TextBlock block={textBlock} isAuthor={isAuthor} editing={editing} onSave={onSave} />}
173
+ {textBlock && <TextBlock text={textBlock.text} isAuthor={isAuthor} editing={editing} onSave={onSave} />}
172
174
  {proposalBlock && <div className='me-4 italic'>{proposalBlock.text}</div>}
173
175
  {changeBlock && (
174
176
  <p className='me-4 text-sm break-words'>
@@ -188,18 +190,18 @@ const MessageBody = ({ message, isAuthor, editing, onSave }: MessageBodyProps) =
188
190
  MessageBody.displayName = 'Message.Body';
189
191
 
190
192
  const TextBlock = ({
191
- block,
193
+ text,
192
194
  isAuthor,
193
195
  editing,
194
196
  onSave,
195
197
  }: {
196
- block: ContentBlock.Text;
198
+ text: string;
197
199
  isAuthor?: boolean;
198
200
  editing?: boolean;
199
201
  onSave?: (text: string) => void;
200
202
  }) => {
201
203
  const themeMode = Hooks.useThemeMode();
202
- const inMemoryContentRef = useRef(block.text);
204
+ const inMemoryContentRef = useRef(text);
203
205
 
204
206
  const handleDocumentChange = useCallback((next: string) => {
205
207
  inMemoryContentRef.current = next;
@@ -213,7 +215,7 @@ const TextBlock = ({
213
215
 
214
216
  const { parentRef, focusAttributes, view } = useTextEditor(
215
217
  () => ({
216
- initialValue: block.text,
218
+ initialValue: text,
217
219
  extensions: [
218
220
  // Edit mode is authorisation enough — the control that gets here is already gated on
219
221
  // `isAuthor` — and a mid-edit flip to read-only makes the editor drop input silently.
@@ -228,9 +230,9 @@ const TextBlock = ({
228
230
  ],
229
231
  }),
230
232
  // While editing, the editor owns its content and its authorisation: pinning both keeps an incoming
231
- // `block.text` update or member-list refresh from rebuilding the view being typed in. `editing` is
233
+ // `text` update or member-list refresh from rebuilding the view being typed in. `editing` is
232
234
  // itself a dep, so the flip still rebuilds.
233
- [editing, editing ? undefined : block.text, editing ? undefined : isAuthor, themeMode, handleDocumentChange],
235
+ [editing, editing ? undefined : text, editing ? undefined : isAuthor, themeMode, handleDocumentChange],
234
236
  );
235
237
 
236
238
  useEffect(() => {
@@ -367,10 +369,11 @@ const MessageTile = ({ message, classNames, continues = true }: MessageTileProps
367
369
  } = useThreadContext('Message.Tile');
368
370
  const [editing, setEditing] = useState(false);
369
371
 
372
+ const [blocks] = useObject(message, 'blocks');
370
373
  const metadata = getMetadata(message);
371
374
  const isAuthor = !!identityDid && identityDid === metadata.authorId;
372
- const hasProposal = message.blocks.some((block) => block._tag === 'proposal');
373
- const hasChange = message.blocks.some((block) => block._tag === 'change');
375
+ const hasProposal = blocks.some((block) => block._tag === 'proposal');
376
+ const hasChange = blocks.some((block) => block._tag === 'change');
374
377
 
375
378
  const handleEdit = useCallback(() => setEditing((value) => !value), []);
376
379
  const handleDelete = useCallback(() => onMessageDelete?.(message.id), [onMessageDelete, message.id]);
@@ -507,9 +510,10 @@ const MessageGroup = ({ messages, continues = true, classNames }: MessageGroupPr
507
510
  const [editing, setEditing] = useState(false);
508
511
 
509
512
  const first = messages[0];
513
+ const [blocks] = useObject(first, 'blocks');
510
514
  const metadata = getMetadata(first);
511
515
  const isAuthor = !!identityDid && identityDid === metadata.authorId;
512
- const hasProposal = first.blocks.some((block) => block._tag === 'proposal');
516
+ const hasProposal = blocks.some((block) => block._tag === 'proposal');
513
517
 
514
518
  const handleEdit = useCallback(() => setEditing((value) => !value), []);
515
519
  const handleDelete = useCallback(() => onMessageDelete?.(first.id), [onMessageDelete, first.id]);