@blocksdiy/react-common 1.25.1 → 1.27.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.
|
@@ -23,6 +23,7 @@ export interface AgentChat {
|
|
|
23
23
|
};
|
|
24
24
|
disableGeneratingDynamicChatComponent?: boolean;
|
|
25
25
|
hideToolsUi?: boolean;
|
|
26
|
+
disableAttachments?: boolean;
|
|
26
27
|
}
|
|
27
28
|
export interface Agent {
|
|
28
29
|
id: string;
|
|
@@ -80,13 +81,11 @@ export interface AgentChatMessageProps {
|
|
|
80
81
|
message: Message;
|
|
81
82
|
index: number;
|
|
82
83
|
}
|
|
83
|
-
export declare function AgentChatMessage({ message, index, ...props }: React.ComponentProps<"div"> & AgentChatMessageProps): import("react/jsx-runtime").JSX.Element;
|
|
84
|
+
export declare function AgentChatMessage({ message, index, ...props }: React.ComponentProps<"div"> & AgentChatMessageProps): import("react/jsx-runtime").JSX.Element | null;
|
|
84
85
|
export interface AgentChatMessagesProps {
|
|
85
|
-
asChild?: boolean;
|
|
86
|
-
autoScroll?: boolean;
|
|
87
86
|
scrollAreaClassName?: string;
|
|
88
87
|
}
|
|
89
|
-
export declare function AgentChatMessages({
|
|
88
|
+
export declare function AgentChatMessages({ scrollAreaClassName, ...props }: React.ComponentProps<"div"> & AgentChatMessagesProps): import("react/jsx-runtime").JSX.Element;
|
|
90
89
|
export interface AgentChatThinkingProps {
|
|
91
90
|
asChild?: boolean;
|
|
92
91
|
}
|
|
@@ -98,9 +97,8 @@ export declare function AgentChatFetching({ asChild, ...props }: React.Component
|
|
|
98
97
|
export interface AgentChatInputProps {
|
|
99
98
|
asChild?: boolean;
|
|
100
99
|
onSubmit?: (e: React.FormEvent<HTMLTextAreaElement>) => void;
|
|
101
|
-
acceptFiles?: boolean;
|
|
102
100
|
}
|
|
103
|
-
export declare function AgentChatInput({ asChild, onChange, onKeyDown, onPaste, onDragOver, onDragLeave, onDrop, onSubmit,
|
|
101
|
+
export declare function AgentChatInput({ asChild, onChange, onKeyDown, onPaste, onDragOver, onDragLeave, onDrop, onSubmit, ...props }: React.ComponentProps<"textarea"> & AgentChatInputProps): import("react/jsx-runtime").JSX.Element;
|
|
104
102
|
export interface AgentChatSendTriggerProps {
|
|
105
103
|
asChild?: boolean;
|
|
106
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"new-agent-chat.d.ts","sourceRoot":"","sources":["../../src/components/new-agent-chat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAM/D,OAAO,EAEL,SAAS,EACT,cAAc,EAQf,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,GACf,MAAM,2BAA2B,CAAC;AAQnC,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,6BAA6B,GAAI,MAAM,MAAM,WAEzD,CAAC;AAEF,MAAM,WAAW,SAAS;IACxB,aAAa,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC7D,qCAAqC,CAAC,EAAE,OAAO,CAAC;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AA4ED,MAAM,WAAW,qBAAqB;IAEpC,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,kBAAkB,EAAE,CAAC;IAGjC,WAAW,EAAE,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,MAAM,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1E,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,cAAc,EAAE,CAAC,WAAW,EAAE,UAAU,EAAE,KAAK,IAAI,CAAC;IACpD,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAG7B,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAClD,cAAc,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAC7D,kBAAkB,EAAE,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,kBAAkB,EAAE,CAAC,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,eAAO,MAAM,gBAAgB,uDAAoD,CAAC;AAElF,eAAO,MAAM,YAAY,6BAMxB,CAAC;AA6XF,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mGAAmG;IACnG,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,GAAG,CAAC;IAClB,gBAAgB,CAAC,EAAE,UAAU,EAAE,CAAC;CAIjC;AAED,eAAO,MAAM,aAAa,GAAI,cAK3B,kBAAkB,mDAyEpB,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,gBAAgB,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,qBAAqB,kDAMjH;AAED,MAAM,WAAW,sBAAsB;IACrC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,mBAAmB,EACnB,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,sBAAsB,2CA0BtD;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,OAAe,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,sBAAsB,kDASpH;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,OAAe,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,sBAAsB,kDAuBpH;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,mBAAmB,CAAC,KAAK,IAAI,CAAC;CAC9D;AAED,wBAAgB,cAAc,CAAC,EAC7B,OAAe,EACf,QAAQ,EACR,SAAS,EACT,OAAO,EACP,UAAU,EACV,WAAW,EACX,MAAM,EACN,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,UAAU,CAAC,GAAG,mBAAmB,2CA+JxD;AACD,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,oBAAoB,CAAC,EACnC,OAAe,EACf,OAAO,EACP,QAAQ,EACR,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,yBAAyB,2CAkC5D;AAED,MAAM,WAAW,+BAA+B;IAC9C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,wBAAgB,0BAA0B,CAAC,EACzC,OAAe,EACf,OAAO,EACP,QAAQ,EACR,MAAM,EACN,QAAe,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,+BAA+B,2CAkElE;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CAAC,EAClC,UAAU,EACV,OAAe,EACf,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,wBAAwB,2CAIxD;AAED,MAAM,WAAW,8BAA8B;IAC7C,UAAU,EAAE,UAAU,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,yBAAyB,CAAC,EACxC,UAAU,EACV,OAAe,EACf,OAAO,EACP,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,QAAQ,CAAC,GAAG,8BAA8B,2CAcjE"}
|
|
@@ -2,9 +2,11 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { AppVersionService, BlocksApiService, websocketsService } from "@blocksdiy/blocks-client-api";
|
|
3
3
|
import { getApiHost } from "@blocksdiy/blocks-client-api/envService";
|
|
4
4
|
import { CopilotKit, useCopilotChatInternal } from "@copilotkit/react-core";
|
|
5
|
+
import { useCopilotKit } from "@copilotkit/react-core/v2";
|
|
5
6
|
import { Slot } from "@radix-ui/react-slot";
|
|
6
7
|
import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
7
8
|
export { useRenderTool, useRenderToolCall, useDefaultRenderTool, useHumanInTheLoop, ToolCallStatus, } from "@copilotkit/react-core/v2";
|
|
9
|
+
import { useStickToBottom } from "use-stick-to-bottom";
|
|
8
10
|
// Client-only `useLayoutEffect`, falls back to `useEffect` during SSR.
|
|
9
11
|
// Needed so the `agent.threadId` mirror commits before CopilotKit's
|
|
10
12
|
// connect-on-mount effect reads it.
|
|
@@ -18,6 +20,7 @@ const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024;
|
|
|
18
20
|
// load and break history. Fixed UUID is fine — rows are scoped by
|
|
19
21
|
// (appId, chatId, threadId) and chatId is already per-chat unique.
|
|
20
22
|
const DEFAULT_CHAT_THREAD_ID = "8e1c8e4e-7b15-4f2c-a3d8-9e7f6c2b1a3e";
|
|
23
|
+
const HIDDEN_INITIAL_PROMPT_MESSAGE_NAME = "__agent_chat_hidden_initial_prompt__";
|
|
21
24
|
const generateId = (length = 10) => {
|
|
22
25
|
return Math.random()
|
|
23
26
|
.toString(36)
|
|
@@ -27,10 +30,10 @@ const generateId = (length = 10) => {
|
|
|
27
30
|
* Walk the conversation and return every assistant-emitted tool call
|
|
28
31
|
* that has no matching ToolMessage (i.e. is awaiting a result). With
|
|
29
32
|
* our current setup, the only tool that ends up in this state is the
|
|
30
|
-
* frontend
|
|
31
|
-
* run and never appear pending in the UI. Used by `sendFromInputs`
|
|
32
|
-
*
|
|
33
|
-
* instead of
|
|
33
|
+
* frontend user-choice tool; backend tools resolve in the same agent
|
|
34
|
+
* run and never appear pending in the UI. Used by `sendFromInputs` to
|
|
35
|
+
* auto-skip dangling answer UIs when the user types a new message
|
|
36
|
+
* instead of choosing an answer.
|
|
34
37
|
*/
|
|
35
38
|
const _collectPendingToolCalls = (messages) => {
|
|
36
39
|
const toolMessageIds = new Set();
|
|
@@ -101,6 +104,7 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
101
104
|
// "are we still warming up" signal exposed to consumers.
|
|
102
105
|
const [isConfigLoaded, setIsConfigLoaded] = useState(false);
|
|
103
106
|
const { messages: copilotMessages, sendMessage: copilotSendMessage, stopGeneration: copilotStopGeneration, isAvailable: isAgentReady, isLoading: isAgentRunning, agent: copilotAgent, } = useCopilotChatInternal();
|
|
107
|
+
const { copilotkit } = useCopilotKit();
|
|
104
108
|
// Mirror `currentThreadId` onto the agent instance — the `<CopilotKit>`
|
|
105
109
|
// prop only feeds React context, but `agent.threadId` is what hits the
|
|
106
110
|
// wire and the LangGraph checkpoint. `<CopilotChat>` does this for you;
|
|
@@ -117,7 +121,12 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
117
121
|
const [attachments, setAttachments] = useState([]);
|
|
118
122
|
const [isDraggingFiles, setIsDraggingFiles] = useState(false);
|
|
119
123
|
const blocksApiService = useMemo(() => new BlocksApiService({ token }), [token]);
|
|
124
|
+
const attachmentsEnabled = agentChat ? agentChat.disableAttachments !== true : false;
|
|
120
125
|
const canSendMessage = Boolean(agentChatId && agentChat && agent && isConfigLoaded && isAgentReady && !isAgentRunning);
|
|
126
|
+
const isFetchingMessages = !isConfigLoaded || (!isAgentReady && copilotMessages.length === 0);
|
|
127
|
+
// CopilotKit reports `isLoading` during connect/replay as well as real runs.
|
|
128
|
+
// Only expose thinking once the bootstrap connect phase has completed.
|
|
129
|
+
const isThinking = isAgentRunning && !isFetchingMessages;
|
|
121
130
|
const sendMessage = useCallback((message) => {
|
|
122
131
|
if (!canSendMessage) {
|
|
123
132
|
return Promise.resolve();
|
|
@@ -166,18 +175,6 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
166
175
|
return maybeComponentBlocks.filter((component) => Boolean(component));
|
|
167
176
|
}, [blocksApiService, componentIds]);
|
|
168
177
|
const componentIdsKey = componentIds?.join(",") ?? "";
|
|
169
|
-
const sendAgentInitialPrompt = useCallback(async (agentInitialPrompt) => {
|
|
170
|
-
if (!agentInitialPrompt) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
const { content, hiddenContent } = agentInitialPrompt;
|
|
174
|
-
if (content) {
|
|
175
|
-
await sendMessage({ content });
|
|
176
|
-
}
|
|
177
|
-
if (hiddenContent) {
|
|
178
|
-
await sendMessage({ content: hiddenContent });
|
|
179
|
-
}
|
|
180
|
-
}, [sendMessage]);
|
|
181
178
|
const initKeyRef = useRef(null);
|
|
182
179
|
// Guard against re-firing the initial prompt across re-renders of the
|
|
183
180
|
// post-connect effect. Reset whenever the init key changes (thread
|
|
@@ -218,6 +215,7 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
218
215
|
initialPrompt: agentChatBlock.data.initialPrompt,
|
|
219
216
|
disableGeneratingDynamicChatComponent: agentChatBlock.data.disableGeneratingDynamicChatComponent,
|
|
220
217
|
hideToolsUi: agentChatBlock.data.hideToolsUi,
|
|
218
|
+
disableAttachments: agentChatBlock.data.disableAttachments,
|
|
221
219
|
});
|
|
222
220
|
setComponents(chatComponentBlocks);
|
|
223
221
|
setAgent({
|
|
@@ -276,16 +274,31 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
276
274
|
if (!agentChat?.initialPrompt) {
|
|
277
275
|
return;
|
|
278
276
|
}
|
|
279
|
-
if (copilotAgent
|
|
277
|
+
if (!copilotAgent) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (copilotAgent.messages.length > 0) {
|
|
280
281
|
initialPromptSentRef.current = true;
|
|
281
282
|
return;
|
|
282
283
|
}
|
|
283
284
|
if (!canSendMessage) {
|
|
284
285
|
return;
|
|
285
286
|
}
|
|
287
|
+
const hiddenPrompt = agentChat.initialPrompt.hiddenContent?.trim();
|
|
288
|
+
const visiblePrompt = agentChat.initialPrompt.content?.trim();
|
|
289
|
+
const prompt = hiddenPrompt || visiblePrompt;
|
|
290
|
+
if (!prompt) {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
286
293
|
initialPromptSentRef.current = true;
|
|
287
|
-
|
|
288
|
-
|
|
294
|
+
copilotAgent.addMessage({
|
|
295
|
+
id: generateId(),
|
|
296
|
+
role: "user",
|
|
297
|
+
...(hiddenPrompt ? { name: HIDDEN_INITIAL_PROMPT_MESSAGE_NAME } : {}),
|
|
298
|
+
content: prompt,
|
|
299
|
+
});
|
|
300
|
+
void copilotkit.runAgent({ agent: copilotAgent });
|
|
301
|
+
}, [isAgentReady, isConfigLoaded, agentChat, copilotAgent, canSendMessage, copilotkit]);
|
|
289
302
|
const sendFromInputs = async () => {
|
|
290
303
|
if (!canSendMessage || !hasSendableInput(prompt, attachments)) {
|
|
291
304
|
return;
|
|
@@ -294,21 +307,21 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
294
307
|
// const currentAttachments = attachments;
|
|
295
308
|
setPrompt("");
|
|
296
309
|
setAttachments([]);
|
|
297
|
-
// Auto-skip any
|
|
310
|
+
// Auto-skip any frontend user-choice tool call that's still waiting.
|
|
298
311
|
//
|
|
299
312
|
// If the user types a new message instead of clicking an answer on
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
303
|
-
// * `executingToolCallIds` keeps the toolCallId (the parked
|
|
304
|
-
//
|
|
313
|
+
// the inline form, the agent had returned an assistant message with
|
|
314
|
+
// a tool call and is parked awaiting a ToolMessage that never arrives.
|
|
315
|
+
// Without intervention here:
|
|
316
|
+
// * `executingToolCallIds` keeps the toolCallId (the parked Promise
|
|
317
|
+
// from useHumanInTheLoop never resolves, so
|
|
305
318
|
// onToolExecutionEnd never fires).
|
|
306
319
|
// * No ToolMessage is ever added, so the render code stays on
|
|
307
320
|
// "executing" → the form looks live forever, the next refresh
|
|
308
321
|
// re-renders it as live, and the backend sees a dangling tool
|
|
309
322
|
// call followed by a new user turn (confusing the model).
|
|
310
323
|
//
|
|
311
|
-
// Synthesizing a `{"skipped"
|
|
324
|
+
// Synthesizing a `{"status":"skipped"}` ToolMessage here:
|
|
312
325
|
// * flips the render to "complete" (toolMessage wins over the
|
|
313
326
|
// executingToolCallIds check),
|
|
314
327
|
// * gets persisted by AgUiMessagePersister so a refresh shows
|
|
@@ -317,10 +330,10 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
317
330
|
// (assistant_tool_call → skip_result → new user turn) so the
|
|
318
331
|
// agent can respond coherently.
|
|
319
332
|
//
|
|
320
|
-
// Caveat: we don't (and can't, with the current useHumanInTheLoop
|
|
321
|
-
//
|
|
322
|
-
//
|
|
323
|
-
//
|
|
333
|
+
// Caveat: we don't (and can't, with the current useHumanInTheLoop API)
|
|
334
|
+
// resolve the parked Promise itself — it lingers in memory until page
|
|
335
|
+
// reload. Acceptable: one closure per skip, cleared on refresh, no
|
|
336
|
+
// functional impact.
|
|
324
337
|
if (copilotAgent) {
|
|
325
338
|
const pending = _collectPendingToolCalls(copilotAgent.messages);
|
|
326
339
|
for (const toolCall of pending) {
|
|
@@ -328,13 +341,16 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
328
341
|
id: generateId(),
|
|
329
342
|
role: "tool",
|
|
330
343
|
toolCallId: toolCall.id,
|
|
331
|
-
content: JSON.stringify({
|
|
344
|
+
content: JSON.stringify({ status: "skipped" }),
|
|
332
345
|
});
|
|
333
346
|
}
|
|
334
347
|
}
|
|
335
348
|
await sendMessage({ content: currentPrompt });
|
|
336
349
|
};
|
|
337
350
|
const addAttachments = (attachments) => {
|
|
351
|
+
if (!attachmentsEnabled) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
338
354
|
setAttachments((prevAttachments) => [...prevAttachments, ...attachments]);
|
|
339
355
|
};
|
|
340
356
|
const removeAttachment = (url) => {
|
|
@@ -343,6 +359,19 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
343
359
|
const clearAttachments = () => {
|
|
344
360
|
setAttachments([]);
|
|
345
361
|
};
|
|
362
|
+
const setChatAttachments = (value) => {
|
|
363
|
+
if (!attachmentsEnabled) {
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
setAttachments(value);
|
|
367
|
+
};
|
|
368
|
+
const setChatIsDraggingFiles = (value) => {
|
|
369
|
+
if (!attachmentsEnabled) {
|
|
370
|
+
setIsDraggingFiles(false);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
setIsDraggingFiles(value);
|
|
374
|
+
};
|
|
346
375
|
return (_jsx(AgentChatContext.Provider, { value: {
|
|
347
376
|
messages: copilotMessages,
|
|
348
377
|
prompt,
|
|
@@ -354,12 +383,12 @@ const ChatInitializer = ({ agentId, agentChatId, chatId, token, appId, component
|
|
|
354
383
|
removeAttachment,
|
|
355
384
|
clearAttachments,
|
|
356
385
|
setPrompt,
|
|
357
|
-
setAttachments,
|
|
358
|
-
setIsDraggingFiles,
|
|
386
|
+
setAttachments: setChatAttachments,
|
|
387
|
+
setIsDraggingFiles: setChatIsDraggingFiles,
|
|
359
388
|
setCurrentThreadId,
|
|
360
389
|
isDraggingFiles,
|
|
361
|
-
isFetchingMessages
|
|
362
|
-
isThinking
|
|
390
|
+
isFetchingMessages,
|
|
391
|
+
isThinking,
|
|
363
392
|
isAgentReady,
|
|
364
393
|
agentChat,
|
|
365
394
|
agent,
|
|
@@ -401,24 +430,29 @@ export const AgentChatRoot = ({ ...props
|
|
|
401
430
|
}, children: [_jsx(SessionReadables, {}), _jsx(ChatInitializer, { agentId: agentId, agentChatId: agentChatId, chatId: chatId, noPersistency: noPersistency, token: token, appId: appId, componentIds: componentIds, currentThreadId: currentThreadId, setCurrentThreadId: setCurrentThreadId, children: children })] }));
|
|
402
431
|
};
|
|
403
432
|
export function AgentChatMessage({ message, index, ...props }) {
|
|
433
|
+
if (message.role === "user" && message.name === HIDDEN_INITIAL_PROMPT_MESSAGE_NAME) {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
404
436
|
return _jsx("div", { "data-message-index": index, "data-message-role": message.role, ...props });
|
|
405
437
|
}
|
|
406
|
-
export function AgentChatMessages({
|
|
407
|
-
const scrollRef = useRef(null);
|
|
408
|
-
const Comp = asChild ? Slot : "div";
|
|
438
|
+
export function AgentChatMessages({ scrollAreaClassName, ...props }) {
|
|
409
439
|
const { messages } = useAgentChat();
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
440
|
+
const userMessageCount = messages.filter((message) => message.role === "user" && message.name !== HIDDEN_INITIAL_PROMPT_MESSAGE_NAME).length;
|
|
441
|
+
const previousUserMessageCountRef = useRef(userMessageCount);
|
|
442
|
+
const { scrollRef, contentRef, scrollToBottom } = useStickToBottom({
|
|
443
|
+
resize: "smooth",
|
|
444
|
+
initial: { damping: 1, stiffness: 1 },
|
|
445
|
+
});
|
|
415
446
|
useEffect(() => {
|
|
416
|
-
if (
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
447
|
+
if (userMessageCount > previousUserMessageCountRef.current) {
|
|
448
|
+
void scrollToBottom({
|
|
449
|
+
animation: "smooth",
|
|
450
|
+
ignoreEscapes: true,
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
previousUserMessageCountRef.current = userMessageCount;
|
|
454
|
+
}, [scrollToBottom, userMessageCount]);
|
|
455
|
+
return (_jsx("div", { ref: scrollRef, style: { overflow: "auto" }, className: scrollAreaClassName, children: _jsx("div", { ref: contentRef, "data-slot": "agent-chat-messages", ...props }) }));
|
|
422
456
|
}
|
|
423
457
|
export function AgentChatThinking({ asChild = false, ...props }) {
|
|
424
458
|
const { isThinking } = useAgentChat();
|
|
@@ -430,18 +464,28 @@ export function AgentChatThinking({ asChild = false, ...props }) {
|
|
|
430
464
|
}
|
|
431
465
|
export function AgentChatFetching({ asChild = false, ...props }) {
|
|
432
466
|
const { isFetchingMessages } = useAgentChat();
|
|
467
|
+
const [shouldRender, setShouldRender] = useState(isFetchingMessages);
|
|
433
468
|
const Comp = asChild ? Slot : "div";
|
|
434
|
-
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
const timeoutId = window.setTimeout(() => {
|
|
471
|
+
setShouldRender(isFetchingMessages);
|
|
472
|
+
}, isFetchingMessages ? 0 : 200);
|
|
473
|
+
return () => {
|
|
474
|
+
window.clearTimeout(timeoutId);
|
|
475
|
+
};
|
|
476
|
+
}, [isFetchingMessages]);
|
|
477
|
+
if (!shouldRender) {
|
|
435
478
|
return null;
|
|
436
479
|
}
|
|
437
|
-
return _jsx(Comp, { "data-slot": "agent-chat-fetching", ...props });
|
|
480
|
+
return _jsx(Comp, { "data-state": isFetchingMessages ? "open" : "closed", "data-slot": "agent-chat-fetching", ...props });
|
|
438
481
|
}
|
|
439
|
-
export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste, onDragOver, onDragLeave, onDrop, onSubmit,
|
|
440
|
-
const { setIsDraggingFiles, addAttachments, attachments, prompt, setPrompt, sendFromInputs, isThinking, isAgentReady, isFetchingMessages, } = useAgentChat();
|
|
482
|
+
export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste, onDragOver, onDragLeave, onDrop, onSubmit, ...props }) {
|
|
483
|
+
const { setIsDraggingFiles, addAttachments, attachments, prompt, setPrompt, sendFromInputs, isThinking, isAgentReady, isFetchingMessages, agentChat, } = useAgentChat();
|
|
484
|
+
const attachmentsEnabled = agentChat ? agentChat.disableAttachments !== true : false;
|
|
441
485
|
const isDisabled = props.disabled || isThinking || !isAgentReady || isFetchingMessages;
|
|
442
|
-
const isSubmitDisabled = isDisabled || !hasSendableInput(prompt, attachments);
|
|
486
|
+
const isSubmitDisabled = isDisabled || !hasSendableInput(prompt, attachmentsEnabled ? attachments : []);
|
|
443
487
|
const handleDragOver = useCallback((e) => {
|
|
444
|
-
if (!
|
|
488
|
+
if (!attachmentsEnabled) {
|
|
445
489
|
return;
|
|
446
490
|
}
|
|
447
491
|
e.preventDefault();
|
|
@@ -449,17 +493,17 @@ export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste,
|
|
|
449
493
|
if (e.dataTransfer.types.includes("Files")) {
|
|
450
494
|
setIsDraggingFiles(true);
|
|
451
495
|
}
|
|
452
|
-
}, [setIsDraggingFiles,
|
|
496
|
+
}, [setIsDraggingFiles, attachmentsEnabled]);
|
|
453
497
|
const handleDragLeave = useCallback((e) => {
|
|
454
|
-
if (!
|
|
498
|
+
if (!attachmentsEnabled) {
|
|
455
499
|
return;
|
|
456
500
|
}
|
|
457
501
|
e.preventDefault();
|
|
458
502
|
e.stopPropagation();
|
|
459
503
|
setIsDraggingFiles(false);
|
|
460
|
-
}, [setIsDraggingFiles,
|
|
504
|
+
}, [setIsDraggingFiles, attachmentsEnabled]);
|
|
461
505
|
const handleDrop = useCallback((e) => {
|
|
462
|
-
if (!
|
|
506
|
+
if (!attachmentsEnabled) {
|
|
463
507
|
return;
|
|
464
508
|
}
|
|
465
509
|
e.preventDefault();
|
|
@@ -470,7 +514,7 @@ export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste,
|
|
|
470
514
|
if (attachments) {
|
|
471
515
|
addAttachments(attachments);
|
|
472
516
|
}
|
|
473
|
-
}, [addAttachments, setIsDraggingFiles,
|
|
517
|
+
}, [addAttachments, setIsDraggingFiles, attachmentsEnabled]);
|
|
474
518
|
const handleKeyDown = useCallback((e) => {
|
|
475
519
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
476
520
|
e.preventDefault();
|
|
@@ -485,7 +529,7 @@ export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste,
|
|
|
485
529
|
setPrompt(e.target.value);
|
|
486
530
|
}, [setPrompt]);
|
|
487
531
|
const handlePaste = useCallback(async (e) => {
|
|
488
|
-
if (!
|
|
532
|
+
if (!attachmentsEnabled) {
|
|
489
533
|
return;
|
|
490
534
|
}
|
|
491
535
|
const clipboardData = e.clipboardData;
|
|
@@ -501,7 +545,7 @@ export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste,
|
|
|
501
545
|
if (attachments) {
|
|
502
546
|
addAttachments(attachments);
|
|
503
547
|
}
|
|
504
|
-
}, [addAttachments,
|
|
548
|
+
}, [addAttachments, attachmentsEnabled]);
|
|
505
549
|
const Comp = asChild ? Slot : "textarea";
|
|
506
550
|
return (_jsx(Comp, { value: prompt, onChange: (e) => {
|
|
507
551
|
handleChange(e);
|
|
@@ -512,17 +556,17 @@ export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste,
|
|
|
512
556
|
}, onPaste: (e) => {
|
|
513
557
|
handlePaste(e);
|
|
514
558
|
onPaste?.(e);
|
|
515
|
-
}, onDragOver:
|
|
559
|
+
}, onDragOver: attachmentsEnabled
|
|
516
560
|
? (e) => {
|
|
517
561
|
handleDragOver(e);
|
|
518
562
|
onDragOver?.(e);
|
|
519
563
|
}
|
|
520
|
-
: onDragOver, onDragLeave:
|
|
564
|
+
: onDragOver, onDragLeave: attachmentsEnabled
|
|
521
565
|
? (e) => {
|
|
522
566
|
handleDragLeave(e);
|
|
523
567
|
onDragLeave?.(e);
|
|
524
568
|
}
|
|
525
|
-
: onDragLeave, onDrop:
|
|
569
|
+
: onDragLeave, onDrop: attachmentsEnabled
|
|
526
570
|
? (e) => {
|
|
527
571
|
handleDrop(e);
|
|
528
572
|
onDrop?.(e);
|
|
@@ -530,7 +574,9 @@ export function AgentChatInput({ asChild = false, onChange, onKeyDown, onPaste,
|
|
|
530
574
|
: onDrop, "data-slot": "agent-chat-input", ...props }));
|
|
531
575
|
}
|
|
532
576
|
export function AgentChatSendTrigger({ asChild = false, onClick, disabled, ...props }) {
|
|
533
|
-
const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs
|
|
577
|
+
const { attachments, prompt, isThinking, isAgentReady, isFetchingMessages, sendFromInputs,
|
|
578
|
+
/* , stopGeneration */
|
|
579
|
+
} = useAgentChat();
|
|
534
580
|
const Comp = asChild ? Slot : "button";
|
|
535
581
|
const isDisabled = Boolean(disabled || isThinking || !isAgentReady || isFetchingMessages || !hasSendableInput(prompt, attachments));
|
|
536
582
|
return (_jsx(Comp, { onClick: (e) => {
|
|
@@ -547,10 +593,14 @@ export function AgentChatSendTrigger({ asChild = false, onClick, disabled, ...pr
|
|
|
547
593
|
}, disabled: isDisabled, "data-slot": "agent-chat-send-trigger", ...props }));
|
|
548
594
|
}
|
|
549
595
|
export function AgentChatAttachmentTrigger({ asChild = false, onClick, disabled, accept, multiple = true, ...props }) {
|
|
550
|
-
const { isThinking, isAgentReady, isFetchingMessages, addAttachments } = useAgentChat();
|
|
596
|
+
const { isThinking, isAgentReady, isFetchingMessages, addAttachments, agentChat } = useAgentChat();
|
|
551
597
|
const fileInputRef = useRef(null);
|
|
552
|
-
const
|
|
598
|
+
const disableAttachments = agentChat ? agentChat.disableAttachments === true : true;
|
|
599
|
+
const isDisabled = disabled ?? (disableAttachments || isThinking || !isAgentReady || isFetchingMessages);
|
|
553
600
|
const handleFileChange = useCallback(async (e) => {
|
|
601
|
+
if (disableAttachments) {
|
|
602
|
+
return;
|
|
603
|
+
}
|
|
554
604
|
const files = e.target.files;
|
|
555
605
|
if (!files || files.length === 0) {
|
|
556
606
|
return;
|
|
@@ -563,12 +613,15 @@ export function AgentChatAttachmentTrigger({ asChild = false, onClick, disabled,
|
|
|
563
613
|
if (fileInputRef.current) {
|
|
564
614
|
fileInputRef.current.value = "";
|
|
565
615
|
}
|
|
566
|
-
}, [addAttachments]);
|
|
616
|
+
}, [addAttachments, disableAttachments]);
|
|
567
617
|
const handleClick = useCallback(() => {
|
|
618
|
+
if (disableAttachments) {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
568
621
|
fileInputRef.current?.click();
|
|
569
|
-
}, []);
|
|
622
|
+
}, [disableAttachments]);
|
|
570
623
|
const Comp = asChild ? Slot : "button";
|
|
571
|
-
return (_jsxs(_Fragment, { children: [_jsx("input", { type: "file", ref: fileInputRef, onChange: handleFileChange, accept: accept, multiple: multiple, style: { display: "none" }, "aria-hidden": "true" }), _jsx(Comp, { onClick: (e) => {
|
|
624
|
+
return (_jsxs(_Fragment, { children: [!disableAttachments && (_jsx("input", { type: "file", ref: fileInputRef, onChange: handleFileChange, accept: accept, multiple: multiple, disabled: isDisabled, style: { display: "none" }, "aria-hidden": "true" })), _jsx(Comp, { onClick: (e) => {
|
|
572
625
|
handleClick();
|
|
573
626
|
onClick?.(e);
|
|
574
627
|
}, disabled: isDisabled, "data-slot": "agent-chat-attachment-trigger", ...props })] }));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocksdiy/react-common",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.27.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "React common",
|
|
6
6
|
"keywords": [],
|
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
"@vapi-ai/web": "^2.5.2",
|
|
38
38
|
"penpal": "^7.0.6",
|
|
39
39
|
"react": "^19.2.4",
|
|
40
|
+
"use-stick-to-bottom": "1.1.4",
|
|
40
41
|
"@blocksdiy/blocks-client-api": "1.5.0"
|
|
41
42
|
},
|
|
42
43
|
"devDependencies": {
|