@assistant-ui/core 0.1.16 → 0.2.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/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/react/index.d.ts +3 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +2 -0
- package/dist/react/index.js.map +1 -1
- package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts +5 -0
- package/dist/react/primitive-hooks/useThreadListLoadMore.d.ts.map +1 -0
- package/dist/react/primitive-hooks/useThreadListLoadMore.js +11 -0
- package/dist/react/primitive-hooks/useThreadListLoadMore.js.map +1 -0
- package/dist/react/primitives/message/MessageGroupedParts.d.ts +104 -0
- package/dist/react/primitives/message/MessageGroupedParts.d.ts.map +1 -0
- package/dist/react/primitives/message/MessageGroupedParts.js +74 -0
- package/dist/react/primitives/message/MessageGroupedParts.js.map +1 -0
- package/dist/react/primitives/message/MessageParts.d.ts +8 -1
- package/dist/react/primitives/message/MessageParts.d.ts.map +1 -1
- package/dist/react/primitives/message/MessageParts.js +45 -42
- package/dist/react/primitives/message/MessageParts.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts +2 -4
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +4 -3
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +8 -6
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +86 -38
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/useLocalRuntime.d.ts +1 -1
- package/dist/react/utils/groupParts.d.ts +49 -0
- package/dist/react/utils/groupParts.d.ts.map +1 -0
- package/dist/react/utils/groupParts.js +97 -0
- package/dist/react/utils/groupParts.js.map +1 -0
- package/dist/runtime/api/assistant-runtime.d.ts +0 -33
- package/dist/runtime/api/assistant-runtime.d.ts.map +1 -1
- package/dist/runtime/api/assistant-runtime.js +0 -23
- package/dist/runtime/api/assistant-runtime.js.map +1 -1
- package/dist/runtime/api/bindings.d.ts +1 -3
- package/dist/runtime/api/bindings.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.d.ts +3 -3
- package/dist/runtime/api/composer-runtime.d.ts.map +1 -1
- package/dist/runtime/api/composer-runtime.js +1 -1
- package/dist/runtime/api/composer-runtime.js.map +1 -1
- package/dist/runtime/api/message-runtime.d.ts +1 -6
- package/dist/runtime/api/message-runtime.d.ts.map +1 -1
- package/dist/runtime/api/message-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts +18 -3
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts +4 -0
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +6 -0
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +6 -29
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +2 -21
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts +4 -3
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +47 -33
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts +3 -4
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +11 -11
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +28 -2
- package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +3 -0
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-runtime-core.d.ts +35 -4
- package/dist/runtime/interfaces/thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/utils/chat-model-adapter.d.ts +0 -4
- package/dist/runtime/utils/chat-model-adapter.d.ts.map +1 -1
- package/dist/runtime/utils/external-store-message.d.ts +0 -4
- package/dist/runtime/utils/external-store-message.d.ts.map +1 -1
- package/dist/runtime/utils/external-store-message.js +0 -7
- package/dist/runtime/utils/external-store-message.js.map +1 -1
- package/dist/runtimes/assistant-transport/utils.d.ts +0 -9
- package/dist/runtimes/assistant-transport/utils.d.ts.map +1 -1
- package/dist/runtimes/assistant-transport/utils.js +0 -13
- package/dist/runtimes/assistant-transport/utils.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts +0 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js +2 -5
- package/dist/runtimes/external-store/external-store-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts +0 -1
- package/dist/runtimes/local/local-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/local/local-thread-runtime-core.js +2 -6
- package/dist/runtimes/local/local-thread-runtime-core.js.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts +0 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.d.ts.map +1 -1
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js +0 -3
- package/dist/runtimes/readonly/ReadonlyThreadRuntimeCore.js.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/empty-thread-core.js +0 -3
- package/dist/runtimes/remote-thread-list/empty-thread-core.js.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +12 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js +34 -0
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +5 -1
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/clients/thread-message-client.d.ts.map +1 -1
- package/dist/store/clients/thread-message-client.js +0 -1
- package/dist/store/clients/thread-message-client.js.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +5 -6
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.js +3 -0
- package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +0 -1
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/scopes/composer.d.ts +11 -1
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +1 -3
- package/dist/store/scopes/message.d.ts.map +1 -1
- package/dist/store/scopes/thread-list-item.d.ts +10 -0
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/dist/store/scopes/thread.d.ts +17 -4
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/store/scopes/threads.d.ts +3 -0
- package/dist/store/scopes/threads.d.ts.map +1 -1
- package/dist/subscribable/subscribable.d.ts +4 -4
- package/dist/subscribable/subscribable.d.ts.map +1 -1
- package/dist/subscribable/subscribable.js +4 -4
- package/dist/subscribable/subscribable.js.map +1 -1
- package/package.json +25 -13
- package/src/index.ts +12 -6
- package/src/react/index.ts +3 -0
- package/src/react/primitive-hooks/useThreadListLoadMore.ts +15 -0
- package/src/react/primitives/message/MessageGroupedParts.tsx +186 -0
- package/src/react/primitives/message/MessageParts.tsx +80 -55
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +7 -6
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +96 -43
- package/src/react/utils/groupParts.ts +152 -0
- package/src/runtime/api/assistant-runtime.ts +0 -62
- package/src/runtime/api/bindings.ts +1 -6
- package/src/runtime/api/composer-runtime.ts +10 -9
- package/src/runtime/api/message-runtime.ts +1 -8
- package/src/runtime/api/thread-list-item-runtime.ts +28 -6
- package/src/runtime/api/thread-list-runtime.ts +10 -0
- package/src/runtime/api/thread-runtime.ts +12 -53
- package/src/runtime/base/base-composer-runtime-core.ts +85 -42
- package/src/runtime/base/base-thread-runtime-core.ts +21 -13
- package/src/runtime/interfaces/composer-runtime-core.ts +39 -7
- package/src/runtime/interfaces/thread-list-runtime-core.ts +3 -0
- package/src/runtime/interfaces/thread-runtime-core.ts +42 -9
- package/src/runtime/utils/chat-model-adapter.ts +0 -5
- package/src/runtime/utils/external-store-message.ts +0 -8
- package/src/runtimes/assistant-transport/utils.ts +0 -28
- package/src/runtimes/external-store/external-store-thread-runtime-core.ts +2 -6
- package/src/runtimes/local/local-thread-runtime-core.ts +2 -7
- package/src/runtimes/readonly/ReadonlyThreadRuntimeCore.ts +0 -4
- package/src/runtimes/remote-thread-list/empty-thread-core.ts +0 -4
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +54 -1
- package/src/runtimes/remote-thread-list/types.ts +6 -1
- package/src/store/clients/thread-message-client.ts +0 -1
- package/src/store/runtime-clients/composer-runtime-client.ts +5 -9
- package/src/store/runtime-clients/thread-list-runtime-client.ts +3 -0
- package/src/store/runtime-clients/thread-runtime-client.ts +0 -1
- package/src/store/scopes/composer.ts +11 -0
- package/src/store/scopes/message.ts +1 -6
- package/src/store/scopes/thread-list-item.ts +10 -0
- package/src/store/scopes/thread.ts +17 -5
- package/src/store/scopes/threads.ts +3 -0
- package/src/subscribable/subscribable.ts +10 -7
- package/src/tests/RemoteThreadListThreadListRuntimeCore-loadMore.test.ts +448 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-reload.test.ts +6 -1
- package/src/tests/base-composer-runtime-core-addAttachment.test.ts +63 -0
- package/src/tests/groupParts.test.ts +114 -0
package/src/index.ts
CHANGED
|
@@ -32,7 +32,9 @@ export type {
|
|
|
32
32
|
export type {
|
|
33
33
|
Attachment,
|
|
34
34
|
PendingAttachment,
|
|
35
|
+
PendingAttachmentStatus,
|
|
35
36
|
CompleteAttachment,
|
|
37
|
+
CompleteAttachmentStatus,
|
|
36
38
|
AttachmentStatus,
|
|
37
39
|
CreateAttachment,
|
|
38
40
|
} from "./types/attachment";
|
|
@@ -143,7 +145,11 @@ export type {
|
|
|
143
145
|
|
|
144
146
|
// Runtime Core Interface Types
|
|
145
147
|
export type {
|
|
148
|
+
AttachmentAddErrorEvent,
|
|
149
|
+
AttachmentAddErrorReason,
|
|
146
150
|
ComposerRuntimeCore,
|
|
151
|
+
ComposerRuntimeEventCallback,
|
|
152
|
+
ComposerRuntimeEventPayload,
|
|
147
153
|
ComposerRuntimeEventType,
|
|
148
154
|
DictationState,
|
|
149
155
|
EditComposerRuntimeCore,
|
|
@@ -160,6 +166,8 @@ export type {
|
|
|
160
166
|
SpeechState,
|
|
161
167
|
VoiceSessionState,
|
|
162
168
|
SubmittedFeedback,
|
|
169
|
+
ThreadRuntimeEventCallback,
|
|
170
|
+
ThreadRuntimeEventPayload,
|
|
163
171
|
ThreadRuntimeEventType,
|
|
164
172
|
StartRunConfig,
|
|
165
173
|
ResumeRunConfig,
|
|
@@ -191,6 +199,8 @@ export type {
|
|
|
191
199
|
} from "./runtime/api/thread-list-runtime";
|
|
192
200
|
|
|
193
201
|
export type {
|
|
202
|
+
ThreadListItemEventCallback,
|
|
203
|
+
ThreadListItemEventPayload,
|
|
194
204
|
ThreadListItemEventType,
|
|
195
205
|
ThreadListItemRuntime,
|
|
196
206
|
} from "./runtime/api/thread-list-item-runtime";
|
|
@@ -234,7 +244,6 @@ export type { ThreadMessageLike } from "./runtime/utils/thread-message-like";
|
|
|
234
244
|
|
|
235
245
|
// External Store Message Utilities
|
|
236
246
|
export {
|
|
237
|
-
getExternalStoreMessage,
|
|
238
247
|
getExternalStoreMessages,
|
|
239
248
|
bindExternalStoreMessage,
|
|
240
249
|
} from "./runtime/utils/external-store-message";
|
|
@@ -261,13 +270,10 @@ export type {
|
|
|
261
270
|
RemoteThreadInitializeResponse,
|
|
262
271
|
RemoteThreadMetadata,
|
|
263
272
|
RemoteThreadListResponse,
|
|
273
|
+
RemoteThreadListPageOptions,
|
|
264
274
|
} from "./runtimes/remote-thread-list/types";
|
|
265
275
|
|
|
266
276
|
export { InMemoryThreadListAdapter } from "./runtimes/remote-thread-list/adapter/in-memory";
|
|
267
277
|
|
|
268
278
|
// Assistant Transport Utilities
|
|
269
|
-
export {
|
|
270
|
-
toAISDKTools,
|
|
271
|
-
getEnabledTools,
|
|
272
|
-
createRequestHeaders,
|
|
273
|
-
} from "./runtimes/assistant-transport/utils";
|
|
279
|
+
export { createRequestHeaders } from "./runtimes/assistant-transport/utils";
|
package/src/react/index.ts
CHANGED
|
@@ -163,7 +163,9 @@ export {
|
|
|
163
163
|
MessagePrimitivePartByIndex,
|
|
164
164
|
defaultComponents as messagePartsDefaultComponents,
|
|
165
165
|
type EnrichedPartState,
|
|
166
|
+
type PartState,
|
|
166
167
|
} from "./primitives/message/MessageParts";
|
|
168
|
+
export { MessagePrimitiveGroupedParts } from "./primitives/message/MessageGroupedParts";
|
|
167
169
|
export { MessagePrimitiveQuote } from "./primitives/message/MessageQuote";
|
|
168
170
|
export {
|
|
169
171
|
MessagePrimitiveAttachments,
|
|
@@ -233,6 +235,7 @@ export { useThreadListItemDelete } from "./primitive-hooks/useThreadListItemDele
|
|
|
233
235
|
export { useThreadListItemUnarchive } from "./primitive-hooks/useThreadListItemUnarchive";
|
|
234
236
|
export { useThreadListItemTrigger } from "./primitive-hooks/useThreadListItemTrigger";
|
|
235
237
|
export { useThreadListNew } from "./primitive-hooks/useThreadListNew";
|
|
238
|
+
export { useThreadListLoadMore } from "./primitive-hooks/useThreadListLoadMore";
|
|
236
239
|
export { useEditComposerCancel } from "./primitive-hooks/useEditComposerCancel";
|
|
237
240
|
export { useEditComposerSend } from "./primitive-hooks/useEditComposerSend";
|
|
238
241
|
export { useMessageError } from "./primitive-hooks/useMessageError";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useCallback } from "react";
|
|
2
|
+
import { useAui, useAuiState } from "@assistant-ui/store";
|
|
3
|
+
|
|
4
|
+
export const useThreadListLoadMore = () => {
|
|
5
|
+
const aui = useAui();
|
|
6
|
+
const disabled = useAuiState(
|
|
7
|
+
(s) => !s.threads.hasMore || s.threads.isLoading || s.threads.isLoadingMore,
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
const loadMore = useCallback(() => {
|
|
11
|
+
aui.threads().loadMore();
|
|
12
|
+
}, [aui]);
|
|
13
|
+
|
|
14
|
+
return { loadMore, disabled };
|
|
15
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Fragment, type FC, type ReactNode, useMemo } from "react";
|
|
4
|
+
import { useAuiState } from "@assistant-ui/store";
|
|
5
|
+
import { useShallow } from "zustand/shallow";
|
|
6
|
+
import type { PartState } from "../../../store/scopes/part";
|
|
7
|
+
import type {
|
|
8
|
+
MessagePartStatus,
|
|
9
|
+
ToolCallMessagePartStatus,
|
|
10
|
+
} from "../../../types/message";
|
|
11
|
+
import {
|
|
12
|
+
buildGroupTree,
|
|
13
|
+
type GroupKey,
|
|
14
|
+
type GroupNode,
|
|
15
|
+
normalizeGroupKey,
|
|
16
|
+
} from "../../utils/groupParts";
|
|
17
|
+
import { MessagePartChildren, type EnrichedPartState } from "./MessageParts";
|
|
18
|
+
|
|
19
|
+
export namespace MessagePrimitiveGroupedParts {
|
|
20
|
+
/**
|
|
21
|
+
* A coalesced group of adjacent parts. Surfaced through the same
|
|
22
|
+
* `{ part }` channel as a leaf {@link EnrichedPartState} so consumers
|
|
23
|
+
* dispatch on a single `switch (part.type)`. `type` is the group key
|
|
24
|
+
* (always `"group-…"`); `status` mirrors the last contained part.
|
|
25
|
+
*/
|
|
26
|
+
export type GroupPart<TKey extends `group-${string}` = `group-${string}`> = {
|
|
27
|
+
readonly type: TKey;
|
|
28
|
+
readonly status: MessagePartStatus | ToolCallMessagePartStatus;
|
|
29
|
+
readonly indices: readonly number[];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type RenderInfo<TKey extends `group-${string}` = `group-${string}`> = {
|
|
33
|
+
/**
|
|
34
|
+
* Either a coalesced group ({@link GroupPart}, identified by a
|
|
35
|
+
* `group-…` `type`) or a single enriched part. Use one switch over
|
|
36
|
+
* `part.type` to handle both.
|
|
37
|
+
*/
|
|
38
|
+
readonly part: GroupPart<TKey> | EnrichedPartState;
|
|
39
|
+
/**
|
|
40
|
+
* For group nodes: the recursively-rendered subtree (subgroups +
|
|
41
|
+
* leaf parts). For leaf parts: a sentinel that throws when rendered
|
|
42
|
+
* — accidental fall-through (`default: return children;`) errors
|
|
43
|
+
* loudly instead of silently rendering nothing.
|
|
44
|
+
*/
|
|
45
|
+
readonly children: ReactNode;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export type Props<TKey extends `group-${string}` = `group-${string}`> = {
|
|
49
|
+
/**
|
|
50
|
+
* Maps each part to its group key path. Adjacent parts that share a
|
|
51
|
+
* prefix coalesce up to that prefix. Return `null`, `undefined`, or
|
|
52
|
+
* `[]` to leave a part ungrouped — it will be rendered as a leaf
|
|
53
|
+
* through `children` with `part` set to its {@link EnrichedPartState}.
|
|
54
|
+
*
|
|
55
|
+
* Keys must start with `"group-"` so the renderer's
|
|
56
|
+
* `switch (part.type)` can distinguish groups from real part types.
|
|
57
|
+
*
|
|
58
|
+
* For best performance, pass a stable reference (module-level
|
|
59
|
+
* constant or `useCallback`).
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const groupBy = (part) =>
|
|
64
|
+
* part.type === "reasoning" ? ["group-thought", "group-reasoning"] :
|
|
65
|
+
* part.type === "tool-call" ? ["group-thought", "group-tool"] :
|
|
66
|
+
* null;
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
readonly groupBy: (
|
|
70
|
+
part: PartState,
|
|
71
|
+
index: number,
|
|
72
|
+
parts: readonly PartState[],
|
|
73
|
+
) => GroupKey<TKey>;
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Render function called once per group node and once per leaf part.
|
|
77
|
+
* Switch on `part.type`: `"group-…"` cases wrap `children`; real
|
|
78
|
+
* part types (`"text"`, `"tool-call"`, …) render the part directly.
|
|
79
|
+
*
|
|
80
|
+
* Leaf parts receive the same {@link EnrichedPartState} that
|
|
81
|
+
* `<MessagePrimitive.Parts>` would produce (`toolUI`, `addResult`,
|
|
82
|
+
* `resume`, `dataRendererUI`).
|
|
83
|
+
*/
|
|
84
|
+
readonly children: (info: RenderInfo<TKey>) => ReactNode;
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const COMPLETE_STATUS: MessagePartStatus = Object.freeze({ type: "complete" });
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* `children` placeholder passed for leaf-part renders. Leaf parts have no
|
|
92
|
+
* inner subtree; rendering this sentinel signals the consumer wrote
|
|
93
|
+
* `default: return children;` and accidentally fell through for a part —
|
|
94
|
+
* surface the bug loudly instead of silently rendering nothing.
|
|
95
|
+
*/
|
|
96
|
+
const PartChildrenSentinel: FC = () => {
|
|
97
|
+
throw new Error(
|
|
98
|
+
"MessagePrimitive.GroupedParts: rendered `children` under a leaf " +
|
|
99
|
+
"part. `children` is only meaningful for `group-…` cases — add a " +
|
|
100
|
+
"matching case for the part type or return `null` to skip it.",
|
|
101
|
+
);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const renderNode = <TKey extends `group-${string}`>(
|
|
105
|
+
node: GroupNode,
|
|
106
|
+
parts: readonly PartState[],
|
|
107
|
+
render: (info: MessagePrimitiveGroupedParts.RenderInfo<TKey>) => ReactNode,
|
|
108
|
+
): ReactNode => {
|
|
109
|
+
if (node.type === "part") {
|
|
110
|
+
return (
|
|
111
|
+
<MessagePartChildren key={node.nodeKey} index={node.index}>
|
|
112
|
+
{({ part }) => render({ part, children: <PartChildrenSentinel /> })}
|
|
113
|
+
</MessagePartChildren>
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const status = parts[node.indices.at(-1)!]?.status ?? COMPLETE_STATUS;
|
|
118
|
+
const groupPart: MessagePrimitiveGroupedParts.GroupPart<TKey> = {
|
|
119
|
+
type: node.key as TKey,
|
|
120
|
+
status,
|
|
121
|
+
indices: node.indices,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<Fragment key={node.nodeKey}>
|
|
126
|
+
{render({
|
|
127
|
+
part: groupPart,
|
|
128
|
+
children: (
|
|
129
|
+
<>{node.children.map((child) => renderNode(child, parts, render))}</>
|
|
130
|
+
),
|
|
131
|
+
})}
|
|
132
|
+
</Fragment>
|
|
133
|
+
);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Groups adjacent message parts into a tree of coalesced runs and
|
|
138
|
+
* renders each node — group or part — through a single `children`
|
|
139
|
+
* function.
|
|
140
|
+
*
|
|
141
|
+
* The render function receives `{ part, children }` where `part.type`
|
|
142
|
+
* is either a `"group-…"` literal (for a group, `children` is the
|
|
143
|
+
* recursively-rendered subtree) or a real part type (`"text"`,
|
|
144
|
+
* `"tool-call"`, …) for a leaf (`children` is a sentinel that throws
|
|
145
|
+
* if rendered — use `part.type` to distinguish).
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```tsx
|
|
149
|
+
* <MessagePrimitive.GroupedParts
|
|
150
|
+
* groupBy={(part) =>
|
|
151
|
+
* part.type === "reasoning" ? ["group-thought", "group-reasoning"] :
|
|
152
|
+
* part.type === "tool-call" ? ["group-thought", "group-tool"] :
|
|
153
|
+
* null
|
|
154
|
+
* }
|
|
155
|
+
* >
|
|
156
|
+
* {({ part, children }) => {
|
|
157
|
+
* switch (part.type) {
|
|
158
|
+
* case "group-thought": return <Thought>{children}</Thought>;
|
|
159
|
+
* case "group-reasoning": return <Reasoning>{children}</Reasoning>;
|
|
160
|
+
* case "group-tool": return <ToolStack>{children}</ToolStack>;
|
|
161
|
+
* case "text": return <MarkdownText />;
|
|
162
|
+
* case "tool-call": return part.toolUI ?? <ToolFallback {...part} />;
|
|
163
|
+
* default: return null;
|
|
164
|
+
* }
|
|
165
|
+
* }}
|
|
166
|
+
* </MessagePrimitive.GroupedParts>
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export const MessagePrimitiveGroupedParts = <TKey extends `group-${string}`>({
|
|
170
|
+
groupBy,
|
|
171
|
+
children,
|
|
172
|
+
}: MessagePrimitiveGroupedParts.Props<TKey>): ReactNode => {
|
|
173
|
+
const parts = useAuiState(useShallow((s) => s.message.parts));
|
|
174
|
+
|
|
175
|
+
const tree = useMemo(
|
|
176
|
+
() =>
|
|
177
|
+
buildGroupTree(
|
|
178
|
+
parts.map((part, i) => normalizeGroupKey(groupBy(part, i, parts))),
|
|
179
|
+
),
|
|
180
|
+
[parts, groupBy],
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
return <>{tree.map((node) => renderNode(node, parts, children))}</>;
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
MessagePrimitiveGroupedParts.displayName = "MessagePrimitive.GroupedParts";
|
|
@@ -208,7 +208,7 @@ export namespace MessagePrimitiveParts {
|
|
|
208
208
|
* @param endIndex - Index of the last tool call in the group
|
|
209
209
|
* @param children - Rendered tool call components to display within the group
|
|
210
210
|
*
|
|
211
|
-
* @deprecated
|
|
211
|
+
* @deprecated Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
|
|
212
212
|
*/
|
|
213
213
|
ToolGroup?: ComponentType<
|
|
214
214
|
PropsWithChildren<{ startIndex: number; endIndex: number }>
|
|
@@ -220,6 +220,8 @@ export namespace MessagePrimitiveParts {
|
|
|
220
220
|
* @param startIndex - Index of the first reasoning part in the group
|
|
221
221
|
* @param endIndex - Index of the last reasoning part in the group
|
|
222
222
|
* @param children - Rendered reasoning part components
|
|
223
|
+
*
|
|
224
|
+
* @deprecated Use `<MessagePrimitive.GroupedParts>` with a custom `groupBy` instead.
|
|
223
225
|
*/
|
|
224
226
|
ReasoningGroup?: ReasoningGroupComponent;
|
|
225
227
|
|
|
@@ -234,6 +236,11 @@ export namespace MessagePrimitiveParts {
|
|
|
234
236
|
* `ToolGroup` components cannot be used alongside it.
|
|
235
237
|
*/
|
|
236
238
|
type ChainOfThoughtComponents = BaseComponents & {
|
|
239
|
+
/**
|
|
240
|
+
* @deprecated Use `<MessagePrimitive.GroupedParts>` with a `groupBy`
|
|
241
|
+
* that returns `["group-thought", ...]` for reasoning and tool-call
|
|
242
|
+
* parts. See `@assistant-ui/ui` for a worked example.
|
|
243
|
+
*/
|
|
237
244
|
ChainOfThought: ComponentType;
|
|
238
245
|
|
|
239
246
|
Reasoning?: never;
|
|
@@ -604,70 +611,88 @@ const EMPTY_RUNNING_TEXT_PART: Extract<EnrichedPartState, { type: "text" }> =
|
|
|
604
611
|
status: RUNNING_STATUS,
|
|
605
612
|
});
|
|
606
613
|
|
|
614
|
+
/**
|
|
615
|
+
* @internal
|
|
616
|
+
* Renders a single part by index, calling `children` with the
|
|
617
|
+
* {@link EnrichedPartState} (tool/data UI enrichments + addResult/resume
|
|
618
|
+
* for tool calls). Shared between `<MessagePrimitive.Parts>` and
|
|
619
|
+
* `<MessagePrimitive.GroupedParts>`. Returns whatever `children`
|
|
620
|
+
* returns — callers decide how to handle a `null` return.
|
|
621
|
+
*/
|
|
622
|
+
export const MessagePartChildren: FC<{
|
|
623
|
+
index: number;
|
|
624
|
+
children: (value: { part: EnrichedPartState }) => ReactNode;
|
|
625
|
+
}> = ({ index, children }) => {
|
|
626
|
+
const aui = useAui();
|
|
627
|
+
// Subscribed (not snapshotted like `tools`) so fallbacks registered
|
|
628
|
+
// after the first render trigger a re-render and `hasUI` re-evaluates.
|
|
629
|
+
const dataRenderers = useAuiState((s) => s.dataRenderers);
|
|
630
|
+
|
|
631
|
+
return (
|
|
632
|
+
<PartByIndexProvider index={index}>
|
|
633
|
+
<RenderChildrenWithAccessor
|
|
634
|
+
getItemState={(aui) => aui.message().part({ index }).getState()}
|
|
635
|
+
>
|
|
636
|
+
{(getItem) =>
|
|
637
|
+
children({
|
|
638
|
+
get part() {
|
|
639
|
+
const state = getItem();
|
|
640
|
+
if (state.type === "tool-call") {
|
|
641
|
+
const entry = aui.tools().getState().tools[state.toolName];
|
|
642
|
+
const hasUI = Array.isArray(entry) ? !!entry[0] : !!entry;
|
|
643
|
+
const partMethods = aui.message().part({ index });
|
|
644
|
+
return {
|
|
645
|
+
...state,
|
|
646
|
+
toolUI: hasUI ? <RegisteredToolUI /> : null,
|
|
647
|
+
addResult: partMethods.addToolResult,
|
|
648
|
+
resume: partMethods.resumeToolCall,
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
if (state.type === "data") {
|
|
652
|
+
const hasUI =
|
|
653
|
+
getDataRenderer(dataRenderers, state.name, undefined) !==
|
|
654
|
+
undefined;
|
|
655
|
+
return {
|
|
656
|
+
...state,
|
|
657
|
+
dataRendererUI: hasUI ? <RegisteredDataRendererUI /> : null,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
return state;
|
|
661
|
+
},
|
|
662
|
+
})
|
|
663
|
+
}
|
|
664
|
+
</RenderChildrenWithAccessor>
|
|
665
|
+
</PartByIndexProvider>
|
|
666
|
+
);
|
|
667
|
+
};
|
|
668
|
+
|
|
607
669
|
const MessagePrimitivePartsInner: FC<{
|
|
608
670
|
children: (value: { part: EnrichedPartState }) => ReactNode;
|
|
609
671
|
}> = ({ children }) => {
|
|
610
|
-
const aui = useAui();
|
|
611
672
|
const contentLength = useAuiState((s) => s.message.parts.length);
|
|
612
673
|
const isRunning = useAuiState(
|
|
613
674
|
(s) => (s.message.status?.type ?? "complete") === "running",
|
|
614
675
|
);
|
|
615
676
|
const isEmptyRunning = contentLength === 0 && isRunning;
|
|
616
|
-
// Subscribed (not snapshotted like `tools`) so fallbacks registered after
|
|
617
|
-
// the first render trigger a re-render and `hasUI` re-evaluates.
|
|
618
|
-
const dataRenderers = useAuiState((s) => s.dataRenderers);
|
|
619
|
-
|
|
620
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: aui accessors are stable refs
|
|
621
|
-
return useMemo(() => {
|
|
622
|
-
if (contentLength === 0) {
|
|
623
|
-
if (!isEmptyRunning) return null;
|
|
624
677
|
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
678
|
+
if (contentLength === 0) {
|
|
679
|
+
if (!isEmptyRunning) return null;
|
|
680
|
+
return (
|
|
681
|
+
<TextMessagePartProvider text="" isRunning>
|
|
682
|
+
{children({ part: EMPTY_RUNNING_TEXT_PART })}
|
|
683
|
+
</TextMessagePartProvider>
|
|
684
|
+
);
|
|
685
|
+
}
|
|
631
686
|
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
if (state.type === "tool-call") {
|
|
642
|
-
const entry = aui.tools().getState().tools[state.toolName];
|
|
643
|
-
const hasUI = Array.isArray(entry) ? !!entry[0] : !!entry;
|
|
644
|
-
const partMethods = aui.message().part({ index });
|
|
645
|
-
return {
|
|
646
|
-
...state,
|
|
647
|
-
toolUI: hasUI ? <RegisteredToolUI /> : null,
|
|
648
|
-
addResult: partMethods.addToolResult,
|
|
649
|
-
resume: partMethods.resumeToolCall,
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
if (state.type === "data") {
|
|
653
|
-
const hasUI =
|
|
654
|
-
getDataRenderer(dataRenderers, state.name, undefined) !==
|
|
655
|
-
undefined;
|
|
656
|
-
return {
|
|
657
|
-
...state,
|
|
658
|
-
dataRendererUI: hasUI ? <RegisteredDataRendererUI /> : null,
|
|
659
|
-
};
|
|
660
|
-
}
|
|
661
|
-
return state;
|
|
662
|
-
},
|
|
663
|
-
});
|
|
664
|
-
if (result !== null) return result;
|
|
665
|
-
return <DefaultPartFallback />;
|
|
666
|
-
}}
|
|
667
|
-
</RenderChildrenWithAccessor>
|
|
668
|
-
</PartByIndexProvider>
|
|
669
|
-
));
|
|
670
|
-
}, [contentLength, children, isEmptyRunning, dataRenderers]);
|
|
687
|
+
return (
|
|
688
|
+
<>
|
|
689
|
+
{Array.from({ length: contentLength }, (_, index) => (
|
|
690
|
+
<MessagePartChildren key={index} index={index}>
|
|
691
|
+
{(value) => children(value) ?? <DefaultPartFallback />}
|
|
692
|
+
</MessagePartChildren>
|
|
693
|
+
))}
|
|
694
|
+
</>
|
|
695
|
+
);
|
|
671
696
|
};
|
|
672
697
|
|
|
673
698
|
/**
|
|
@@ -94,7 +94,8 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
// Rendered
|
|
97
|
+
// Rendered as a child of the user's Provider so the runtime hook can
|
|
98
|
+
// read context the Provider injects (e.g. RuntimeAdapterProvider).
|
|
98
99
|
private _RuntimeBinder: FC<PropsWithChildren<{ threadId: string }>> = ({
|
|
99
100
|
threadId,
|
|
100
101
|
children,
|
|
@@ -172,7 +173,7 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
|
|
|
172
173
|
console.warn(
|
|
173
174
|
"RemoteThreadListAdapter.unstable_Provider did not render its `children` synchronously. " +
|
|
174
175
|
"Render `children` on first commit; deferring them behind a loading state, Suspense boundary, " +
|
|
175
|
-
"or `useEffect` gate
|
|
176
|
+
"or `useEffect` gate strands the runtime binder and leaves the thread without context.",
|
|
176
177
|
);
|
|
177
178
|
}
|
|
178
179
|
}, 100);
|
|
@@ -183,11 +184,11 @@ export class RemoteThreadListHookInstanceManager extends BaseSubscribable {
|
|
|
183
184
|
|
|
184
185
|
return (
|
|
185
186
|
<ThreadListItemRuntimeProvider runtime={runtime}>
|
|
186
|
-
<
|
|
187
|
-
<
|
|
187
|
+
<Provider>
|
|
188
|
+
<this._RuntimeBinder threadId={threadId}>
|
|
188
189
|
<ProviderRenderDetector detectorRef={detectorRef} />
|
|
189
|
-
</
|
|
190
|
-
</
|
|
190
|
+
</this._RuntimeBinder>
|
|
191
|
+
</Provider>
|
|
191
192
|
</ThreadListItemRuntimeProvider>
|
|
192
193
|
);
|
|
193
194
|
});
|