@assistant-ui/react 0.10.33 → 0.10.34

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
@@ -28,7 +28,7 @@
28
28
  "conversational-ui",
29
29
  "conversational-ai"
30
30
  ],
31
- "version": "0.10.33",
31
+ "version": "0.10.34",
32
32
  "license": "MIT",
33
33
  "type": "module",
34
34
  "exports": {
@@ -57,13 +57,13 @@
57
57
  "@radix-ui/react-use-callback-ref": "^1.1.1",
58
58
  "@radix-ui/react-use-escape-keydown": "^1.1.1",
59
59
  "@standard-schema/spec": "^1.0.0",
60
- "assistant-stream": "^0.2.21",
60
+ "assistant-stream": "^0.2.22",
61
61
  "json-schema": "^0.4.0",
62
62
  "nanoid": "5.1.5",
63
63
  "react-textarea-autosize": "^8.5.9",
64
- "zod": "^3.25.67",
65
- "zustand": "^5.0.6",
66
- "assistant-cloud": "0.0.3"
64
+ "zod": "^4.0.14",
65
+ "zustand": "^5.0.7",
66
+ "assistant-cloud": "0.0.4"
67
67
  },
68
68
  "peerDependencies": {
69
69
  "@types/react": "*",
@@ -83,9 +83,9 @@
83
83
  "@stryker-mutator/core": "^9.0.1",
84
84
  "@stryker-mutator/vitest-runner": "^9.0.1",
85
85
  "@types/json-schema": "^7.0.15",
86
- "@types/node": "^24.0.4",
86
+ "@types/node": "^24.1.0",
87
87
  "eslint": "^9",
88
- "eslint-config-next": "15.3.4",
88
+ "eslint-config-next": "15.4.5",
89
89
  "tsx": "^4.20.3",
90
90
  "vitest": "^3.2.4",
91
91
  "@assistant-ui/x-buildutils": "0.0.1"
@@ -35,16 +35,18 @@ import { MessagePartPrimitiveInProgress } from "../messagePart/MessagePartInProg
35
35
  import { MessagePartStatus } from "../../types/AssistantTypes";
36
36
 
37
37
  type MessagePartGroup = {
38
- parentId: string | undefined;
38
+ groupKey: string | undefined;
39
39
  indices: number[];
40
40
  };
41
41
 
42
+ export type GroupingFunction = (parts: readonly any[]) => MessagePartGroup[];
43
+
42
44
  /**
43
45
  * Groups message parts by their parent ID.
44
46
  * Parts without a parent ID appear in their chronological position as individual groups.
45
47
  * Parts with the same parent ID are grouped together at the position of their first occurrence.
46
48
  */
47
- const groupMessagePartsByParentId = (
49
+ const groupMessagePartsByParentId: GroupingFunction = (
48
50
  parts: readonly any[],
49
51
  ): MessagePartGroup[] => {
50
52
  // Map maintains insertion order, so groups appear in order of first occurrence
@@ -68,33 +70,80 @@ const groupMessagePartsByParentId = (
68
70
  const groups: MessagePartGroup[] = [];
69
71
  for (const [groupId, indices] of groupMap) {
70
72
  // Extract parentId (undefined for ungrouped parts)
71
- const parentId = groupId.startsWith("__ungrouped_") ? undefined : groupId;
72
- groups.push({ parentId, indices });
73
+ const groupKey = groupId.startsWith("__ungrouped_") ? undefined : groupId;
74
+ groups.push({ groupKey, indices });
73
75
  }
74
76
 
75
77
  return groups;
76
78
  };
77
79
 
78
- const useMessagePartsGroupedByParentId = (): MessagePartGroup[] => {
80
+ const useMessagePartsGrouped = (
81
+ groupingFunction: GroupingFunction,
82
+ ): MessagePartGroup[] => {
79
83
  const parts = useMessage((m) => m.content);
80
84
 
81
85
  return useMemo(() => {
82
86
  if (parts.length === 0) {
83
87
  return [];
84
88
  }
85
- return groupMessagePartsByParentId(parts);
86
- }, [parts]);
89
+ return groupingFunction(parts);
90
+ }, [parts, groupingFunction]);
87
91
  };
88
92
 
89
- export namespace MessagePrimitiveUnstable_PartsGroupedByParentId {
93
+ export namespace MessagePrimitiveUnstable_PartsGrouped {
90
94
  export type Props = {
95
+ /**
96
+ * Function that takes an array of message parts and returns an array of groups.
97
+ * Each group contains a key (for identification) and an array of indices.
98
+ *
99
+ * @example
100
+ * ```tsx
101
+ * // Group by parent ID (default behavior)
102
+ * groupingFunction={(parts) => {
103
+ * const groups = new Map<string, number[]>();
104
+ * parts.forEach((part, i) => {
105
+ * const key = part.parentId ?? `__ungrouped_${i}`;
106
+ * const indices = groups.get(key) ?? [];
107
+ * indices.push(i);
108
+ * groups.set(key, indices);
109
+ * });
110
+ * return Array.from(groups.entries()).map(([key, indices]) => ({
111
+ * key: key.startsWith("__ungrouped_") ? undefined : key,
112
+ * indices
113
+ * }));
114
+ * }}
115
+ * ```
116
+ *
117
+ * @example
118
+ * ```tsx
119
+ * // Group by tool name
120
+ * import { groupMessagePartsByToolName } from "@assistant-ui/react";
121
+ *
122
+ * <MessagePrimitive.Unstable_PartsGrouped
123
+ * groupingFunction={groupMessagePartsByToolName}
124
+ * components={{
125
+ * Group: ({ key, indices, children }) => {
126
+ * if (!key) return <>{children}</>;
127
+ * return (
128
+ * <div className="tool-group">
129
+ * <h4>Tool: {key}</h4>
130
+ * {children}
131
+ * </div>
132
+ * );
133
+ * }
134
+ * }}
135
+ * />
136
+ * ```
137
+ */
138
+ groupingFunction: GroupingFunction;
139
+
91
140
  /**
92
141
  * Component configuration for rendering different types of message content.
93
142
  *
94
143
  * You can provide custom components for each content type (text, image, file, etc.)
95
144
  * and configure tool rendering behavior. If not provided, default components will be used.
96
145
  */
97
- components?:
146
+ components:
98
147
  | {
99
148
  /** Component for rendering empty messages */
100
149
  Empty?: EmptyMessagePartComponent | undefined;
@@ -127,28 +176,27 @@ export namespace MessagePrimitiveUnstable_PartsGroupedByParentId {
127
176
  | undefined;
128
177
 
129
178
  /**
130
- * Component for rendering grouped message parts with the same parent ID.
179
+ * Component for rendering grouped message parts.
131
180
  *
132
181
  * When provided, this component will automatically wrap message parts that share
133
- * the same parent ID, allowing you to create collapsible sections, custom styling,
134
- * or other grouped presentations.
182
+ * the same group key as determined by the groupingFunction.
135
183
  *
136
184
  * The component receives:
137
- * - `parentId`: The parent ID shared by all parts in the group (or undefined for ungrouped parts)
185
+ * - `groupKey`: The group key (or undefined for ungrouped parts)
138
186
  * - `indices`: Array of indices for the parts in this group
139
187
  * - `children`: The rendered message part components
140
188
  *
141
189
  * @example
142
190
  * ```tsx
143
- * // Collapsible parent ID group
144
- * Group: ({ parentId, indices, children }) => {
145
- * if (!parentId) return <>{children}</>;
191
+ * // Collapsible group
192
+ * Group: ({ groupKey, indices, children }) => {
193
+ * if (!groupKey) return <>{children}</>;
146
194
  * return (
147
- * <details className="parent-group">
195
+ * <details className="message-group">
148
196
  * <summary>
149
- * Group {parentId} ({indices.length} parts)
197
+ * Group {groupKey} ({indices.length} parts)
150
198
  * </summary>
151
- * <div className="parent-group-content">
199
+ * <div className="group-content">
152
200
  * {children}
153
201
  * </div>
154
202
  * </details>
@@ -156,31 +204,13 @@ export namespace MessagePrimitiveUnstable_PartsGroupedByParentId {
156
204
  * }
157
205
  * ```
158
206
  *
159
- * @example
160
- * ```tsx
161
- * // Custom styled parent ID group
162
- * Group: ({ parentId, indices, children }) => {
163
- * if (!parentId) return <>{children}</>;
164
- * return (
165
- * <div className="border rounded-lg p-4 my-2">
166
- * <div className="text-sm text-gray-600 mb-2">
167
- * Related content ({parentId})
168
- * </div>
169
- * <div className="space-y-2">
170
- * {children}
171
- * </div>
172
- * </div>
173
- * );
174
- * }
175
- * ```
176
- *
177
- * @param parentId - The parent ID shared by all parts in this group (undefined for ungrouped parts)
207
+ * @param groupKey - The group key (undefined for ungrouped parts)
178
208
  * @param indices - Array of indices for the parts in this group
179
209
  * @param children - Rendered message part components to display within the group
180
210
  */
181
211
  Group?: ComponentType<
182
212
  PropsWithChildren<{
183
- parentId: string | undefined;
213
+ groupKey: string | undefined;
184
214
  indices: number[];
185
215
  }>
186
216
  >;
@@ -215,10 +245,10 @@ const defaultComponents = {
215
245
  File: () => null,
216
246
  Unstable_Audio: () => null,
217
247
  Group: ({ children }) => children,
218
- } satisfies MessagePrimitiveUnstable_PartsGroupedByParentId.Props["components"];
248
+ } satisfies MessagePrimitiveUnstable_PartsGrouped.Props["components"];
219
249
 
220
250
  type MessagePartComponentProps = {
221
- components: MessagePrimitiveUnstable_PartsGroupedByParentId.Props["components"];
251
+ components: MessagePrimitiveUnstable_PartsGrouped.Props["components"];
222
252
  };
223
253
 
224
254
  const MessagePartComponent: FC<MessagePartComponentProps> = ({
@@ -276,7 +306,7 @@ const MessagePartComponent: FC<MessagePartComponentProps> = ({
276
306
 
277
307
  type MessagePartProps = {
278
308
  partIndex: number;
279
- components: MessagePrimitiveUnstable_PartsGroupedByParentId.Props["components"];
309
+ components: MessagePrimitiveUnstable_PartsGrouped.Props["components"];
280
310
  };
281
311
 
282
312
  const MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {
@@ -344,23 +374,45 @@ const EmptyParts = memo(
344
374
  );
345
375
 
346
376
  /**
347
- * Renders the parts of a message grouped by their parent ID.
377
+ * Renders the parts of a message grouped by a custom grouping function.
348
378
  *
349
- * This component automatically groups message parts that share the same parent ID,
350
- * allowing you to create hierarchical or related content presentations. Parts without
351
- * a parent ID appear after grouped parts and remain ungrouped.
379
+ * This component allows you to group message parts based on any criteria you define.
380
+ * The grouping function receives all message parts and returns an array of groups,
381
+ * where each group has a key and an array of part indices.
352
382
  *
353
383
  * @example
354
384
  * ```tsx
355
- * <MessagePrimitive.Unstable_PartsGroupedByParentId
385
+ * // Group by parent ID (default behavior)
386
+ * <MessagePrimitive.Unstable_PartsGrouped
356
387
  * components={{
357
388
  * Text: ({ text }) => <p className="message-text">{text}</p>,
358
389
  * Image: ({ image }) => <img src={image} alt="Message image" />,
359
- * Group: ({ parentId, indices, children }) => {
360
- * if (!parentId) return <>{children}</>;
390
+ * Group: ({ groupKey, indices, children }) => {
391
+ * if (!groupKey) return <>{children}</>;
361
392
  * return (
362
393
  * <div className="parent-group border rounded p-4">
363
- * <h4>Related Content</h4>
394
+ * <h4>Parent ID: {groupKey}</h4>
395
+ * {children}
396
+ * </div>
397
+ * );
398
+ * }
399
+ * }}
400
+ * />
401
+ * ```
402
+ *
403
+ * @example
404
+ * ```tsx
405
+ * // Group by tool name
406
+ * import { groupMessagePartsByToolName } from "@assistant-ui/react";
407
+ *
408
+ * <MessagePrimitive.Unstable_PartsGrouped
409
+ * groupingFunction={groupMessagePartsByToolName}
410
+ * components={{
411
+ * Group: ({ groupKey, indices, children }) => {
412
+ * if (!groupKey) return <>{children}</>;
413
+ * return (
414
+ * <div className="tool-group">
415
+ * <h4>Tool: {groupKey}</h4>
364
416
  * {children}
365
417
  * </div>
366
418
  * );
@@ -369,11 +421,11 @@ const EmptyParts = memo(
369
421
  * />
370
422
  * ```
371
423
  */
372
- export const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<
373
- MessagePrimitiveUnstable_PartsGroupedByParentId.Props
374
- > = ({ components }) => {
424
+ export const MessagePrimitiveUnstable_PartsGrouped: FC<
425
+ MessagePrimitiveUnstable_PartsGrouped.Props
426
+ > = ({ groupingFunction, components }) => {
375
427
  const contentLength = useMessage((s) => s.content.length);
376
- const messageGroups = useMessagePartsGroupedByParentId();
428
+ const messageGroups = useMessagePartsGrouped(groupingFunction);
377
429
 
378
430
  const partsElements = useMemo(() => {
379
431
  if (contentLength === 0) {
@@ -385,8 +437,8 @@ export const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<
385
437
 
386
438
  return (
387
439
  <GroupComponent
388
- key={`group-${groupIndex}-${group.parentId ?? "ungrouped"}`}
389
- parentId={group.parentId}
440
+ key={`group-${groupIndex}-${group.groupKey ?? "ungrouped"}`}
441
+ groupKey={group.groupKey}
390
442
  indices={group.indices}
391
443
  >
392
444
  {group.indices.map((partIndex) => (
@@ -404,5 +456,26 @@ export const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<
404
456
  return <>{partsElements}</>;
405
457
  };
406
458
 
459
+ MessagePrimitiveUnstable_PartsGrouped.displayName =
460
+ "MessagePrimitive.Unstable_PartsGrouped";
461
+
462
+ /**
463
+ * Renders the parts of a message grouped by their parent ID.
464
+ * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping.
465
+ *
466
+ * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility
467
+ */
468
+ export const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<
469
+ Omit<MessagePrimitiveUnstable_PartsGrouped.Props, "groupingFunction">
470
+ > = ({ components, ...props }) => {
471
+ return (
472
+ <MessagePrimitiveUnstable_PartsGrouped
473
+ {...props}
474
+ components={components}
475
+ groupingFunction={groupMessagePartsByParentId}
476
+ />
477
+ );
478
+ };
479
+
407
480
  MessagePrimitiveUnstable_PartsGroupedByParentId.displayName =
408
481
  "MessagePrimitive.Unstable_PartsGroupedByParentId";
@@ -4,4 +4,7 @@ export { MessagePrimitiveParts as Content } from "./MessageParts";
4
4
  export { MessagePrimitiveIf as If } from "./MessageIf";
5
5
  export { MessagePrimitiveAttachments as Attachments } from "./MessageAttachments";
6
6
  export { MessagePrimitiveError as Error } from "./MessageError";
7
- export { MessagePrimitiveUnstable_PartsGroupedByParentId as Unstable_PartsGroupedByParentId } from "./MessagePartsGroupedByParentId";
7
+ export {
8
+ MessagePrimitiveUnstable_PartsGrouped as Unstable_PartsGrouped,
9
+ MessagePrimitiveUnstable_PartsGroupedByParentId as Unstable_PartsGroupedByParentId,
10
+ } from "./MessagePartsGrouped";
@@ -184,7 +184,7 @@ export class ExternalStoreThreadRuntimeCore
184
184
  // Common logic for both paths
185
185
  if (messages.length > 0) this.ensureInitialized();
186
186
 
187
- if (oldStore?.isRunning ?? false !== store.isRunning ?? false) {
187
+ if ((oldStore?.isRunning ?? false) !== (store.isRunning ?? false)) {
188
188
  if (store.isRunning) {
189
189
  this._notifyEventSubscribers("run-start");
190
190
  } else {
@@ -1,122 +0,0 @@
1
- import { type ComponentType, type FC, PropsWithChildren } from "react";
2
- import type { Unstable_AudioMessagePartComponent, EmptyMessagePartComponent, TextMessagePartComponent, ImageMessagePartComponent, SourceMessagePartComponent, ToolCallMessagePartComponent, ToolCallMessagePartProps, FileMessagePartComponent, ReasoningMessagePartComponent } from "../../types/MessagePartComponentTypes";
3
- export declare namespace MessagePrimitiveUnstable_PartsGroupedByParentId {
4
- type Props = {
5
- /**
6
- * Component configuration for rendering different types of message content.
7
- *
8
- * You can provide custom components for each content type (text, image, file, etc.)
9
- * and configure tool rendering behavior. If not provided, default components will be used.
10
- */
11
- components?: {
12
- /** Component for rendering empty messages */
13
- Empty?: EmptyMessagePartComponent | undefined;
14
- /** Component for rendering text content */
15
- Text?: TextMessagePartComponent | undefined;
16
- /** Component for rendering reasoning content (typically hidden) */
17
- Reasoning?: ReasoningMessagePartComponent | undefined;
18
- /** Component for rendering source content */
19
- Source?: SourceMessagePartComponent | undefined;
20
- /** Component for rendering image content */
21
- Image?: ImageMessagePartComponent | undefined;
22
- /** Component for rendering file content */
23
- File?: FileMessagePartComponent | undefined;
24
- /** Component for rendering audio content (experimental) */
25
- Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;
26
- /** Configuration for tool call rendering */
27
- tools?: {
28
- /** Map of tool names to their specific components */
29
- by_name?: Record<string, ToolCallMessagePartComponent | undefined> | undefined;
30
- /** Fallback component for unregistered tools */
31
- Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;
32
- } | {
33
- /** Override component that handles all tool calls */
34
- Override: ComponentType<ToolCallMessagePartProps>;
35
- } | undefined;
36
- /**
37
- * Component for rendering grouped message parts with the same parent ID.
38
- *
39
- * When provided, this component will automatically wrap message parts that share
40
- * the same parent ID, allowing you to create collapsible sections, custom styling,
41
- * or other grouped presentations.
42
- *
43
- * The component receives:
44
- * - `parentId`: The parent ID shared by all parts in the group (or undefined for ungrouped parts)
45
- * - `indices`: Array of indices for the parts in this group
46
- * - `children`: The rendered message part components
47
- *
48
- * @example
49
- * ```tsx
50
- * // Collapsible parent ID group
51
- * Group: ({ parentId, indices, children }) => {
52
- * if (!parentId) return <>{children}</>;
53
- * return (
54
- * <details className="parent-group">
55
- * <summary>
56
- * Group {parentId} ({indices.length} parts)
57
- * </summary>
58
- * <div className="parent-group-content">
59
- * {children}
60
- * </div>
61
- * </details>
62
- * );
63
- * }
64
- * ```
65
- *
66
- * @example
67
- * ```tsx
68
- * // Custom styled parent ID group
69
- * Group: ({ parentId, indices, children }) => {
70
- * if (!parentId) return <>{children}</>;
71
- * return (
72
- * <div className="border rounded-lg p-4 my-2">
73
- * <div className="text-sm text-gray-600 mb-2">
74
- * Related content ({parentId})
75
- * </div>
76
- * <div className="space-y-2">
77
- * {children}
78
- * </div>
79
- * </div>
80
- * );
81
- * }
82
- * ```
83
- *
84
- * @param parentId - The parent ID shared by all parts in this group (undefined for ungrouped parts)
85
- * @param indices - Array of indices for the parts in this group
86
- * @param children - Rendered message part components to display within the group
87
- */
88
- Group?: ComponentType<PropsWithChildren<{
89
- parentId: string | undefined;
90
- indices: number[];
91
- }>>;
92
- } | undefined;
93
- };
94
- }
95
- /**
96
- * Renders the parts of a message grouped by their parent ID.
97
- *
98
- * This component automatically groups message parts that share the same parent ID,
99
- * allowing you to create hierarchical or related content presentations. Parts without
100
- * a parent ID appear after grouped parts and remain ungrouped.
101
- *
102
- * @example
103
- * ```tsx
104
- * <MessagePrimitive.Unstable_PartsGroupedByParentId
105
- * components={{
106
- * Text: ({ text }) => <p className="message-text">{text}</p>,
107
- * Image: ({ image }) => <img src={image} alt="Message image" />,
108
- * Group: ({ parentId, indices, children }) => {
109
- * if (!parentId) return <>{children}</>;
110
- * return (
111
- * <div className="parent-group border rounded p-4">
112
- * <h4>Related Content</h4>
113
- * {children}
114
- * </div>
115
- * );
116
- * }
117
- * }}
118
- * />
119
- * ```
120
- */
121
- export declare const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<MessagePrimitiveUnstable_PartsGroupedByParentId.Props>;
122
- //# sourceMappingURL=MessagePartsGroupedByParentId.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MessagePartsGroupedByParentId.d.ts","sourceRoot":"","sources":["../../../src/primitives/message/MessagePartsGroupedByParentId.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,EAAE,EAEP,iBAAiB,EAElB,MAAM,OAAO,CAAC;AAcf,OAAO,KAAK,EACV,kCAAkC,EAClC,yBAAyB,EACzB,wBAAwB,EACxB,yBAAyB,EACzB,0BAA0B,EAC1B,4BAA4B,EAC5B,wBAAwB,EACxB,wBAAwB,EACxB,6BAA6B,EAC9B,MAAM,uCAAuC,CAAC;AAwD/C,yBAAiB,+CAA+C,CAAC;IAC/D,KAAY,KAAK,GAAG;QAClB;;;;;WAKG;QACH,UAAU,CAAC,EACP;YACE,6CAA6C;YAC7C,KAAK,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;YAC9C,2CAA2C;YAC3C,IAAI,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;YAC5C,mEAAmE;YACnE,SAAS,CAAC,EAAE,6BAA6B,GAAG,SAAS,CAAC;YACtD,6CAA6C;YAC7C,MAAM,CAAC,EAAE,0BAA0B,GAAG,SAAS,CAAC;YAChD,4CAA4C;YAC5C,KAAK,CAAC,EAAE,yBAAyB,GAAG,SAAS,CAAC;YAC9C,2CAA2C;YAC3C,IAAI,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;YAC5C,2DAA2D;YAC3D,cAAc,CAAC,EAAE,kCAAkC,GAAG,SAAS,CAAC;YAChE,4CAA4C;YAC5C,KAAK,CAAC,EACF;gBACE,qDAAqD;gBACrD,OAAO,CAAC,EACJ,MAAM,CAAC,MAAM,EAAE,4BAA4B,GAAG,SAAS,CAAC,GACxD,SAAS,CAAC;gBACd,gDAAgD;gBAChD,QAAQ,CAAC,EAAE,aAAa,CAAC,wBAAwB,CAAC,GAAG,SAAS,CAAC;aAChE,GACD;gBACE,qDAAqD;gBACrD,QAAQ,EAAE,aAAa,CAAC,wBAAwB,CAAC,CAAC;aACnD,GACD,SAAS,CAAC;YAEd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAmDG;YACH,KAAK,CAAC,EAAE,aAAa,CACnB,iBAAiB,CAAC;gBAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;gBAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;aACnB,CAAC,CACH,CAAC;SACH,GACD,SAAS,CAAC;KACf,CAAC;CACH;AA4JD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,+CAA+C,EAAE,EAAE,CAC9D,+CAA+C,CAAC,KAAK,CAgCtD,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/primitives/message/MessagePartsGroupedByParentId.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n type ComponentType,\n type FC,\n memo,\n PropsWithChildren,\n useMemo,\n} from \"react\";\nimport {\n TextMessagePartProvider,\n useMessagePart,\n useMessagePartRuntime,\n useToolUIs,\n} from \"../../context\";\nimport {\n useMessage,\n useMessageRuntime,\n} from \"../../context/react/MessageContext\";\nimport { MessagePartRuntimeProvider } from \"../../context/providers/MessagePartRuntimeProvider\";\nimport { MessagePartPrimitiveText } from \"../messagePart/MessagePartText\";\nimport { MessagePartPrimitiveImage } from \"../messagePart/MessagePartImage\";\nimport type {\n Unstable_AudioMessagePartComponent,\n EmptyMessagePartComponent,\n TextMessagePartComponent,\n ImageMessagePartComponent,\n SourceMessagePartComponent,\n ToolCallMessagePartComponent,\n ToolCallMessagePartProps,\n FileMessagePartComponent,\n ReasoningMessagePartComponent,\n} from \"../../types/MessagePartComponentTypes\";\nimport { MessagePartPrimitiveInProgress } from \"../messagePart/MessagePartInProgress\";\nimport { MessagePartStatus } from \"../../types/AssistantTypes\";\n\ntype MessagePartGroup = {\n parentId: string | undefined;\n indices: number[];\n};\n\n/**\n * Groups message parts by their parent ID.\n * Parts without a parent ID appear in their chronological position as individual groups.\n * Parts with the same parent ID are grouped together at the position of their first occurrence.\n */\nconst groupMessagePartsByParentId = (\n parts: readonly any[],\n): MessagePartGroup[] => {\n // Map maintains insertion order, so groups appear in order of first occurrence\n const groupMap = new Map<string, number[]>();\n\n // Process each part in order\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const parentId = part?.parentId as string | undefined;\n\n // For parts without parentId, assign a unique group ID to maintain their position\n const groupId = parentId ?? `__ungrouped_${i}`;\n\n // Get or create the indices array for this group\n const indices = groupMap.get(groupId) ?? [];\n indices.push(i);\n groupMap.set(groupId, indices);\n }\n\n // Convert map to array of groups\n const groups: MessagePartGroup[] = [];\n for (const [groupId, indices] of groupMap) {\n // Extract parentId (undefined for ungrouped parts)\n const parentId = groupId.startsWith(\"__ungrouped_\") ? undefined : groupId;\n groups.push({ parentId, indices });\n }\n\n return groups;\n};\n\nconst useMessagePartsGroupedByParentId = (): MessagePartGroup[] => {\n const parts = useMessage((m) => m.content);\n\n return useMemo(() => {\n if (parts.length === 0) {\n return [];\n }\n return groupMessagePartsByParentId(parts);\n }, [parts]);\n};\n\nexport namespace MessagePrimitiveUnstable_PartsGroupedByParentId {\n export type Props = {\n /**\n * Component configuration for rendering different types of message content.\n *\n * You can provide custom components for each content type (text, image, file, etc.)\n * and configure tool rendering behavior. If not provided, default components will be used.\n */\n components?:\n | {\n /** Component for rendering empty messages */\n Empty?: EmptyMessagePartComponent | undefined;\n /** Component for rendering text content */\n Text?: TextMessagePartComponent | undefined;\n /** Component for rendering reasoning content (typically hidden) */\n Reasoning?: ReasoningMessagePartComponent | undefined;\n /** Component for rendering source content */\n Source?: SourceMessagePartComponent | undefined;\n /** Component for rendering image content */\n Image?: ImageMessagePartComponent | undefined;\n /** Component for rendering file content */\n File?: FileMessagePartComponent | undefined;\n /** Component for rendering audio content (experimental) */\n Unstable_Audio?: Unstable_AudioMessagePartComponent | undefined;\n /** Configuration for tool call rendering */\n tools?:\n | {\n /** Map of tool names to their specific components */\n by_name?:\n | Record<string, ToolCallMessagePartComponent | undefined>\n | undefined;\n /** Fallback component for unregistered tools */\n Fallback?: ComponentType<ToolCallMessagePartProps> | undefined;\n }\n | {\n /** Override component that handles all tool calls */\n Override: ComponentType<ToolCallMessagePartProps>;\n }\n | undefined;\n\n /**\n * Component for rendering grouped message parts with the same parent ID.\n *\n * When provided, this component will automatically wrap message parts that share\n * the same parent ID, allowing you to create collapsible sections, custom styling,\n * or other grouped presentations.\n *\n * The component receives:\n * - `parentId`: The parent ID shared by all parts in the group (or undefined for ungrouped parts)\n * - `indices`: Array of indices for the parts in this group\n * - `children`: The rendered message part components\n *\n * @example\n * ```tsx\n * // Collapsible parent ID group\n * Group: ({ parentId, indices, children }) => {\n * if (!parentId) return <>{children}</>;\n * return (\n * <details className=\"parent-group\">\n * <summary>\n * Group {parentId} ({indices.length} parts)\n * </summary>\n * <div className=\"parent-group-content\">\n * {children}\n * </div>\n * </details>\n * );\n * }\n * ```\n *\n * @example\n * ```tsx\n * // Custom styled parent ID group\n * Group: ({ parentId, indices, children }) => {\n * if (!parentId) return <>{children}</>;\n * return (\n * <div className=\"border rounded-lg p-4 my-2\">\n * <div className=\"text-sm text-gray-600 mb-2\">\n * Related content ({parentId})\n * </div>\n * <div className=\"space-y-2\">\n * {children}\n * </div>\n * </div>\n * );\n * }\n * ```\n *\n * @param parentId - The parent ID shared by all parts in this group (undefined for ungrouped parts)\n * @param indices - Array of indices for the parts in this group\n * @param children - Rendered message part components to display within the group\n */\n Group?: ComponentType<\n PropsWithChildren<{\n parentId: string | undefined;\n indices: number[];\n }>\n >;\n }\n | undefined;\n };\n}\n\nconst ToolUIDisplay = ({\n Fallback,\n ...props\n}: {\n Fallback: ToolCallMessagePartComponent | undefined;\n} & ToolCallMessagePartProps) => {\n const Render = useToolUIs((s) => s.getToolUI(props.toolName)) ?? Fallback;\n if (!Render) return null;\n return <Render {...props} />;\n};\n\nconst defaultComponents = {\n Text: () => (\n <p style={{ whiteSpace: \"pre-line\" }}>\n <MessagePartPrimitiveText />\n <MessagePartPrimitiveInProgress>\n <span style={{ fontFamily: \"revert\" }}>{\" \\u25CF\"}</span>\n </MessagePartPrimitiveInProgress>\n </p>\n ),\n Reasoning: () => null,\n Source: () => null,\n Image: () => <MessagePartPrimitiveImage />,\n File: () => null,\n Unstable_Audio: () => null,\n Group: ({ children }) => children,\n} satisfies MessagePrimitiveUnstable_PartsGroupedByParentId.Props[\"components\"];\n\ntype MessagePartComponentProps = {\n components: MessagePrimitiveUnstable_PartsGroupedByParentId.Props[\"components\"];\n};\n\nconst MessagePartComponent: FC<MessagePartComponentProps> = ({\n components: {\n Text = defaultComponents.Text,\n Reasoning = defaultComponents.Reasoning,\n Image = defaultComponents.Image,\n Source = defaultComponents.Source,\n File = defaultComponents.File,\n Unstable_Audio: Audio = defaultComponents.Unstable_Audio,\n tools = {},\n } = {},\n}) => {\n const MessagePartRuntime = useMessagePartRuntime();\n\n const part = useMessagePart();\n\n const type = part.type;\n if (type === \"tool-call\") {\n const addResult = (result: any) => MessagePartRuntime.addToolResult(result);\n if (\"Override\" in tools)\n return <tools.Override {...part} addResult={addResult} />;\n const Tool = tools.by_name?.[part.toolName] ?? tools.Fallback;\n return <ToolUIDisplay {...part} Fallback={Tool} addResult={addResult} />;\n }\n\n if (part.status.type === \"requires-action\")\n throw new Error(\"Encountered unexpected requires-action status\");\n\n switch (type) {\n case \"text\":\n return <Text {...part} />;\n\n case \"reasoning\":\n return <Reasoning {...part} />;\n\n case \"source\":\n return <Source {...part} />;\n\n case \"image\":\n // eslint-disable-next-line jsx-a11y/alt-text\n return <Image {...part} />;\n\n case \"file\":\n return <File {...part} />;\n\n case \"audio\":\n return <Audio {...part} />;\n\n default:\n const unhandledType: never = type;\n throw new Error(`Unknown message part type: ${unhandledType}`);\n }\n};\n\ntype MessagePartProps = {\n partIndex: number;\n components: MessagePrimitiveUnstable_PartsGroupedByParentId.Props[\"components\"];\n};\n\nconst MessagePartImpl: FC<MessagePartProps> = ({ partIndex, components }) => {\n const messageRuntime = useMessageRuntime();\n const runtime = useMemo(\n () => messageRuntime.getMessagePartByIndex(partIndex),\n [messageRuntime, partIndex],\n );\n\n return (\n <MessagePartRuntimeProvider runtime={runtime}>\n <MessagePartComponent components={components} />\n </MessagePartRuntimeProvider>\n );\n};\n\nconst MessagePart = memo(\n MessagePartImpl,\n (prev, next) =>\n prev.partIndex === next.partIndex &&\n prev.components?.Text === next.components?.Text &&\n prev.components?.Reasoning === next.components?.Reasoning &&\n prev.components?.Source === next.components?.Source &&\n prev.components?.Image === next.components?.Image &&\n prev.components?.File === next.components?.File &&\n prev.components?.Unstable_Audio === next.components?.Unstable_Audio &&\n prev.components?.tools === next.components?.tools &&\n prev.components?.Group === next.components?.Group,\n);\n\nconst COMPLETE_STATUS: MessagePartStatus = Object.freeze({\n type: \"complete\",\n});\n\nconst EmptyPartFallback: FC<{\n status: MessagePartStatus;\n component: TextMessagePartComponent;\n}> = ({ status, component: Component }) => {\n return (\n <TextMessagePartProvider text=\"\" isRunning={status.type === \"running\"}>\n <Component type=\"text\" text=\"\" status={status} />\n </TextMessagePartProvider>\n );\n};\n\nconst EmptyPartsImpl: FC<MessagePartComponentProps> = ({ components }) => {\n const status =\n useMessage((s) => s.status as MessagePartStatus) ?? COMPLETE_STATUS;\n\n if (components?.Empty) return <components.Empty status={status} />;\n\n return (\n <EmptyPartFallback\n status={status}\n component={components?.Text ?? defaultComponents.Text}\n />\n );\n};\n\nconst EmptyParts = memo(\n EmptyPartsImpl,\n (prev, next) =>\n prev.components?.Empty === next.components?.Empty &&\n prev.components?.Text === next.components?.Text,\n);\n\n/**\n * Renders the parts of a message grouped by their parent ID.\n *\n * This component automatically groups message parts that share the same parent ID,\n * allowing you to create hierarchical or related content presentations. Parts without\n * a parent ID appear after grouped parts and remain ungrouped.\n *\n * @example\n * ```tsx\n * <MessagePrimitive.Unstable_PartsGroupedByParentId\n * components={{\n * Text: ({ text }) => <p className=\"message-text\">{text}</p>,\n * Image: ({ image }) => <img src={image} alt=\"Message image\" />,\n * Group: ({ parentId, indices, children }) => {\n * if (!parentId) return <>{children}</>;\n * return (\n * <div className=\"parent-group border rounded p-4\">\n * <h4>Related Content</h4>\n * {children}\n * </div>\n * );\n * }\n * }}\n * />\n * ```\n */\nexport const MessagePrimitiveUnstable_PartsGroupedByParentId: FC<\n MessagePrimitiveUnstable_PartsGroupedByParentId.Props\n> = ({ components }) => {\n const contentLength = useMessage((s) => s.content.length);\n const messageGroups = useMessagePartsGroupedByParentId();\n\n const partsElements = useMemo(() => {\n if (contentLength === 0) {\n return <EmptyParts components={components} />;\n }\n\n return messageGroups.map((group, groupIndex) => {\n const GroupComponent = components?.Group ?? defaultComponents.Group;\n\n return (\n <GroupComponent\n key={`group-${groupIndex}-${group.parentId ?? \"ungrouped\"}`}\n parentId={group.parentId}\n indices={group.indices}\n >\n {group.indices.map((partIndex) => (\n <MessagePart\n key={partIndex}\n partIndex={partIndex}\n components={components}\n />\n ))}\n </GroupComponent>\n );\n });\n }, [messageGroups, components, contentLength]);\n\n return <>{partsElements}</>;\n};\n\nMessagePrimitiveUnstable_PartsGroupedByParentId.displayName =\n \"MessagePrimitive.Unstable_PartsGroupedByParentId\";\n"],"mappings":"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","names":[]}