@assistant-ui/react 0.11.39 → 0.11.43
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/client/AssistantClient.d.ts.map +1 -1
- package/dist/client/AssistantClient.js.map +1 -1
- package/dist/client/ModelContext.d.ts +1 -1
- package/dist/client/ModelContext.d.ts.map +1 -1
- package/dist/client/ModelContext.js.map +1 -1
- package/dist/client/ModelContextClient.d.ts +1 -1
- package/dist/client/ThreadMessageClient.d.ts +1 -0
- package/dist/client/ThreadMessageClient.d.ts.map +1 -1
- package/dist/client/ThreadMessageClient.js +3 -1
- package/dist/client/ThreadMessageClient.js.map +1 -1
- package/dist/client/types/Message.d.ts +2 -0
- package/dist/client/types/Message.d.ts.map +1 -1
- package/dist/client/types/ModelContext.d.ts +1 -1
- package/dist/client/types/ModelContext.d.ts.map +1 -1
- package/dist/client/types/Tools.d.ts +1 -2
- package/dist/client/types/Tools.d.ts.map +1 -1
- package/dist/context/providers/ThreadViewportProvider.d.ts +5 -1
- package/dist/context/providers/ThreadViewportProvider.d.ts.map +1 -1
- package/dist/context/providers/ThreadViewportProvider.js +17 -6
- package/dist/context/providers/ThreadViewportProvider.js.map +1 -1
- package/dist/context/react/AssistantApiContext.d.ts +1 -1
- package/dist/context/react/AssistantApiContext.d.ts.map +1 -1
- package/dist/context/react/AssistantApiContext.js +1 -2
- package/dist/context/react/AssistantApiContext.js.map +1 -1
- package/dist/context/stores/ThreadViewport.d.ts +33 -3
- package/dist/context/stores/ThreadViewport.d.ts.map +1 -1
- package/dist/context/stores/ThreadViewport.js +67 -5
- package/dist/context/stores/ThreadViewport.js.map +1 -1
- package/dist/devtools/DevToolsHooks.d.ts +1 -1
- package/dist/devtools/DevToolsHooks.d.ts.map +1 -1
- package/dist/devtools/DevToolsHooks.js.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.d.ts.map +1 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js +2 -1
- package/dist/legacy-runtime/AssistantRuntimeProvider.js.map +1 -1
- package/dist/legacy-runtime/client/ComposerRuntimeClient.d.ts +3 -3
- package/dist/legacy-runtime/client/ComposerRuntimeClient.d.ts.map +1 -1
- package/dist/legacy-runtime/client/ComposerRuntimeClient.js.map +1 -1
- package/dist/legacy-runtime/client/EventManagerRuntimeClient.d.ts +1 -1
- package/dist/legacy-runtime/client/ThreadRuntimeClient.js.map +1 -1
- package/dist/legacy-runtime/runtime/MessageRuntime.d.ts +3 -0
- package/dist/legacy-runtime/runtime/MessageRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime/MessageRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime/RuntimeBindings.d.ts +2 -0
- package/dist/legacy-runtime/runtime/RuntimeBindings.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts +1 -0
- package/dist/legacy-runtime/runtime/ThreadRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime/ThreadRuntime.js +6 -3
- package/dist/legacy-runtime/runtime/ThreadRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/runManager.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts +8 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/types.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js +23 -11
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js +5 -5
- package/dist/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.js.map +1 -1
- package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts +1 -0
- package/dist/legacy-runtime/runtime-cores/core/BaseThreadRuntimeCore.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts +1 -0
- package/dist/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts +2 -0
- package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts +2 -0
- package/dist/legacy-runtime/runtime-cores/remote-thread-list/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.d.ts +1 -0
- package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.d.ts.map +1 -1
- package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js +2 -1
- package/dist/legacy-runtime/runtime-cores/utils/MessageRepository.js.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts +2 -2
- package/dist/primitives/composer/ComposerAttachmentDropzone.d.ts.map +1 -1
- package/dist/primitives/composer/ComposerAttachmentDropzone.js +31 -11
- package/dist/primitives/composer/ComposerAttachmentDropzone.js.map +1 -1
- package/dist/primitives/composer/index.d.ts +1 -0
- package/dist/primitives/composer/index.d.ts.map +1 -1
- package/dist/primitives/composer/index.js +2 -0
- package/dist/primitives/composer/index.js.map +1 -1
- package/dist/primitives/message/MessageRoot.d.ts +3 -0
- package/dist/primitives/message/MessageRoot.d.ts.map +1 -1
- package/dist/primitives/message/MessageRoot.js +24 -2
- package/dist/primitives/message/MessageRoot.js.map +1 -1
- package/dist/primitives/messagePart/useMessagePartData.d.ts.map +1 -1
- package/dist/primitives/messagePart/useMessagePartData.js.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.d.ts +7 -2
- package/dist/primitives/thread/ThreadScrollToBottom.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadScrollToBottom.js +7 -4
- package/dist/primitives/thread/ThreadScrollToBottom.js.map +1 -1
- package/dist/primitives/thread/ThreadViewport.d.ts +17 -3
- package/dist/primitives/thread/ThreadViewport.d.ts.map +1 -1
- package/dist/primitives/thread/ThreadViewport.js +19 -5
- package/dist/primitives/thread/ThreadViewport.js.map +1 -1
- package/dist/primitives/thread/ThreadViewportFooter.d.ts +31 -0
- package/dist/primitives/thread/ThreadViewportFooter.d.ts.map +1 -0
- package/dist/primitives/thread/ThreadViewportFooter.js +27 -0
- package/dist/primitives/thread/ThreadViewportFooter.js.map +1 -0
- package/dist/primitives/thread/ThreadViewportSlack.d.ts +20 -0
- package/dist/primitives/thread/ThreadViewportSlack.d.ts.map +1 -0
- package/dist/primitives/thread/ThreadViewportSlack.js +77 -0
- package/dist/primitives/thread/ThreadViewportSlack.js.map +1 -0
- package/dist/primitives/thread/index.d.ts +3 -0
- package/dist/primitives/thread/index.d.ts.map +1 -1
- package/dist/primitives/thread/index.js +7 -1
- package/dist/primitives/thread/index.js.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts +6 -0
- package/dist/primitives/thread/useThreadViewportAutoScroll.d.ts.map +1 -1
- package/dist/primitives/thread/useThreadViewportAutoScroll.js +17 -8
- package/dist/primitives/thread/useThreadViewportAutoScroll.js.map +1 -1
- package/dist/tests/setup.js +14 -14
- package/dist/tests/setup.js.map +1 -1
- package/dist/utils/hooks/useOnScrollToBottom.d.ts +3 -1
- package/dist/utils/hooks/useOnScrollToBottom.d.ts.map +1 -1
- package/dist/utils/hooks/useOnScrollToBottom.js.map +1 -1
- package/dist/utils/hooks/useSizeHandle.d.ts +11 -0
- package/dist/utils/hooks/useSizeHandle.d.ts.map +1 -0
- package/dist/utils/hooks/useSizeHandle.js +30 -0
- package/dist/utils/hooks/useSizeHandle.js.map +1 -0
- package/dist/utils/tap-store/derived-scopes.d.ts +2 -1
- package/dist/utils/tap-store/derived-scopes.d.ts.map +1 -1
- package/dist/utils/tap-store/derived-scopes.js.map +1 -1
- package/dist/utils/tap-store/store.d.ts +2 -1
- package/dist/utils/tap-store/store.d.ts.map +1 -1
- package/dist/utils/tap-store/store.js.map +1 -1
- package/package.json +6 -6
- package/src/client/AssistantClient.ts +1 -1
- package/src/client/ModelContext.ts +1 -1
- package/src/client/ThreadMessageClient.tsx +4 -1
- package/src/client/types/Message.ts +3 -0
- package/src/client/types/ModelContext.ts +1 -1
- package/src/client/types/Tools.ts +1 -2
- package/src/context/providers/ThreadViewportProvider.tsx +27 -5
- package/src/context/react/AssistantApiContext.tsx +2 -5
- package/src/context/stores/ThreadViewport.tsx +125 -7
- package/src/devtools/DevToolsHooks.ts +1 -1
- package/src/legacy-runtime/AssistantRuntimeProvider.tsx +6 -1
- package/src/legacy-runtime/client/ComposerRuntimeClient.ts +3 -3
- package/src/legacy-runtime/client/ThreadRuntimeClient.ts +2 -2
- package/src/legacy-runtime/runtime/MessageRuntime.ts +2 -0
- package/src/legacy-runtime/runtime/RuntimeBindings.ts +2 -0
- package/src/legacy-runtime/runtime/ThreadRuntime.ts +6 -3
- package/src/legacy-runtime/runtime-cores/assistant-transport/runManager.ts +2 -2
- package/src/legacy-runtime/runtime-cores/assistant-transport/types.ts +8 -1
- package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.tsx +26 -11
- package/src/legacy-runtime/runtime-cores/assistant-transport/useToolInvocations.ts +5 -5
- package/src/legacy-runtime/runtime-cores/core/ThreadRuntimeCore.tsx +1 -0
- package/src/legacy-runtime/runtime-cores/utils/MessageRepository.tsx +1 -0
- package/src/primitives/composer/ComposerAttachmentDropzone.tsx +35 -12
- package/src/primitives/composer/index.ts +1 -0
- package/src/primitives/message/MessageRoot.tsx +45 -2
- package/src/primitives/messagePart/useMessagePartData.tsx +0 -1
- package/src/primitives/thread/ThreadScrollToBottom.tsx +12 -3
- package/src/primitives/thread/ThreadViewport.tsx +35 -9
- package/src/primitives/thread/ThreadViewportFooter.tsx +57 -0
- package/src/primitives/thread/ThreadViewportSlack.tsx +109 -0
- package/src/primitives/thread/index.ts +3 -0
- package/src/primitives/thread/useThreadViewportAutoScroll.tsx +24 -12
- package/src/utils/hooks/useOnScrollToBottom.tsx +3 -1
- package/src/utils/hooks/useSizeHandle.ts +43 -0
- package/src/utils/tap-store/derived-scopes.ts +2 -1
- package/src/utils/tap-store/store.ts +1 -1
|
@@ -447,6 +447,7 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
|
|
|
447
447
|
return {
|
|
448
448
|
message,
|
|
449
449
|
parentId: messages[idx - 1]?.id ?? null,
|
|
450
|
+
index: idx,
|
|
450
451
|
};
|
|
451
452
|
},
|
|
452
453
|
);
|
|
@@ -468,19 +469,20 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
|
|
|
468
469
|
private _getMessageRuntime(
|
|
469
470
|
path: MessageRuntimePath,
|
|
470
471
|
callback: () =>
|
|
471
|
-
| { parentId: string | null; message: ThreadMessage }
|
|
472
|
+
| { parentId: string | null; message: ThreadMessage; index: number }
|
|
472
473
|
| undefined,
|
|
473
474
|
) {
|
|
474
475
|
return new MessageRuntimeImpl(
|
|
475
476
|
new ShallowMemoizeSubject({
|
|
476
477
|
path,
|
|
477
478
|
getState: () => {
|
|
478
|
-
const { message, parentId } = callback() ?? {};
|
|
479
|
+
const { message, parentId, index } = callback() ?? {};
|
|
479
480
|
|
|
480
481
|
const { messages, speech: speechState } =
|
|
481
482
|
this._threadBinding.getState();
|
|
482
483
|
|
|
483
|
-
if (!message || parentId === undefined
|
|
484
|
+
if (!message || parentId === undefined || index === undefined)
|
|
485
|
+
return SKIP_UPDATE;
|
|
484
486
|
|
|
485
487
|
const thread = this._threadBinding.getState();
|
|
486
488
|
|
|
@@ -491,6 +493,7 @@ export class ThreadRuntimeImpl implements ThreadRuntime {
|
|
|
491
493
|
...message,
|
|
492
494
|
...{ [symbolInnerMessage]: (message as any)[symbolInnerMessage] },
|
|
493
495
|
|
|
496
|
+
index,
|
|
494
497
|
isLast: messages.at(-1)?.id === message.id,
|
|
495
498
|
parentId,
|
|
496
499
|
|
|
@@ -11,7 +11,7 @@ export function useRunManager(config: {
|
|
|
11
11
|
onRun: (signal: AbortSignal) => Promise<void>;
|
|
12
12
|
onFinish?: (() => void) | undefined;
|
|
13
13
|
onCancel?: (() => void) | undefined;
|
|
14
|
-
onError?: ((error: Error) => void) | undefined;
|
|
14
|
+
onError?: ((error: Error) => void | Promise<void>) | undefined;
|
|
15
15
|
}): RunManager {
|
|
16
16
|
const [isRunning, setIsRunning] = useState(false);
|
|
17
17
|
const stateRef = useRef({
|
|
@@ -37,7 +37,7 @@ export function useRunManager(config: {
|
|
|
37
37
|
if (ac.signal.aborted) {
|
|
38
38
|
onCancelRef.current?.();
|
|
39
39
|
} else {
|
|
40
|
-
onErrorRef.current?.(error as Error);
|
|
40
|
+
await onErrorRef.current?.(error as Error);
|
|
41
41
|
}
|
|
42
42
|
} finally {
|
|
43
43
|
onFinishRef.current?.();
|
|
@@ -97,10 +97,17 @@ export type AssistantTransportOptions<T> = {
|
|
|
97
97
|
commands: AssistantTransportCommand[];
|
|
98
98
|
updateState: (updater: (state: T) => T) => void;
|
|
99
99
|
},
|
|
100
|
-
) => void
|
|
100
|
+
) => void | Promise<void>;
|
|
101
|
+
/**
|
|
102
|
+
* Called when commands are cancelled.
|
|
103
|
+
*
|
|
104
|
+
* When an error occurs, queued commands are automatically cancelled after `onError` settles.
|
|
105
|
+
* In this case, the `error` parameter contains the error that caused the cancellation.
|
|
106
|
+
*/
|
|
101
107
|
onCancel?: (params: {
|
|
102
108
|
commands: AssistantTransportCommand[];
|
|
103
109
|
updateState: (updater: (state: T) => T) => void;
|
|
110
|
+
error?: Error;
|
|
104
111
|
}) => void;
|
|
105
112
|
adapters?: {
|
|
106
113
|
attachments?: AttachmentAdapter | undefined;
|
package/src/legacy-runtime/runtime-cores/assistant-transport/useAssistantTransportRuntime.tsx
CHANGED
|
@@ -174,6 +174,9 @@ const useAssistantTransportThreadRuntime = <T,>(
|
|
|
174
174
|
...commandQueue.state.inTransit,
|
|
175
175
|
...commandQueue.state.queued,
|
|
176
176
|
];
|
|
177
|
+
|
|
178
|
+
commandQueue.reset();
|
|
179
|
+
|
|
177
180
|
options.onCancel?.({
|
|
178
181
|
commands: cmds,
|
|
179
182
|
updateState: (updater) => {
|
|
@@ -181,19 +184,31 @@ const useAssistantTransportThreadRuntime = <T,>(
|
|
|
181
184
|
rerender((prev) => prev + 1);
|
|
182
185
|
},
|
|
183
186
|
});
|
|
187
|
+
},
|
|
188
|
+
onError: async (error) => {
|
|
189
|
+
const inTransitCmds = [...commandQueue.state.inTransit];
|
|
190
|
+
const queuedCmds = [...commandQueue.state.queued];
|
|
184
191
|
|
|
185
192
|
commandQueue.reset();
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
await options.onError?.(error as Error, {
|
|
196
|
+
commands: inTransitCmds,
|
|
197
|
+
updateState: (updater) => {
|
|
198
|
+
agentStateRef.current = updater(agentStateRef.current);
|
|
199
|
+
rerender((prev) => prev + 1);
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
} finally {
|
|
203
|
+
options.onCancel?.({
|
|
204
|
+
commands: queuedCmds,
|
|
205
|
+
updateState: (updater) => {
|
|
206
|
+
agentStateRef.current = updater(agentStateRef.current);
|
|
207
|
+
rerender((prev) => prev + 1);
|
|
208
|
+
},
|
|
209
|
+
error: error as Error,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
197
212
|
},
|
|
198
213
|
});
|
|
199
214
|
|
|
@@ -131,7 +131,7 @@ export function useToolInvocations({
|
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
const ignoredToolIds = useRef<Set<string>>(new Set());
|
|
134
|
-
const
|
|
134
|
+
const isInitialState = useRef(true);
|
|
135
135
|
|
|
136
136
|
useEffect(() => {
|
|
137
137
|
const processMessages = (
|
|
@@ -140,7 +140,7 @@ export function useToolInvocations({
|
|
|
140
140
|
messages.forEach((message) => {
|
|
141
141
|
message.content.forEach((content) => {
|
|
142
142
|
if (content.type === "tool-call") {
|
|
143
|
-
if (
|
|
143
|
+
if (isInitialState.current) {
|
|
144
144
|
ignoredToolIds.current.add(content.toolCallId);
|
|
145
145
|
} else {
|
|
146
146
|
if (ignoredToolIds.current.has(content.toolCallId)) {
|
|
@@ -225,8 +225,8 @@ export function useToolInvocations({
|
|
|
225
225
|
|
|
226
226
|
processMessages(state.messages);
|
|
227
227
|
|
|
228
|
-
if (
|
|
229
|
-
|
|
228
|
+
if (isInitialState.current) {
|
|
229
|
+
isInitialState.current = false;
|
|
230
230
|
}
|
|
231
231
|
}, [state, controller, onResult]);
|
|
232
232
|
|
|
@@ -244,7 +244,7 @@ export function useToolInvocations({
|
|
|
244
244
|
return {
|
|
245
245
|
reset: () => {
|
|
246
246
|
abort();
|
|
247
|
-
|
|
247
|
+
isInitialState.current = true;
|
|
248
248
|
},
|
|
249
249
|
abort,
|
|
250
250
|
resume: (toolCallId: string, payload: unknown) => {
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import { forwardRef, useCallback, useState } from "react";
|
|
2
4
|
|
|
3
5
|
import { Slot } from "@radix-ui/react-slot";
|
|
4
6
|
import React from "react";
|
|
5
7
|
import { useAssistantApi } from "../../context";
|
|
6
8
|
|
|
7
|
-
export namespace
|
|
9
|
+
export namespace ComposerPrimitiveAttachmentDropzone {
|
|
8
10
|
export type Element = HTMLDivElement;
|
|
9
11
|
export type Props = React.HTMLAttributes<HTMLDivElement> & {
|
|
10
12
|
asChild?: boolean | undefined;
|
|
@@ -12,19 +14,40 @@ export namespace ComposerAttachmentDropzonePrimitive {
|
|
|
12
14
|
};
|
|
13
15
|
}
|
|
14
16
|
|
|
15
|
-
export const
|
|
17
|
+
export const ComposerPrimitiveAttachmentDropzone = forwardRef<
|
|
16
18
|
HTMLDivElement,
|
|
17
|
-
|
|
19
|
+
ComposerPrimitiveAttachmentDropzone.Props
|
|
18
20
|
>(({ disabled, asChild = false, children, ...rest }, ref) => {
|
|
19
21
|
const [isDragging, setIsDragging] = useState(false);
|
|
20
22
|
const api = useAssistantApi();
|
|
21
23
|
|
|
22
|
-
const
|
|
24
|
+
const handleDragEnterCapture = useCallback(
|
|
25
|
+
(e: React.DragEvent) => {
|
|
26
|
+
if (disabled) return;
|
|
27
|
+
e.preventDefault();
|
|
28
|
+
setIsDragging(true);
|
|
29
|
+
},
|
|
30
|
+
[disabled],
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const handleDragOverCapture = useCallback(
|
|
23
34
|
(e: React.DragEvent) => {
|
|
24
35
|
if (disabled) return;
|
|
25
36
|
e.preventDefault();
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
if (!isDragging) setIsDragging(true);
|
|
38
|
+
},
|
|
39
|
+
[disabled, isDragging],
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const handleDragLeaveCapture = useCallback(
|
|
43
|
+
(e: React.DragEvent) => {
|
|
44
|
+
if (disabled) return;
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
const next = e.relatedTarget as Node | null;
|
|
47
|
+
if (next && e.currentTarget.contains(next)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
setIsDragging(false);
|
|
28
51
|
},
|
|
29
52
|
[disabled],
|
|
30
53
|
);
|
|
@@ -33,7 +56,6 @@ export const ComposerAttachmentDropzone = forwardRef<
|
|
|
33
56
|
async (e: React.DragEvent) => {
|
|
34
57
|
if (disabled) return;
|
|
35
58
|
e.preventDefault();
|
|
36
|
-
e.stopPropagation();
|
|
37
59
|
setIsDragging(false);
|
|
38
60
|
for (const file of e.dataTransfer.files) {
|
|
39
61
|
try {
|
|
@@ -47,10 +69,10 @@ export const ComposerAttachmentDropzone = forwardRef<
|
|
|
47
69
|
);
|
|
48
70
|
|
|
49
71
|
const dragProps = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
onDragEnterCapture: handleDragEnterCapture,
|
|
73
|
+
onDragOverCapture: handleDragOverCapture,
|
|
74
|
+
onDragLeaveCapture: handleDragLeaveCapture,
|
|
75
|
+
onDropCapture: handleDrop,
|
|
54
76
|
};
|
|
55
77
|
|
|
56
78
|
const Comp = asChild ? Slot : "div";
|
|
@@ -67,4 +89,5 @@ export const ComposerAttachmentDropzone = forwardRef<
|
|
|
67
89
|
);
|
|
68
90
|
});
|
|
69
91
|
|
|
70
|
-
|
|
92
|
+
ComposerPrimitiveAttachmentDropzone.displayName =
|
|
93
|
+
"ComposerPrimitive.AttachmentDropzone";
|
|
@@ -5,4 +5,5 @@ export { ComposerPrimitiveCancel as Cancel } from "./ComposerCancel";
|
|
|
5
5
|
export { ComposerPrimitiveAddAttachment as AddAttachment } from "./ComposerAddAttachment";
|
|
6
6
|
export { ComposerPrimitiveAttachments as Attachments } from "./ComposerAttachments";
|
|
7
7
|
export { ComposerPrimitiveAttachmentByIndex as AttachmentByIndex } from "./ComposerAttachments";
|
|
8
|
+
export { ComposerPrimitiveAttachmentDropzone as AttachmentDropzone } from "./ComposerAttachmentDropzone";
|
|
8
9
|
export { ComposerPrimitiveIf as If } from "./ComposerIf";
|
|
@@ -9,7 +9,10 @@ import {
|
|
|
9
9
|
} from "react";
|
|
10
10
|
import { useAssistantApi, useAssistantState } from "../../context";
|
|
11
11
|
import { useManagedRef } from "../../utils/hooks/useManagedRef";
|
|
12
|
+
import { useSizeHandle } from "../../utils/hooks/useSizeHandle";
|
|
12
13
|
import { useComposedRefs } from "@radix-ui/react-compose-refs";
|
|
14
|
+
import { useThreadViewport } from "../../context/react/ThreadViewportContext";
|
|
15
|
+
import { ThreadPrimitiveViewportSlack } from "../thread/ThreadViewportSlack";
|
|
13
16
|
|
|
14
17
|
const useIsHoveringRef = () => {
|
|
15
18
|
const api = useAssistantApi();
|
|
@@ -41,6 +44,34 @@ const useIsHoveringRef = () => {
|
|
|
41
44
|
return useManagedRef(callbackRef);
|
|
42
45
|
};
|
|
43
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Hook that registers the anchor user message as a content inset.
|
|
49
|
+
* Only registers if: user message, at index messages.length-2, and last message is assistant.
|
|
50
|
+
*/
|
|
51
|
+
const useMessageViewportRef = () => {
|
|
52
|
+
const turnAnchor = useThreadViewport((s) => s.turnAnchor);
|
|
53
|
+
const registerUserHeight = useThreadViewport(
|
|
54
|
+
(s) => s.registerUserMessageHeight,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
// inset rules:
|
|
58
|
+
// - the previous user message before the last assistant message registers its full height
|
|
59
|
+
const shouldRegisterAsInset = useAssistantState(
|
|
60
|
+
({ thread, message }) =>
|
|
61
|
+
turnAnchor === "top" &&
|
|
62
|
+
message.role === "user" &&
|
|
63
|
+
message.index === thread.messages.length - 2 &&
|
|
64
|
+
thread.messages.at(-1)?.role === "assistant",
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const getHeight = useCallback((el: HTMLElement) => el.offsetHeight, []);
|
|
68
|
+
|
|
69
|
+
return useSizeHandle(
|
|
70
|
+
shouldRegisterAsInset ? registerUserHeight : null,
|
|
71
|
+
getHeight,
|
|
72
|
+
);
|
|
73
|
+
};
|
|
74
|
+
|
|
44
75
|
export namespace MessagePrimitiveRoot {
|
|
45
76
|
export type Element = ComponentRef<typeof Primitive.div>;
|
|
46
77
|
/**
|
|
@@ -57,6 +88,9 @@ export namespace MessagePrimitiveRoot {
|
|
|
57
88
|
* hover state management for the message. It automatically tracks when the user
|
|
58
89
|
* is hovering over the message, which can be used by child components like action bars.
|
|
59
90
|
*
|
|
91
|
+
* When `turnAnchor="top"` is set on the viewport, this component
|
|
92
|
+
* registers itself as the scroll anchor if it's the last user message.
|
|
93
|
+
*
|
|
60
94
|
* @example
|
|
61
95
|
* ```tsx
|
|
62
96
|
* <MessagePrimitive.Root>
|
|
@@ -73,9 +107,18 @@ export const MessagePrimitiveRoot = forwardRef<
|
|
|
73
107
|
MessagePrimitiveRoot.Props
|
|
74
108
|
>((props, forwardRef) => {
|
|
75
109
|
const isHoveringRef = useIsHoveringRef();
|
|
76
|
-
const
|
|
110
|
+
const anchorUserMessageRef = useMessageViewportRef();
|
|
111
|
+
const ref = useComposedRefs<HTMLDivElement>(
|
|
112
|
+
forwardRef,
|
|
113
|
+
isHoveringRef,
|
|
114
|
+
anchorUserMessageRef,
|
|
115
|
+
);
|
|
77
116
|
|
|
78
|
-
return
|
|
117
|
+
return (
|
|
118
|
+
<ThreadPrimitiveViewportSlack>
|
|
119
|
+
<Primitive.div {...props} ref={ref} />
|
|
120
|
+
</ThreadPrimitiveViewportSlack>
|
|
121
|
+
);
|
|
79
122
|
});
|
|
80
123
|
|
|
81
124
|
MessagePrimitiveRoot.displayName = "MessagePrimitive.Root";
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import { useAssistantState } from "../../context";
|
|
4
4
|
import { DataMessagePart } from "../../types";
|
|
5
5
|
|
|
6
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
7
6
|
export const useMessagePartData = <T = any,>(name?: string) => {
|
|
8
7
|
const part = useAssistantState(({ part }) => {
|
|
9
8
|
if (part.type !== "data") {
|
|
@@ -11,14 +11,22 @@ import {
|
|
|
11
11
|
useThreadViewportStore,
|
|
12
12
|
} from "../../context/react/ThreadViewportContext";
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
export namespace useThreadScrollToBottom {
|
|
15
|
+
export type Options = {
|
|
16
|
+
behavior?: ScrollBehavior | undefined;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const useThreadScrollToBottom = ({
|
|
21
|
+
behavior,
|
|
22
|
+
}: useThreadScrollToBottom.Options = {}) => {
|
|
15
23
|
const isAtBottom = useThreadViewport((s) => s.isAtBottom);
|
|
16
24
|
|
|
17
25
|
const threadViewportStore = useThreadViewportStore();
|
|
18
26
|
|
|
19
27
|
const handleScrollToBottom = useCallback(() => {
|
|
20
|
-
threadViewportStore.getState().scrollToBottom();
|
|
21
|
-
}, [threadViewportStore]);
|
|
28
|
+
threadViewportStore.getState().scrollToBottom({ behavior });
|
|
29
|
+
}, [threadViewportStore, behavior]);
|
|
22
30
|
|
|
23
31
|
if (isAtBottom) return null;
|
|
24
32
|
return handleScrollToBottom;
|
|
@@ -32,4 +40,5 @@ export namespace ThreadPrimitiveScrollToBottom {
|
|
|
32
40
|
export const ThreadPrimitiveScrollToBottom = createActionButton(
|
|
33
41
|
"ThreadPrimitive.ScrollToBottom",
|
|
34
42
|
useThreadScrollToBottom,
|
|
43
|
+
["behavior"],
|
|
35
44
|
);
|
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
import { useComposedRefs } from "@radix-ui/react-compose-refs";
|
|
4
4
|
import { Primitive } from "@radix-ui/react-primitive";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
type ComponentRef,
|
|
7
|
+
forwardRef,
|
|
8
|
+
ComponentPropsWithoutRef,
|
|
9
|
+
useCallback,
|
|
10
|
+
} from "react";
|
|
6
11
|
import { useThreadViewportAutoScroll } from "./useThreadViewportAutoScroll";
|
|
7
|
-
import {
|
|
12
|
+
import { ThreadPrimitiveViewportProvider } from "../../context/providers/ThreadViewportProvider";
|
|
13
|
+
import { useSizeHandle } from "../../utils/hooks/useSizeHandle";
|
|
14
|
+
import { useThreadViewport } from "../../context/react/ThreadViewportContext";
|
|
8
15
|
|
|
9
16
|
export namespace ThreadPrimitiveViewport {
|
|
10
17
|
export type Element = ComponentRef<typeof Primitive.div>;
|
|
@@ -12,12 +19,31 @@ export namespace ThreadPrimitiveViewport {
|
|
|
12
19
|
/**
|
|
13
20
|
* Whether to automatically scroll to the bottom when new messages are added.
|
|
14
21
|
* When enabled, the viewport will automatically scroll to show the latest content.
|
|
15
|
-
*
|
|
22
|
+
*
|
|
23
|
+
* Default false if `turnAnchor` is "top", otherwise defaults to true.
|
|
16
24
|
*/
|
|
17
25
|
autoScroll?: boolean | undefined;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Controls scroll anchoring behavior for new messages.
|
|
29
|
+
* - "bottom" (default): Messages anchor at the bottom, classic chat behavior.
|
|
30
|
+
* - "top": New user messages anchor at the top of the viewport for a focused reading experience.
|
|
31
|
+
*/
|
|
32
|
+
turnAnchor?: "top" | "bottom" | undefined;
|
|
18
33
|
};
|
|
19
34
|
}
|
|
20
35
|
|
|
36
|
+
const useViewportSizeRef = () => {
|
|
37
|
+
const register = useThreadViewport((s) => s.registerViewport);
|
|
38
|
+
const getHeight = useCallback(
|
|
39
|
+
(el: HTMLElement) =>
|
|
40
|
+
el.clientHeight - parseFloat(getComputedStyle(el).paddingTop),
|
|
41
|
+
[],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return useSizeHandle(register, getHeight);
|
|
45
|
+
};
|
|
46
|
+
|
|
21
47
|
const ThreadPrimitiveViewportScrollable = forwardRef<
|
|
22
48
|
ThreadPrimitiveViewport.Element,
|
|
23
49
|
ThreadPrimitiveViewport.Props
|
|
@@ -25,8 +51,8 @@ const ThreadPrimitiveViewportScrollable = forwardRef<
|
|
|
25
51
|
const autoScrollRef = useThreadViewportAutoScroll<HTMLDivElement>({
|
|
26
52
|
autoScroll,
|
|
27
53
|
});
|
|
28
|
-
|
|
29
|
-
const ref = useComposedRefs(forwardedRef, autoScrollRef);
|
|
54
|
+
const viewportSizeRef = useViewportSizeRef();
|
|
55
|
+
const ref = useComposedRefs(forwardedRef, autoScrollRef, viewportSizeRef);
|
|
30
56
|
|
|
31
57
|
return (
|
|
32
58
|
<Primitive.div {...rest} ref={ref}>
|
|
@@ -47,7 +73,7 @@ ThreadPrimitiveViewportScrollable.displayName =
|
|
|
47
73
|
*
|
|
48
74
|
* @example
|
|
49
75
|
* ```tsx
|
|
50
|
-
* <ThreadPrimitive.Viewport
|
|
76
|
+
* <ThreadPrimitive.Viewport turnAnchor="top">
|
|
51
77
|
* <ThreadPrimitive.Messages components={{ Message: MyMessage }} />
|
|
52
78
|
* </ThreadPrimitive.Viewport>
|
|
53
79
|
* ```
|
|
@@ -55,11 +81,11 @@ ThreadPrimitiveViewportScrollable.displayName =
|
|
|
55
81
|
export const ThreadPrimitiveViewport = forwardRef<
|
|
56
82
|
ThreadPrimitiveViewport.Element,
|
|
57
83
|
ThreadPrimitiveViewport.Props
|
|
58
|
-
>((props, ref) => {
|
|
84
|
+
>(({ turnAnchor, ...props }, ref) => {
|
|
59
85
|
return (
|
|
60
|
-
<
|
|
86
|
+
<ThreadPrimitiveViewportProvider options={{ turnAnchor }}>
|
|
61
87
|
<ThreadPrimitiveViewportScrollable {...props} ref={ref} />
|
|
62
|
-
</
|
|
88
|
+
</ThreadPrimitiveViewportProvider>
|
|
63
89
|
);
|
|
64
90
|
});
|
|
65
91
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useComposedRefs } from "@radix-ui/react-compose-refs";
|
|
4
|
+
import { Primitive } from "@radix-ui/react-primitive";
|
|
5
|
+
import {
|
|
6
|
+
type ComponentRef,
|
|
7
|
+
forwardRef,
|
|
8
|
+
ComponentPropsWithoutRef,
|
|
9
|
+
useCallback,
|
|
10
|
+
} from "react";
|
|
11
|
+
import { useSizeHandle } from "../../utils/hooks/useSizeHandle";
|
|
12
|
+
import { useThreadViewport } from "../../context/react/ThreadViewportContext";
|
|
13
|
+
|
|
14
|
+
export namespace ThreadPrimitiveViewportFooter {
|
|
15
|
+
export type Element = ComponentRef<typeof Primitive.div>;
|
|
16
|
+
export type Props = ComponentPropsWithoutRef<typeof Primitive.div>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A footer container that measures its height for scroll calculations.
|
|
21
|
+
*
|
|
22
|
+
* This component measures its height and provides it to the viewport context
|
|
23
|
+
* for use in scroll calculations (e.g., ViewportSlack min-height).
|
|
24
|
+
*
|
|
25
|
+
* Multiple ViewportFooter components can be used - their heights are summed.
|
|
26
|
+
*
|
|
27
|
+
* Typically used with `className="sticky bottom-0"` to keep the footer
|
|
28
|
+
* visible at the bottom of the viewport while scrolling.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* <ThreadPrimitive.Viewport>
|
|
33
|
+
* <ThreadPrimitive.Messages components={{ ... }} />
|
|
34
|
+
* <ThreadPrimitive.ViewportFooter className="sticky bottom-0">
|
|
35
|
+
* <Composer />
|
|
36
|
+
* </ThreadPrimitive.ViewportFooter>
|
|
37
|
+
* </ThreadPrimitive.Viewport>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export const ThreadPrimitiveViewportFooter = forwardRef<
|
|
41
|
+
ThreadPrimitiveViewportFooter.Element,
|
|
42
|
+
ThreadPrimitiveViewportFooter.Props
|
|
43
|
+
>((props, forwardedRef) => {
|
|
44
|
+
const register = useThreadViewport((s) => s.registerContentInset);
|
|
45
|
+
const getHeight = useCallback((el: HTMLElement) => {
|
|
46
|
+
const marginTop = parseFloat(getComputedStyle(el).marginTop) || 0;
|
|
47
|
+
return el.offsetHeight + marginTop;
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
const resizeRef = useSizeHandle(register, getHeight);
|
|
51
|
+
|
|
52
|
+
const ref = useComposedRefs(forwardedRef, resizeRef);
|
|
53
|
+
|
|
54
|
+
return <Primitive.div {...props} ref={ref} />;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
ThreadPrimitiveViewportFooter.displayName = "ThreadPrimitive.ViewportFooter";
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
4
|
+
import {
|
|
5
|
+
createContext,
|
|
6
|
+
type FC,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
useCallback,
|
|
9
|
+
useContext,
|
|
10
|
+
} from "react";
|
|
11
|
+
import { useThreadViewportStore } from "../../context/react/ThreadViewportContext";
|
|
12
|
+
import { useAssistantState } from "../../context";
|
|
13
|
+
import { useManagedRef } from "../../utils/hooks/useManagedRef";
|
|
14
|
+
|
|
15
|
+
const SlackNestingContext = createContext(false);
|
|
16
|
+
|
|
17
|
+
const parseCssLength = (value: string, element: HTMLElement): number => {
|
|
18
|
+
const match = value.match(/^([\d.]+)(em|px|rem)$/);
|
|
19
|
+
if (!match) return 0;
|
|
20
|
+
|
|
21
|
+
const num = parseFloat(match[1]!);
|
|
22
|
+
const unit = match[2];
|
|
23
|
+
|
|
24
|
+
if (unit === "px") return num;
|
|
25
|
+
if (unit === "em") {
|
|
26
|
+
const fontSize = parseFloat(getComputedStyle(element).fontSize) || 16;
|
|
27
|
+
return num * fontSize;
|
|
28
|
+
}
|
|
29
|
+
if (unit === "rem") {
|
|
30
|
+
const rootFontSize =
|
|
31
|
+
parseFloat(getComputedStyle(document.documentElement).fontSize) || 16;
|
|
32
|
+
return num * rootFontSize;
|
|
33
|
+
}
|
|
34
|
+
return 0;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export type ThreadViewportSlackProps = {
|
|
38
|
+
/** Threshold at which the user message height clamps to the offset */
|
|
39
|
+
fillClampThreshold?: string;
|
|
40
|
+
/** Offset used when clamping large user messages */
|
|
41
|
+
fillClampOffset?: string;
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A slot component that provides minimum height to enable scroll anchoring.
|
|
47
|
+
*
|
|
48
|
+
* When using `turnAnchor="top"`, this component ensures there is
|
|
49
|
+
* enough scroll room below the anchor point (last user message) for it to scroll
|
|
50
|
+
* to the top of the viewport. The min-height is applied only to the last
|
|
51
|
+
* assistant message.
|
|
52
|
+
*
|
|
53
|
+
* This component is used internally by MessagePrimitive.Root.
|
|
54
|
+
*/
|
|
55
|
+
export const ThreadPrimitiveViewportSlack: FC<ThreadViewportSlackProps> = ({
|
|
56
|
+
children,
|
|
57
|
+
fillClampThreshold = "10em",
|
|
58
|
+
fillClampOffset = "6em",
|
|
59
|
+
}) => {
|
|
60
|
+
const isLast = useAssistantState(({ message }) => message.isLast);
|
|
61
|
+
const threadViewportStore = useThreadViewportStore({ optional: true });
|
|
62
|
+
const isNested = useContext(SlackNestingContext);
|
|
63
|
+
|
|
64
|
+
const callback = useCallback(
|
|
65
|
+
(el: HTMLElement) => {
|
|
66
|
+
if (!threadViewportStore || isNested) return;
|
|
67
|
+
|
|
68
|
+
const updateMinHeight = () => {
|
|
69
|
+
const state = threadViewportStore.getState();
|
|
70
|
+
if (state.turnAnchor === "top" && isLast) {
|
|
71
|
+
const { viewport, inset, userMessage } = state.height;
|
|
72
|
+
const threshold = parseCssLength(fillClampThreshold, el);
|
|
73
|
+
const offset = parseCssLength(fillClampOffset, el);
|
|
74
|
+
const clampAdjustment =
|
|
75
|
+
userMessage <= threshold ? userMessage : offset;
|
|
76
|
+
|
|
77
|
+
const minHeight = Math.max(0, viewport - inset - clampAdjustment);
|
|
78
|
+
el.style.minHeight = `${minHeight}px`;
|
|
79
|
+
el.style.flexShrink = "0";
|
|
80
|
+
el.style.transition = "min-height 0s";
|
|
81
|
+
} else {
|
|
82
|
+
el.style.minHeight = "";
|
|
83
|
+
el.style.flexShrink = "";
|
|
84
|
+
el.style.transition = "";
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
updateMinHeight();
|
|
89
|
+
return threadViewportStore.subscribe(updateMinHeight);
|
|
90
|
+
},
|
|
91
|
+
[
|
|
92
|
+
threadViewportStore,
|
|
93
|
+
isLast,
|
|
94
|
+
isNested,
|
|
95
|
+
fillClampThreshold,
|
|
96
|
+
fillClampOffset,
|
|
97
|
+
],
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
const ref = useManagedRef<HTMLElement>(callback);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<SlackNestingContext.Provider value={true}>
|
|
104
|
+
<Slot ref={ref}>{children}</Slot>
|
|
105
|
+
</SlackNestingContext.Provider>
|
|
106
|
+
);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
ThreadPrimitiveViewportSlack.displayName = "ThreadPrimitive.ViewportSlack";
|
|
@@ -2,6 +2,9 @@ export { ThreadPrimitiveRoot as Root } from "./ThreadRoot";
|
|
|
2
2
|
export { ThreadPrimitiveEmpty as Empty } from "./ThreadEmpty";
|
|
3
3
|
export { ThreadPrimitiveIf as If } from "./ThreadIf";
|
|
4
4
|
export { ThreadPrimitiveViewport as Viewport } from "./ThreadViewport";
|
|
5
|
+
export { ThreadPrimitiveViewportProvider as ViewportProvider } from "../../context/providers/ThreadViewportProvider";
|
|
6
|
+
export { ThreadPrimitiveViewportFooter as ViewportFooter } from "./ThreadViewportFooter";
|
|
7
|
+
export { ThreadPrimitiveViewportSlack as ViewportSlack } from "./ThreadViewportSlack";
|
|
5
8
|
export { ThreadPrimitiveMessages as Messages } from "./ThreadMessages";
|
|
6
9
|
export { ThreadPrimitiveMessageByIndex as MessageByIndex } from "./ThreadMessages";
|
|
7
10
|
export { ThreadPrimitiveScrollToBottom as ScrollToBottom } from "./ThreadScrollToBottom";
|