@copilotkitnext/react 1.51.5-next.4 → 1.52.0-next.6
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/components/WildcardToolCallRender.cjs +13 -13
- package/dist/components/WildcardToolCallRender.cjs.map +1 -1
- package/dist/components/WildcardToolCallRender.mjs +13 -13
- package/dist/components/WildcardToolCallRender.mjs.map +1 -1
- package/dist/components/chat/CopilotChat.cjs.map +1 -1
- package/dist/components/chat/CopilotChat.mjs.map +1 -1
- package/dist/components/chat/CopilotChatAssistantMessage.cjs +36 -28
- package/dist/components/chat/CopilotChatAssistantMessage.cjs.map +1 -1
- package/dist/components/chat/CopilotChatAssistantMessage.mjs +37 -29
- package/dist/components/chat/CopilotChatAssistantMessage.mjs.map +1 -1
- package/dist/components/chat/CopilotChatAudioRecorder.cjs +2 -2
- package/dist/components/chat/CopilotChatAudioRecorder.cjs.map +1 -1
- package/dist/components/chat/CopilotChatAudioRecorder.mjs +2 -2
- package/dist/components/chat/CopilotChatAudioRecorder.mjs.map +1 -1
- package/dist/components/chat/CopilotChatInput.cjs +59 -51
- package/dist/components/chat/CopilotChatInput.cjs.map +1 -1
- package/dist/components/chat/CopilotChatInput.d.cts.map +1 -1
- package/dist/components/chat/CopilotChatInput.d.mts.map +1 -1
- package/dist/components/chat/CopilotChatInput.mjs +59 -51
- package/dist/components/chat/CopilotChatInput.mjs.map +1 -1
- package/dist/components/chat/CopilotChatMessageView.cjs +12 -7
- package/dist/components/chat/CopilotChatMessageView.cjs.map +1 -1
- package/dist/components/chat/CopilotChatMessageView.d.cts.map +1 -1
- package/dist/components/chat/CopilotChatMessageView.d.mts.map +1 -1
- package/dist/components/chat/CopilotChatMessageView.mjs +12 -7
- package/dist/components/chat/CopilotChatMessageView.mjs.map +1 -1
- package/dist/components/chat/CopilotChatReasoningMessage.cjs +24 -20
- package/dist/components/chat/CopilotChatReasoningMessage.cjs.map +1 -1
- package/dist/components/chat/CopilotChatReasoningMessage.d.cts.map +1 -1
- package/dist/components/chat/CopilotChatReasoningMessage.d.mts.map +1 -1
- package/dist/components/chat/CopilotChatReasoningMessage.mjs +25 -21
- package/dist/components/chat/CopilotChatReasoningMessage.mjs.map +1 -1
- package/dist/components/chat/CopilotChatSuggestionPill.cjs +6 -5
- package/dist/components/chat/CopilotChatSuggestionPill.cjs.map +1 -1
- package/dist/components/chat/CopilotChatSuggestionPill.mjs +6 -5
- package/dist/components/chat/CopilotChatSuggestionPill.mjs.map +1 -1
- package/dist/components/chat/CopilotChatSuggestionView.cjs +24 -12
- package/dist/components/chat/CopilotChatSuggestionView.cjs.map +1 -1
- package/dist/components/chat/CopilotChatSuggestionView.d.cts.map +1 -1
- package/dist/components/chat/CopilotChatSuggestionView.d.mts.map +1 -1
- package/dist/components/chat/CopilotChatSuggestionView.mjs +25 -13
- package/dist/components/chat/CopilotChatSuggestionView.mjs.map +1 -1
- package/dist/components/chat/CopilotChatToggleButton.cjs +7 -6
- package/dist/components/chat/CopilotChatToggleButton.cjs.map +1 -1
- package/dist/components/chat/CopilotChatToggleButton.d.cts.map +1 -1
- package/dist/components/chat/CopilotChatToggleButton.d.mts.map +1 -1
- package/dist/components/chat/CopilotChatToggleButton.mjs +7 -6
- package/dist/components/chat/CopilotChatToggleButton.mjs.map +1 -1
- package/dist/components/chat/CopilotChatUserMessage.cjs +29 -24
- package/dist/components/chat/CopilotChatUserMessage.cjs.map +1 -1
- package/dist/components/chat/CopilotChatUserMessage.d.cts.map +1 -1
- package/dist/components/chat/CopilotChatUserMessage.d.mts.map +1 -1
- package/dist/components/chat/CopilotChatUserMessage.mjs +30 -25
- package/dist/components/chat/CopilotChatUserMessage.mjs.map +1 -1
- package/dist/components/chat/CopilotChatView.cjs +45 -35
- package/dist/components/chat/CopilotChatView.cjs.map +1 -1
- package/dist/components/chat/CopilotChatView.d.cts +1 -1
- package/dist/components/chat/CopilotChatView.d.cts.map +1 -1
- package/dist/components/chat/CopilotChatView.d.mts +1 -1
- package/dist/components/chat/CopilotChatView.d.mts.map +1 -1
- package/dist/components/chat/CopilotChatView.mjs +45 -35
- package/dist/components/chat/CopilotChatView.mjs.map +1 -1
- package/dist/components/chat/CopilotModalHeader.cjs +8 -8
- package/dist/components/chat/CopilotModalHeader.cjs.map +1 -1
- package/dist/components/chat/CopilotModalHeader.d.cts.map +1 -1
- package/dist/components/chat/CopilotModalHeader.d.mts.map +1 -1
- package/dist/components/chat/CopilotModalHeader.mjs +8 -8
- package/dist/components/chat/CopilotModalHeader.mjs.map +1 -1
- package/dist/components/chat/CopilotPopupView.cjs +20 -15
- package/dist/components/chat/CopilotPopupView.cjs.map +1 -1
- package/dist/components/chat/CopilotPopupView.d.cts.map +1 -1
- package/dist/components/chat/CopilotPopupView.d.mts.map +1 -1
- package/dist/components/chat/CopilotPopupView.mjs +20 -15
- package/dist/components/chat/CopilotPopupView.mjs.map +1 -1
- package/dist/components/chat/CopilotSidebarView.cjs +54 -43
- package/dist/components/chat/CopilotSidebarView.cjs.map +1 -1
- package/dist/components/chat/CopilotSidebarView.d.cts.map +1 -1
- package/dist/components/chat/CopilotSidebarView.d.mts.map +1 -1
- package/dist/components/chat/CopilotSidebarView.mjs +55 -44
- package/dist/components/chat/CopilotSidebarView.mjs.map +1 -1
- package/dist/components/ui/button.cjs +42 -42
- package/dist/components/ui/button.cjs.map +1 -1
- package/dist/components/ui/button.mjs +42 -42
- package/dist/components/ui/button.mjs.map +1 -1
- package/dist/components/ui/dropdown-menu.cjs +7 -6
- package/dist/components/ui/dropdown-menu.cjs.map +1 -1
- package/dist/components/ui/dropdown-menu.mjs +7 -6
- package/dist/components/ui/dropdown-menu.mjs.map +1 -1
- package/dist/components/ui/tooltip.cjs +3 -2
- package/dist/components/ui/tooltip.cjs.map +1 -1
- package/dist/components/ui/tooltip.mjs +3 -2
- package/dist/components/ui/tooltip.mjs.map +1 -1
- package/dist/hooks/use-component.cjs +18 -5
- package/dist/hooks/use-component.cjs.map +1 -1
- package/dist/hooks/use-component.d.cts +21 -7
- package/dist/hooks/use-component.d.cts.map +1 -1
- package/dist/hooks/use-component.d.mts +21 -7
- package/dist/hooks/use-component.d.mts.map +1 -1
- package/dist/hooks/use-component.mjs +18 -5
- package/dist/hooks/use-component.mjs.map +1 -1
- package/dist/hooks/use-default-render-tool.cjs +2 -2
- package/dist/hooks/use-default-render-tool.cjs.map +1 -1
- package/dist/hooks/use-default-render-tool.d.cts +3 -3
- package/dist/hooks/use-default-render-tool.d.cts.map +1 -1
- package/dist/hooks/use-default-render-tool.d.mts +3 -3
- package/dist/hooks/use-default-render-tool.d.mts.map +1 -1
- package/dist/hooks/use-default-render-tool.mjs +2 -2
- package/dist/hooks/use-default-render-tool.mjs.map +1 -1
- package/dist/hooks/use-render-tool.cjs +15 -9
- package/dist/hooks/use-render-tool.cjs.map +1 -1
- package/dist/hooks/use-render-tool.d.cts +9 -9
- package/dist/hooks/use-render-tool.d.cts.map +1 -1
- package/dist/hooks/use-render-tool.d.mts +9 -9
- package/dist/hooks/use-render-tool.d.mts.map +1 -1
- package/dist/hooks/use-render-tool.mjs +15 -9
- package/dist/hooks/use-render-tool.mjs.map +1 -1
- package/dist/index.umd.js +429 -339
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/utils.cjs +2 -1
- package/dist/lib/utils.cjs.map +1 -1
- package/dist/lib/utils.mjs +3 -2
- package/dist/lib/utils.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/eslint-rules/README.md +52 -0
- package/eslint-rules/require-cpk-prefix.mjs +375 -0
- package/eslint.config.mjs +17 -0
- package/package.json +8 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatMessageView.cjs","names":["React","useRenderCustomMessages","useRenderActivityMessage","useCopilotKit","useCopilotChatConfiguration","CopilotChatAssistantMessage","isReactComponentType","CopilotChatUserMessage","CopilotChatReasoningMessage","renderSlot"],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"sourcesContent":["import React, { useEffect, useReducer } from \"react\";\nimport { WithSlots, renderSlot, isReactComponentType } from \"@/lib/slots\";\nimport CopilotChatAssistantMessage from \"./CopilotChatAssistantMessage\";\nimport CopilotChatUserMessage from \"./CopilotChatUserMessage\";\nimport CopilotChatReasoningMessage from \"./CopilotChatReasoningMessage\";\nimport {\n ActivityMessage,\n AssistantMessage,\n Message,\n ReasoningMessage,\n UserMessage,\n} from \"@ag-ui/core\";\nimport { twMerge } from \"tailwind-merge\";\nimport { useRenderActivityMessage, useRenderCustomMessages } from \"@/hooks\";\nimport { useCopilotKit } from \"@/providers/CopilotKitProvider\";\nimport { useCopilotChatConfiguration } from \"@/providers/CopilotChatConfigurationProvider\";\n\n/**\n * Memoized wrapper for assistant messages to prevent re-renders when other messages change.\n */\nconst MemoizedAssistantMessage = React.memo(\n function MemoizedAssistantMessage({\n message,\n messages,\n isRunning,\n AssistantMessageComponent,\n slotProps,\n }: {\n message: AssistantMessage;\n messages: Message[];\n isRunning: boolean;\n AssistantMessageComponent: typeof CopilotChatAssistantMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }) {\n return (\n <AssistantMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Compare tool calls if present\n const prevToolCalls = prevProps.message.toolCalls;\n const nextToolCalls = nextProps.message.toolCalls;\n if (prevToolCalls?.length !== nextToolCalls?.length) return false;\n if (prevToolCalls && nextToolCalls) {\n for (let i = 0; i < prevToolCalls.length; i++) {\n const prevTc = prevToolCalls[i];\n const nextTc = nextToolCalls[i];\n if (!prevTc || !nextTc) return false;\n if (prevTc.id !== nextTc.id) return false;\n if (prevTc.function.arguments !== nextTc.function.arguments)\n return false;\n }\n }\n\n // Check if tool results changed for this message's tool calls\n // Tool results are separate messages with role=\"tool\" that reference tool call IDs\n if (prevToolCalls && prevToolCalls.length > 0) {\n const toolCallIds = new Set(prevToolCalls.map((tc) => tc.id));\n\n const prevToolResults = prevProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n const nextToolResults = nextProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n\n // If number of tool results changed, re-render\n if (prevToolResults.length !== nextToolResults.length) return false;\n\n // If any tool result content changed, re-render\n for (let i = 0; i < prevToolResults.length; i++) {\n if (\n (prevToolResults[i] as any).content !==\n (nextToolResults[i] as any).content\n )\n return false;\n }\n }\n\n // Only care about isRunning if this message is CURRENTLY the latest\n // (we don't need to re-render just because a message stopped being the latest)\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.AssistantMessageComponent !==\n nextProps.AssistantMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for user messages to prevent re-renders when other messages change.\n */\nconst MemoizedUserMessage = React.memo(\n function MemoizedUserMessage({\n message,\n UserMessageComponent,\n slotProps,\n }: {\n message: UserMessage;\n UserMessageComponent: typeof CopilotChatUserMessage;\n slotProps?: Partial<React.ComponentProps<typeof CopilotChatUserMessage>>;\n }) {\n return <UserMessageComponent message={message} {...slotProps} />;\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.UserMessageComponent !== nextProps.UserMessageComponent)\n return false;\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n return true;\n },\n);\n\n/**\n * Memoized wrapper for activity messages to prevent re-renders when other messages change.\n */\nconst MemoizedActivityMessage = React.memo(\n function MemoizedActivityMessage({\n message,\n renderActivityMessage,\n }: {\n message: ActivityMessage;\n renderActivityMessage: (\n message: ActivityMessage,\n ) => React.ReactElement | null;\n }) {\n return renderActivityMessage(message);\n },\n (prevProps, nextProps) => {\n // Message ID changed = different message, must re-render\n if (prevProps.message.id !== nextProps.message.id) return false;\n\n // Activity type changed = must re-render\n if (prevProps.message.activityType !== nextProps.message.activityType)\n return false;\n\n // Compare content using JSON.stringify (native code, handles deep comparison)\n if (\n JSON.stringify(prevProps.message.content) !==\n JSON.stringify(nextProps.message.content)\n )\n return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for reasoning messages to prevent re-renders when other messages change.\n */\nconst MemoizedReasoningMessage = React.memo(\n function MemoizedReasoningMessage({\n message,\n messages,\n isRunning,\n ReasoningMessageComponent,\n slotProps,\n }: {\n message: ReasoningMessage;\n messages: Message[];\n isRunning: boolean;\n ReasoningMessageComponent: typeof CopilotChatReasoningMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }) {\n return (\n <ReasoningMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Re-render when \"latest\" status changes (e.g. reasoning message is no longer the last message\n // because a text message was added after it — this transitions isStreaming from true to false)\n const prevIsLatest =\n prevProps.messages[prevProps.messages.length - 1]?.id ===\n prevProps.message.id;\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (prevIsLatest !== nextIsLatest) return false;\n\n // Only care about isRunning if this message is CURRENTLY the latest\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.ReasoningMessageComponent !==\n nextProps.ReasoningMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for custom messages to prevent re-renders when other messages change.\n */\nconst MemoizedCustomMessage = React.memo(\n function MemoizedCustomMessage({\n message,\n position,\n renderCustomMessage,\n }: {\n message: Message;\n position: \"before\" | \"after\";\n renderCustomMessage: (params: {\n message: Message;\n position: \"before\" | \"after\";\n }) => React.ReactElement | null;\n stateSnapshot?: unknown;\n }) {\n return renderCustomMessage({ message, position });\n },\n (prevProps, nextProps) => {\n // Only re-render if the message or position changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.position !== nextProps.position) return false;\n // Compare message content - for assistant messages this is a string, for others may differ\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.message.role !== nextProps.message.role) return false;\n // Compare state snapshot - custom renderers may depend on state\n if (\n JSON.stringify(prevProps.stateSnapshot) !==\n JSON.stringify(nextProps.stateSnapshot)\n )\n return false;\n // Note: We don't compare renderCustomMessage function reference because it changes\n // frequently. The message and state comparison is sufficient to determine if a re-render is needed.\n return true;\n },\n);\n\nexport type CopilotChatMessageViewProps = Omit<\n WithSlots<\n {\n assistantMessage: typeof CopilotChatAssistantMessage;\n userMessage: typeof CopilotChatUserMessage;\n reasoningMessage: typeof CopilotChatReasoningMessage;\n cursor: typeof CopilotChatMessageView.Cursor;\n },\n {\n isRunning?: boolean;\n messages?: Message[];\n } & React.HTMLAttributes<HTMLDivElement>\n >,\n \"children\"\n> & {\n children?: (props: {\n isRunning: boolean;\n messages: Message[];\n messageElements: React.ReactElement[];\n }) => React.ReactElement;\n};\n\nexport function CopilotChatMessageView({\n messages = [],\n assistantMessage,\n userMessage,\n reasoningMessage,\n cursor,\n isRunning = false,\n children,\n className,\n ...props\n}: CopilotChatMessageViewProps) {\n const renderCustomMessage = useRenderCustomMessages();\n const { renderActivityMessage } = useRenderActivityMessage();\n const { copilotkit } = useCopilotKit();\n const config = useCopilotChatConfiguration();\n const [, forceUpdate] = useReducer((x) => x + 1, 0);\n\n // Subscribe to state changes so custom message renderers re-render when state updates.\n useEffect(() => {\n if (!config?.agentId) return;\n const agent = copilotkit.getAgent(config.agentId);\n if (!agent) return;\n\n const subscription = agent.subscribe({\n onStateChanged: forceUpdate,\n });\n return () => subscription.unsubscribe();\n }, [config?.agentId, copilotkit, forceUpdate]);\n\n // Helper to get state snapshot for a message (used for memoization)\n const getStateSnapshotForMessage = (messageId: string): unknown => {\n if (!config) return undefined;\n const resolvedRunId =\n copilotkit.getRunIdForMessage(\n config.agentId,\n config.threadId,\n messageId,\n ) ??\n copilotkit\n .getRunIdsForThread(config.agentId, config.threadId)\n .slice(-1)[0];\n if (!resolvedRunId) return undefined;\n return copilotkit.getStateByRun(\n config.agentId,\n config.threadId,\n resolvedRunId,\n );\n };\n\n const messageElements: React.ReactElement[] = messages\n .flatMap((message) => {\n const elements: (React.ReactElement | null | undefined)[] = [];\n const stateSnapshot = getStateSnapshotForMessage(message.id);\n\n // Render custom message before (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-before`}\n message={message}\n position=\"before\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n // Render the main message using memoized wrappers to prevent unnecessary re-renders\n if (message.role === \"assistant\") {\n // Determine the component and props from slot value\n let AssistantComponent = CopilotChatAssistantMessage;\n let assistantSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatAssistantMessage>>\n | undefined;\n\n if (isReactComponentType(assistantMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n AssistantComponent =\n assistantMessage as typeof CopilotChatAssistantMessage;\n } else if (typeof assistantMessage === \"string\") {\n // className string\n assistantSlotProps = { className: assistantMessage };\n } else if (assistantMessage && typeof assistantMessage === \"object\") {\n // Props object\n assistantSlotProps = assistantMessage as Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }\n\n elements.push(\n <MemoizedAssistantMessage\n key={message.id}\n message={message as AssistantMessage}\n messages={messages}\n isRunning={isRunning}\n AssistantMessageComponent={AssistantComponent}\n slotProps={assistantSlotProps}\n />,\n );\n } else if (message.role === \"user\") {\n // Determine the component and props from slot value\n let UserComponent = CopilotChatUserMessage;\n let userSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatUserMessage>>\n | undefined;\n\n if (isReactComponentType(userMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n UserComponent = userMessage as typeof CopilotChatUserMessage;\n } else if (typeof userMessage === \"string\") {\n // className string\n userSlotProps = { className: userMessage };\n } else if (userMessage && typeof userMessage === \"object\") {\n // Props object\n userSlotProps = userMessage as Partial<\n React.ComponentProps<typeof CopilotChatUserMessage>\n >;\n }\n\n elements.push(\n <MemoizedUserMessage\n key={message.id}\n message={message as UserMessage}\n UserMessageComponent={UserComponent}\n slotProps={userSlotProps}\n />,\n );\n } else if (message.role === \"activity\") {\n // Use memoized wrapper to prevent re-renders when other messages change\n const activityMsg = message as ActivityMessage;\n elements.push(\n <MemoizedActivityMessage\n key={message.id}\n message={activityMsg}\n renderActivityMessage={renderActivityMessage}\n />,\n );\n } else if (message.role === \"reasoning\") {\n // Determine the component and props from slot value\n let ReasoningComponent = CopilotChatReasoningMessage;\n let reasoningSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatReasoningMessage>>\n | undefined;\n\n if (isReactComponentType(reasoningMessage)) {\n ReasoningComponent =\n reasoningMessage as typeof CopilotChatReasoningMessage;\n } else if (typeof reasoningMessage === \"string\") {\n reasoningSlotProps = { className: reasoningMessage };\n } else if (reasoningMessage && typeof reasoningMessage === \"object\") {\n reasoningSlotProps = reasoningMessage as Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }\n\n elements.push(\n <MemoizedReasoningMessage\n key={message.id}\n message={message as ReasoningMessage}\n messages={messages}\n isRunning={isRunning}\n ReasoningMessageComponent={ReasoningComponent}\n slotProps={reasoningSlotProps}\n />,\n );\n }\n\n // Render custom message after (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-after`}\n message={message}\n position=\"after\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n return elements;\n })\n .filter(Boolean) as React.ReactElement[];\n\n if (children) {\n return children({ messageElements, messages, isRunning });\n }\n\n // Hide the chat-level loading cursor when the last message is a reasoning\n // message — the reasoning card already shows its own loading indicator.\n const lastMessage = messages[messages.length - 1];\n const showCursor = isRunning && lastMessage?.role !== \"reasoning\";\n\n return (\n <div className={twMerge(\"flex flex-col\", className)} {...props}>\n {messageElements}\n {showCursor && (\n <div className=\"mt-2\">\n {renderSlot(cursor, CopilotChatMessageView.Cursor, {})}\n </div>\n )}\n </div>\n );\n}\n\nCopilotChatMessageView.Cursor = function Cursor({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={twMerge(\n \"w-[11px] h-[11px] rounded-full bg-foreground animate-pulse-cursor ml-1\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport default CopilotChatMessageView;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CopilotChatMessageView.cjs","names":["React","useRenderCustomMessages","useRenderActivityMessage","useCopilotKit","useCopilotChatConfiguration","CopilotChatAssistantMessage","isReactComponentType","CopilotChatUserMessage","CopilotChatReasoningMessage","renderSlot"],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"sourcesContent":["import React, { useEffect, useReducer } from \"react\";\nimport { WithSlots, renderSlot, isReactComponentType } from \"@/lib/slots\";\nimport CopilotChatAssistantMessage from \"./CopilotChatAssistantMessage\";\nimport CopilotChatUserMessage from \"./CopilotChatUserMessage\";\nimport CopilotChatReasoningMessage from \"./CopilotChatReasoningMessage\";\nimport {\n ActivityMessage,\n AssistantMessage,\n Message,\n ReasoningMessage,\n UserMessage,\n} from \"@ag-ui/core\";\nimport { twMerge } from \"tailwind-merge\";\nimport { useRenderActivityMessage, useRenderCustomMessages } from \"@/hooks\";\nimport { useCopilotKit } from \"@/providers/CopilotKitProvider\";\nimport { useCopilotChatConfiguration } from \"@/providers/CopilotChatConfigurationProvider\";\n\n/**\n * Memoized wrapper for assistant messages to prevent re-renders when other messages change.\n */\nconst MemoizedAssistantMessage = React.memo(\n function MemoizedAssistantMessage({\n message,\n messages,\n isRunning,\n AssistantMessageComponent,\n slotProps,\n }: {\n message: AssistantMessage;\n messages: Message[];\n isRunning: boolean;\n AssistantMessageComponent: typeof CopilotChatAssistantMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }) {\n return (\n <AssistantMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Compare tool calls if present\n const prevToolCalls = prevProps.message.toolCalls;\n const nextToolCalls = nextProps.message.toolCalls;\n if (prevToolCalls?.length !== nextToolCalls?.length) return false;\n if (prevToolCalls && nextToolCalls) {\n for (let i = 0; i < prevToolCalls.length; i++) {\n const prevTc = prevToolCalls[i];\n const nextTc = nextToolCalls[i];\n if (!prevTc || !nextTc) return false;\n if (prevTc.id !== nextTc.id) return false;\n if (prevTc.function.arguments !== nextTc.function.arguments)\n return false;\n }\n }\n\n // Check if tool results changed for this message's tool calls\n // Tool results are separate messages with role=\"tool\" that reference tool call IDs\n if (prevToolCalls && prevToolCalls.length > 0) {\n const toolCallIds = new Set(prevToolCalls.map((tc) => tc.id));\n\n const prevToolResults = prevProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n const nextToolResults = nextProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n\n // If number of tool results changed, re-render\n if (prevToolResults.length !== nextToolResults.length) return false;\n\n // If any tool result content changed, re-render\n for (let i = 0; i < prevToolResults.length; i++) {\n if (\n (prevToolResults[i] as any).content !==\n (nextToolResults[i] as any).content\n )\n return false;\n }\n }\n\n // Only care about isRunning if this message is CURRENTLY the latest\n // (we don't need to re-render just because a message stopped being the latest)\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.AssistantMessageComponent !==\n nextProps.AssistantMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for user messages to prevent re-renders when other messages change.\n */\nconst MemoizedUserMessage = React.memo(\n function MemoizedUserMessage({\n message,\n UserMessageComponent,\n slotProps,\n }: {\n message: UserMessage;\n UserMessageComponent: typeof CopilotChatUserMessage;\n slotProps?: Partial<React.ComponentProps<typeof CopilotChatUserMessage>>;\n }) {\n return <UserMessageComponent message={message} {...slotProps} />;\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.UserMessageComponent !== nextProps.UserMessageComponent)\n return false;\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n return true;\n },\n);\n\n/**\n * Memoized wrapper for activity messages to prevent re-renders when other messages change.\n */\nconst MemoizedActivityMessage = React.memo(\n function MemoizedActivityMessage({\n message,\n renderActivityMessage,\n }: {\n message: ActivityMessage;\n renderActivityMessage: (\n message: ActivityMessage,\n ) => React.ReactElement | null;\n }) {\n return renderActivityMessage(message);\n },\n (prevProps, nextProps) => {\n // Message ID changed = different message, must re-render\n if (prevProps.message.id !== nextProps.message.id) return false;\n\n // Activity type changed = must re-render\n if (prevProps.message.activityType !== nextProps.message.activityType)\n return false;\n\n // Compare content using JSON.stringify (native code, handles deep comparison)\n if (\n JSON.stringify(prevProps.message.content) !==\n JSON.stringify(nextProps.message.content)\n )\n return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for reasoning messages to prevent re-renders when other messages change.\n */\nconst MemoizedReasoningMessage = React.memo(\n function MemoizedReasoningMessage({\n message,\n messages,\n isRunning,\n ReasoningMessageComponent,\n slotProps,\n }: {\n message: ReasoningMessage;\n messages: Message[];\n isRunning: boolean;\n ReasoningMessageComponent: typeof CopilotChatReasoningMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }) {\n return (\n <ReasoningMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Re-render when \"latest\" status changes (e.g. reasoning message is no longer the last message\n // because a text message was added after it — this transitions isStreaming from true to false)\n const prevIsLatest =\n prevProps.messages[prevProps.messages.length - 1]?.id ===\n prevProps.message.id;\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (prevIsLatest !== nextIsLatest) return false;\n\n // Only care about isRunning if this message is CURRENTLY the latest\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.ReasoningMessageComponent !==\n nextProps.ReasoningMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for custom messages to prevent re-renders when other messages change.\n */\nconst MemoizedCustomMessage = React.memo(\n function MemoizedCustomMessage({\n message,\n position,\n renderCustomMessage,\n }: {\n message: Message;\n position: \"before\" | \"after\";\n renderCustomMessage: (params: {\n message: Message;\n position: \"before\" | \"after\";\n }) => React.ReactElement | null;\n stateSnapshot?: unknown;\n }) {\n return renderCustomMessage({ message, position });\n },\n (prevProps, nextProps) => {\n // Only re-render if the message or position changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.position !== nextProps.position) return false;\n // Compare message content - for assistant messages this is a string, for others may differ\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.message.role !== nextProps.message.role) return false;\n // Compare state snapshot - custom renderers may depend on state\n if (\n JSON.stringify(prevProps.stateSnapshot) !==\n JSON.stringify(nextProps.stateSnapshot)\n )\n return false;\n // Note: We don't compare renderCustomMessage function reference because it changes\n // frequently. The message and state comparison is sufficient to determine if a re-render is needed.\n return true;\n },\n);\n\nexport type CopilotChatMessageViewProps = Omit<\n WithSlots<\n {\n assistantMessage: typeof CopilotChatAssistantMessage;\n userMessage: typeof CopilotChatUserMessage;\n reasoningMessage: typeof CopilotChatReasoningMessage;\n cursor: typeof CopilotChatMessageView.Cursor;\n },\n {\n isRunning?: boolean;\n messages?: Message[];\n } & React.HTMLAttributes<HTMLDivElement>\n >,\n \"children\"\n> & {\n children?: (props: {\n isRunning: boolean;\n messages: Message[];\n messageElements: React.ReactElement[];\n }) => React.ReactElement;\n};\n\nexport function CopilotChatMessageView({\n messages = [],\n assistantMessage,\n userMessage,\n reasoningMessage,\n cursor,\n isRunning = false,\n children,\n className,\n ...props\n}: CopilotChatMessageViewProps) {\n const renderCustomMessage = useRenderCustomMessages();\n const { renderActivityMessage } = useRenderActivityMessage();\n const { copilotkit } = useCopilotKit();\n const config = useCopilotChatConfiguration();\n const [, forceUpdate] = useReducer((x) => x + 1, 0);\n\n // Subscribe to state changes so custom message renderers re-render when state updates.\n useEffect(() => {\n if (!config?.agentId) return;\n const agent = copilotkit.getAgent(config.agentId);\n if (!agent) return;\n\n const subscription = agent.subscribe({\n onStateChanged: forceUpdate,\n });\n return () => subscription.unsubscribe();\n }, [config?.agentId, copilotkit, forceUpdate]);\n\n // Helper to get state snapshot for a message (used for memoization)\n const getStateSnapshotForMessage = (messageId: string): unknown => {\n if (!config) return undefined;\n const resolvedRunId =\n copilotkit.getRunIdForMessage(\n config.agentId,\n config.threadId,\n messageId,\n ) ??\n copilotkit\n .getRunIdsForThread(config.agentId, config.threadId)\n .slice(-1)[0];\n if (!resolvedRunId) return undefined;\n return copilotkit.getStateByRun(\n config.agentId,\n config.threadId,\n resolvedRunId,\n );\n };\n\n const messageElements: React.ReactElement[] = messages\n .flatMap((message) => {\n const elements: (React.ReactElement | null | undefined)[] = [];\n const stateSnapshot = getStateSnapshotForMessage(message.id);\n\n // Render custom message before (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-before`}\n message={message}\n position=\"before\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n // Render the main message using memoized wrappers to prevent unnecessary re-renders\n if (message.role === \"assistant\") {\n // Determine the component and props from slot value\n let AssistantComponent = CopilotChatAssistantMessage;\n let assistantSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatAssistantMessage>>\n | undefined;\n\n if (isReactComponentType(assistantMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n AssistantComponent =\n assistantMessage as typeof CopilotChatAssistantMessage;\n } else if (typeof assistantMessage === \"string\") {\n // className string\n assistantSlotProps = { className: assistantMessage };\n } else if (assistantMessage && typeof assistantMessage === \"object\") {\n // Props object\n assistantSlotProps = assistantMessage as Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }\n\n elements.push(\n <MemoizedAssistantMessage\n key={message.id}\n message={message as AssistantMessage}\n messages={messages}\n isRunning={isRunning}\n AssistantMessageComponent={AssistantComponent}\n slotProps={assistantSlotProps}\n />,\n );\n } else if (message.role === \"user\") {\n // Determine the component and props from slot value\n let UserComponent = CopilotChatUserMessage;\n let userSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatUserMessage>>\n | undefined;\n\n if (isReactComponentType(userMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n UserComponent = userMessage as typeof CopilotChatUserMessage;\n } else if (typeof userMessage === \"string\") {\n // className string\n userSlotProps = { className: userMessage };\n } else if (userMessage && typeof userMessage === \"object\") {\n // Props object\n userSlotProps = userMessage as Partial<\n React.ComponentProps<typeof CopilotChatUserMessage>\n >;\n }\n\n elements.push(\n <MemoizedUserMessage\n key={message.id}\n message={message as UserMessage}\n UserMessageComponent={UserComponent}\n slotProps={userSlotProps}\n />,\n );\n } else if (message.role === \"activity\") {\n // Use memoized wrapper to prevent re-renders when other messages change\n const activityMsg = message as ActivityMessage;\n elements.push(\n <MemoizedActivityMessage\n key={message.id}\n message={activityMsg}\n renderActivityMessage={renderActivityMessage}\n />,\n );\n } else if (message.role === \"reasoning\") {\n // Determine the component and props from slot value\n let ReasoningComponent = CopilotChatReasoningMessage;\n let reasoningSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatReasoningMessage>>\n | undefined;\n\n if (isReactComponentType(reasoningMessage)) {\n ReasoningComponent =\n reasoningMessage as typeof CopilotChatReasoningMessage;\n } else if (typeof reasoningMessage === \"string\") {\n reasoningSlotProps = { className: reasoningMessage };\n } else if (reasoningMessage && typeof reasoningMessage === \"object\") {\n reasoningSlotProps = reasoningMessage as Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }\n\n elements.push(\n <MemoizedReasoningMessage\n key={message.id}\n message={message as ReasoningMessage}\n messages={messages}\n isRunning={isRunning}\n ReasoningMessageComponent={ReasoningComponent}\n slotProps={reasoningSlotProps}\n />,\n );\n }\n\n // Render custom message after (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-after`}\n message={message}\n position=\"after\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n return elements;\n })\n .filter(Boolean) as React.ReactElement[];\n\n if (children) {\n return (\n <div data-copilotkit style={{ display: \"contents\" }}>\n {children({ messageElements, messages, isRunning })}\n </div>\n );\n }\n\n // Hide the chat-level loading cursor when the last message is a reasoning\n // message — the reasoning card already shows its own loading indicator.\n const lastMessage = messages[messages.length - 1];\n const showCursor = isRunning && lastMessage?.role !== \"reasoning\";\n\n return (\n <div\n data-copilotkit\n className={twMerge(\"cpk:flex cpk:flex-col\", className)}\n {...props}\n >\n {messageElements}\n {showCursor && (\n <div className=\"cpk:mt-2\">\n {renderSlot(cursor, CopilotChatMessageView.Cursor, {})}\n </div>\n )}\n </div>\n );\n}\n\nCopilotChatMessageView.Cursor = function Cursor({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={twMerge(\n \"cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport default CopilotChatMessageView;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatMessageView.d.cts","names":[],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"mappings":";;;;;;;;;KAgRY,2BAAA,GAA8B,IAAA,CACxC,SAAA;EAEI,gBAAA,SAAyB,2BAAA;EACzB,WAAA,SAAoB,sBAAA;EACpB,gBAAA,SAAyB,2BAAA;EACzB,MAAA,SAAe,sBAAA,CAAuB,MAAA;AAAA;EAGtC,SAAA;EACA,QAAA,GAAW,OAAA;AAAA,IACT,KAAA,CAAM,cAAA,CAAe,cAAA;EAI3B,QAAA,IAAY,KAAA;IACV,SAAA;IACA,QAAA,EAAU,OAAA;IACV,eAAA,EAAiB,KAAA,CAAM,YAAA;EAAA,MACnB,KAAA,CAAM,YAAA;AAAA;AAAA,iBAGE,sBAAA,CAAA;EACd,QAAA;EACA,gBAAA;EACA,WAAA;EACA,gBAAA;EACA,MAAA;EACA,SAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,2BAAA,GAA2B,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAVd,sBAAA;EAAA;;;
|
|
1
|
+
{"version":3,"file":"CopilotChatMessageView.d.cts","names":[],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"mappings":";;;;;;;;;KAgRY,2BAAA,GAA8B,IAAA,CACxC,SAAA;EAEI,gBAAA,SAAyB,2BAAA;EACzB,WAAA,SAAoB,sBAAA;EACpB,gBAAA,SAAyB,2BAAA;EACzB,MAAA,SAAe,sBAAA,CAAuB,MAAA;AAAA;EAGtC,SAAA;EACA,QAAA,GAAW,OAAA;AAAA,IACT,KAAA,CAAM,cAAA,CAAe,cAAA;EAI3B,QAAA,IAAY,KAAA;IACV,SAAA;IACA,QAAA,EAAU,OAAA;IACV,eAAA,EAAiB,KAAA,CAAM,YAAA;EAAA,MACnB,KAAA,CAAM,YAAA;AAAA;AAAA,iBAGE,sBAAA,CAAA;EACd,QAAA;EACA,gBAAA;EACA,WAAA;EACA,gBAAA;EACA,MAAA;EACA,SAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,2BAAA,GAA2B,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAVd,sBAAA;EAAA;;;KAwNb,KAAA,CAAM,cAAA,CAAe,cAAA,MAAe,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatMessageView.d.mts","names":[],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"mappings":";;;;;;;;;KAgRY,2BAAA,GAA8B,IAAA,CACxC,SAAA;EAEI,gBAAA,SAAyB,2BAAA;EACzB,WAAA,SAAoB,sBAAA;EACpB,gBAAA,SAAyB,2BAAA;EACzB,MAAA,SAAe,sBAAA,CAAuB,MAAA;AAAA;EAGtC,SAAA;EACA,QAAA,GAAW,OAAA;AAAA,IACT,KAAA,CAAM,cAAA,CAAe,cAAA;EAI3B,QAAA,IAAY,KAAA;IACV,SAAA;IACA,QAAA,EAAU,OAAA;IACV,eAAA,EAAiB,KAAA,CAAM,YAAA;EAAA,MACnB,KAAA,CAAM,YAAA;AAAA;AAAA,iBAGE,sBAAA,CAAA;EACd,QAAA;EACA,gBAAA;EACA,WAAA;EACA,gBAAA;EACA,MAAA;EACA,SAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,2BAAA,GAA2B,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAVd,sBAAA;EAAA;;;
|
|
1
|
+
{"version":3,"file":"CopilotChatMessageView.d.mts","names":[],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"mappings":";;;;;;;;;KAgRY,2BAAA,GAA8B,IAAA,CACxC,SAAA;EAEI,gBAAA,SAAyB,2BAAA;EACzB,WAAA,SAAoB,sBAAA;EACpB,gBAAA,SAAyB,2BAAA;EACzB,MAAA,SAAe,sBAAA,CAAuB,MAAA;AAAA;EAGtC,SAAA;EACA,QAAA,GAAW,OAAA;AAAA,IACT,KAAA,CAAM,cAAA,CAAe,cAAA;EAI3B,QAAA,IAAY,KAAA;IACV,SAAA;IACA,QAAA,EAAU,OAAA;IACV,eAAA,EAAiB,KAAA,CAAM,YAAA;EAAA,MACnB,KAAA,CAAM,YAAA;AAAA;AAAA,iBAGE,sBAAA,CAAA;EACd,QAAA;EACA,gBAAA;EACA,WAAA;EACA,gBAAA;EACA,MAAA;EACA,SAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,2BAAA,GAA2B,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAVd,sBAAA;EAAA;;;KAwNb,KAAA,CAAM,cAAA,CAAe,cAAA,MAAe,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA"}
|
|
@@ -194,25 +194,30 @@ function CopilotChatMessageView({ messages = [], assistantMessage, userMessage,
|
|
|
194
194
|
}, `${message.id}-custom-after`));
|
|
195
195
|
return elements;
|
|
196
196
|
}).filter(Boolean);
|
|
197
|
-
if (children) return
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
197
|
+
if (children) return /* @__PURE__ */ jsx("div", {
|
|
198
|
+
"data-copilotkit": true,
|
|
199
|
+
style: { display: "contents" },
|
|
200
|
+
children: children({
|
|
201
|
+
messageElements,
|
|
202
|
+
messages,
|
|
203
|
+
isRunning
|
|
204
|
+
})
|
|
201
205
|
});
|
|
202
206
|
const lastMessage = messages[messages.length - 1];
|
|
203
207
|
const showCursor = isRunning && lastMessage?.role !== "reasoning";
|
|
204
208
|
return /* @__PURE__ */ jsxs("div", {
|
|
205
|
-
|
|
209
|
+
"data-copilotkit": true,
|
|
210
|
+
className: twMerge("cpk:flex cpk:flex-col", className),
|
|
206
211
|
...props,
|
|
207
212
|
children: [messageElements, showCursor && /* @__PURE__ */ jsx("div", {
|
|
208
|
-
className: "mt-2",
|
|
213
|
+
className: "cpk:mt-2",
|
|
209
214
|
children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
|
|
210
215
|
})]
|
|
211
216
|
});
|
|
212
217
|
}
|
|
213
218
|
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
|
|
214
219
|
return /* @__PURE__ */ jsx("div", {
|
|
215
|
-
className: twMerge("w-[11px] h-[11px] rounded-full bg-foreground animate-pulse-cursor ml-1", className),
|
|
220
|
+
className: twMerge("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
|
|
216
221
|
...props
|
|
217
222
|
});
|
|
218
223
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatMessageView.mjs","names":["CopilotChatAssistantMessage","CopilotChatUserMessage","CopilotChatReasoningMessage"],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"sourcesContent":["import React, { useEffect, useReducer } from \"react\";\nimport { WithSlots, renderSlot, isReactComponentType } from \"@/lib/slots\";\nimport CopilotChatAssistantMessage from \"./CopilotChatAssistantMessage\";\nimport CopilotChatUserMessage from \"./CopilotChatUserMessage\";\nimport CopilotChatReasoningMessage from \"./CopilotChatReasoningMessage\";\nimport {\n ActivityMessage,\n AssistantMessage,\n Message,\n ReasoningMessage,\n UserMessage,\n} from \"@ag-ui/core\";\nimport { twMerge } from \"tailwind-merge\";\nimport { useRenderActivityMessage, useRenderCustomMessages } from \"@/hooks\";\nimport { useCopilotKit } from \"@/providers/CopilotKitProvider\";\nimport { useCopilotChatConfiguration } from \"@/providers/CopilotChatConfigurationProvider\";\n\n/**\n * Memoized wrapper for assistant messages to prevent re-renders when other messages change.\n */\nconst MemoizedAssistantMessage = React.memo(\n function MemoizedAssistantMessage({\n message,\n messages,\n isRunning,\n AssistantMessageComponent,\n slotProps,\n }: {\n message: AssistantMessage;\n messages: Message[];\n isRunning: boolean;\n AssistantMessageComponent: typeof CopilotChatAssistantMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }) {\n return (\n <AssistantMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Compare tool calls if present\n const prevToolCalls = prevProps.message.toolCalls;\n const nextToolCalls = nextProps.message.toolCalls;\n if (prevToolCalls?.length !== nextToolCalls?.length) return false;\n if (prevToolCalls && nextToolCalls) {\n for (let i = 0; i < prevToolCalls.length; i++) {\n const prevTc = prevToolCalls[i];\n const nextTc = nextToolCalls[i];\n if (!prevTc || !nextTc) return false;\n if (prevTc.id !== nextTc.id) return false;\n if (prevTc.function.arguments !== nextTc.function.arguments)\n return false;\n }\n }\n\n // Check if tool results changed for this message's tool calls\n // Tool results are separate messages with role=\"tool\" that reference tool call IDs\n if (prevToolCalls && prevToolCalls.length > 0) {\n const toolCallIds = new Set(prevToolCalls.map((tc) => tc.id));\n\n const prevToolResults = prevProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n const nextToolResults = nextProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n\n // If number of tool results changed, re-render\n if (prevToolResults.length !== nextToolResults.length) return false;\n\n // If any tool result content changed, re-render\n for (let i = 0; i < prevToolResults.length; i++) {\n if (\n (prevToolResults[i] as any).content !==\n (nextToolResults[i] as any).content\n )\n return false;\n }\n }\n\n // Only care about isRunning if this message is CURRENTLY the latest\n // (we don't need to re-render just because a message stopped being the latest)\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.AssistantMessageComponent !==\n nextProps.AssistantMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for user messages to prevent re-renders when other messages change.\n */\nconst MemoizedUserMessage = React.memo(\n function MemoizedUserMessage({\n message,\n UserMessageComponent,\n slotProps,\n }: {\n message: UserMessage;\n UserMessageComponent: typeof CopilotChatUserMessage;\n slotProps?: Partial<React.ComponentProps<typeof CopilotChatUserMessage>>;\n }) {\n return <UserMessageComponent message={message} {...slotProps} />;\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.UserMessageComponent !== nextProps.UserMessageComponent)\n return false;\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n return true;\n },\n);\n\n/**\n * Memoized wrapper for activity messages to prevent re-renders when other messages change.\n */\nconst MemoizedActivityMessage = React.memo(\n function MemoizedActivityMessage({\n message,\n renderActivityMessage,\n }: {\n message: ActivityMessage;\n renderActivityMessage: (\n message: ActivityMessage,\n ) => React.ReactElement | null;\n }) {\n return renderActivityMessage(message);\n },\n (prevProps, nextProps) => {\n // Message ID changed = different message, must re-render\n if (prevProps.message.id !== nextProps.message.id) return false;\n\n // Activity type changed = must re-render\n if (prevProps.message.activityType !== nextProps.message.activityType)\n return false;\n\n // Compare content using JSON.stringify (native code, handles deep comparison)\n if (\n JSON.stringify(prevProps.message.content) !==\n JSON.stringify(nextProps.message.content)\n )\n return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for reasoning messages to prevent re-renders when other messages change.\n */\nconst MemoizedReasoningMessage = React.memo(\n function MemoizedReasoningMessage({\n message,\n messages,\n isRunning,\n ReasoningMessageComponent,\n slotProps,\n }: {\n message: ReasoningMessage;\n messages: Message[];\n isRunning: boolean;\n ReasoningMessageComponent: typeof CopilotChatReasoningMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }) {\n return (\n <ReasoningMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Re-render when \"latest\" status changes (e.g. reasoning message is no longer the last message\n // because a text message was added after it — this transitions isStreaming from true to false)\n const prevIsLatest =\n prevProps.messages[prevProps.messages.length - 1]?.id ===\n prevProps.message.id;\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (prevIsLatest !== nextIsLatest) return false;\n\n // Only care about isRunning if this message is CURRENTLY the latest\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.ReasoningMessageComponent !==\n nextProps.ReasoningMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for custom messages to prevent re-renders when other messages change.\n */\nconst MemoizedCustomMessage = React.memo(\n function MemoizedCustomMessage({\n message,\n position,\n renderCustomMessage,\n }: {\n message: Message;\n position: \"before\" | \"after\";\n renderCustomMessage: (params: {\n message: Message;\n position: \"before\" | \"after\";\n }) => React.ReactElement | null;\n stateSnapshot?: unknown;\n }) {\n return renderCustomMessage({ message, position });\n },\n (prevProps, nextProps) => {\n // Only re-render if the message or position changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.position !== nextProps.position) return false;\n // Compare message content - for assistant messages this is a string, for others may differ\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.message.role !== nextProps.message.role) return false;\n // Compare state snapshot - custom renderers may depend on state\n if (\n JSON.stringify(prevProps.stateSnapshot) !==\n JSON.stringify(nextProps.stateSnapshot)\n )\n return false;\n // Note: We don't compare renderCustomMessage function reference because it changes\n // frequently. The message and state comparison is sufficient to determine if a re-render is needed.\n return true;\n },\n);\n\nexport type CopilotChatMessageViewProps = Omit<\n WithSlots<\n {\n assistantMessage: typeof CopilotChatAssistantMessage;\n userMessage: typeof CopilotChatUserMessage;\n reasoningMessage: typeof CopilotChatReasoningMessage;\n cursor: typeof CopilotChatMessageView.Cursor;\n },\n {\n isRunning?: boolean;\n messages?: Message[];\n } & React.HTMLAttributes<HTMLDivElement>\n >,\n \"children\"\n> & {\n children?: (props: {\n isRunning: boolean;\n messages: Message[];\n messageElements: React.ReactElement[];\n }) => React.ReactElement;\n};\n\nexport function CopilotChatMessageView({\n messages = [],\n assistantMessage,\n userMessage,\n reasoningMessage,\n cursor,\n isRunning = false,\n children,\n className,\n ...props\n}: CopilotChatMessageViewProps) {\n const renderCustomMessage = useRenderCustomMessages();\n const { renderActivityMessage } = useRenderActivityMessage();\n const { copilotkit } = useCopilotKit();\n const config = useCopilotChatConfiguration();\n const [, forceUpdate] = useReducer((x) => x + 1, 0);\n\n // Subscribe to state changes so custom message renderers re-render when state updates.\n useEffect(() => {\n if (!config?.agentId) return;\n const agent = copilotkit.getAgent(config.agentId);\n if (!agent) return;\n\n const subscription = agent.subscribe({\n onStateChanged: forceUpdate,\n });\n return () => subscription.unsubscribe();\n }, [config?.agentId, copilotkit, forceUpdate]);\n\n // Helper to get state snapshot for a message (used for memoization)\n const getStateSnapshotForMessage = (messageId: string): unknown => {\n if (!config) return undefined;\n const resolvedRunId =\n copilotkit.getRunIdForMessage(\n config.agentId,\n config.threadId,\n messageId,\n ) ??\n copilotkit\n .getRunIdsForThread(config.agentId, config.threadId)\n .slice(-1)[0];\n if (!resolvedRunId) return undefined;\n return copilotkit.getStateByRun(\n config.agentId,\n config.threadId,\n resolvedRunId,\n );\n };\n\n const messageElements: React.ReactElement[] = messages\n .flatMap((message) => {\n const elements: (React.ReactElement | null | undefined)[] = [];\n const stateSnapshot = getStateSnapshotForMessage(message.id);\n\n // Render custom message before (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-before`}\n message={message}\n position=\"before\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n // Render the main message using memoized wrappers to prevent unnecessary re-renders\n if (message.role === \"assistant\") {\n // Determine the component and props from slot value\n let AssistantComponent = CopilotChatAssistantMessage;\n let assistantSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatAssistantMessage>>\n | undefined;\n\n if (isReactComponentType(assistantMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n AssistantComponent =\n assistantMessage as typeof CopilotChatAssistantMessage;\n } else if (typeof assistantMessage === \"string\") {\n // className string\n assistantSlotProps = { className: assistantMessage };\n } else if (assistantMessage && typeof assistantMessage === \"object\") {\n // Props object\n assistantSlotProps = assistantMessage as Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }\n\n elements.push(\n <MemoizedAssistantMessage\n key={message.id}\n message={message as AssistantMessage}\n messages={messages}\n isRunning={isRunning}\n AssistantMessageComponent={AssistantComponent}\n slotProps={assistantSlotProps}\n />,\n );\n } else if (message.role === \"user\") {\n // Determine the component and props from slot value\n let UserComponent = CopilotChatUserMessage;\n let userSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatUserMessage>>\n | undefined;\n\n if (isReactComponentType(userMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n UserComponent = userMessage as typeof CopilotChatUserMessage;\n } else if (typeof userMessage === \"string\") {\n // className string\n userSlotProps = { className: userMessage };\n } else if (userMessage && typeof userMessage === \"object\") {\n // Props object\n userSlotProps = userMessage as Partial<\n React.ComponentProps<typeof CopilotChatUserMessage>\n >;\n }\n\n elements.push(\n <MemoizedUserMessage\n key={message.id}\n message={message as UserMessage}\n UserMessageComponent={UserComponent}\n slotProps={userSlotProps}\n />,\n );\n } else if (message.role === \"activity\") {\n // Use memoized wrapper to prevent re-renders when other messages change\n const activityMsg = message as ActivityMessage;\n elements.push(\n <MemoizedActivityMessage\n key={message.id}\n message={activityMsg}\n renderActivityMessage={renderActivityMessage}\n />,\n );\n } else if (message.role === \"reasoning\") {\n // Determine the component and props from slot value\n let ReasoningComponent = CopilotChatReasoningMessage;\n let reasoningSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatReasoningMessage>>\n | undefined;\n\n if (isReactComponentType(reasoningMessage)) {\n ReasoningComponent =\n reasoningMessage as typeof CopilotChatReasoningMessage;\n } else if (typeof reasoningMessage === \"string\") {\n reasoningSlotProps = { className: reasoningMessage };\n } else if (reasoningMessage && typeof reasoningMessage === \"object\") {\n reasoningSlotProps = reasoningMessage as Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }\n\n elements.push(\n <MemoizedReasoningMessage\n key={message.id}\n message={message as ReasoningMessage}\n messages={messages}\n isRunning={isRunning}\n ReasoningMessageComponent={ReasoningComponent}\n slotProps={reasoningSlotProps}\n />,\n );\n }\n\n // Render custom message after (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-after`}\n message={message}\n position=\"after\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n return elements;\n })\n .filter(Boolean) as React.ReactElement[];\n\n if (children) {\n return children({ messageElements, messages, isRunning });\n }\n\n // Hide the chat-level loading cursor when the last message is a reasoning\n // message — the reasoning card already shows its own loading indicator.\n const lastMessage = messages[messages.length - 1];\n const showCursor = isRunning && lastMessage?.role !== \"reasoning\";\n\n return (\n <div className={twMerge(\"flex flex-col\", className)} {...props}>\n {messageElements}\n {showCursor && (\n <div className=\"mt-2\">\n {renderSlot(cursor, CopilotChatMessageView.Cursor, {})}\n </div>\n )}\n </div>\n );\n}\n\nCopilotChatMessageView.Cursor = function Cursor({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={twMerge(\n \"w-[11px] h-[11px] rounded-full bg-foreground animate-pulse-cursor ml-1\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport default CopilotChatMessageView;\n"],"mappings":";;;;;;;;;;;;;;;;;AAoBA,MAAM,2BAA2B,MAAM,KACrC,SAAS,yBAAyB,EAChC,SACA,UACA,WACA,2BACA,aASC;AACD,QACE,oBAAC;EACU;EACC;EACC;EACX,GAAI;GACJ;IAGL,WAAW,cAAc;AAExB,KAAI,UAAU,QAAQ,OAAO,UAAU,QAAQ,GAAI,QAAO;AAC1D,KAAI,UAAU,QAAQ,YAAY,UAAU,QAAQ,QAAS,QAAO;CAGpE,MAAM,gBAAgB,UAAU,QAAQ;CACxC,MAAM,gBAAgB,UAAU,QAAQ;AACxC,KAAI,eAAe,WAAW,eAAe,OAAQ,QAAO;AAC5D,KAAI,iBAAiB,cACnB,MAAK,IAAI,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;EAC7C,MAAM,SAAS,cAAc;EAC7B,MAAM,SAAS,cAAc;AAC7B,MAAI,CAAC,UAAU,CAAC,OAAQ,QAAO;AAC/B,MAAI,OAAO,OAAO,OAAO,GAAI,QAAO;AACpC,MAAI,OAAO,SAAS,cAAc,OAAO,SAAS,UAChD,QAAO;;AAMb,KAAI,iBAAiB,cAAc,SAAS,GAAG;EAC7C,MAAM,cAAc,IAAI,IAAI,cAAc,KAAK,OAAO,GAAG,GAAG,CAAC;EAE7D,MAAM,kBAAkB,UAAU,SAAS,QACxC,MAAM,EAAE,SAAS,UAAU,YAAY,IAAK,EAAU,WAAW,CACnE;EACD,MAAM,kBAAkB,UAAU,SAAS,QACxC,MAAM,EAAE,SAAS,UAAU,YAAY,IAAK,EAAU,WAAW,CACnE;AAGD,MAAI,gBAAgB,WAAW,gBAAgB,OAAQ,QAAO;AAG9D,OAAK,IAAI,IAAI,GAAG,IAAI,gBAAgB,QAAQ,IAC1C,KACG,gBAAgB,GAAW,YAC3B,gBAAgB,GAAW,QAE5B,QAAO;;AASb,KAFE,UAAU,SAAS,UAAU,SAAS,SAAS,IAAI,OACnD,UAAU,QAAQ,MACA,UAAU,cAAc,UAAU,UACpD,QAAO;AAGT,KACE,UAAU,8BACV,UAAU,0BAEV,QAAO;AAGT,KAAI,UAAU,cAAc,UAAU,UAAW,QAAO;AAExD,QAAO;EAEV;;;;AAKD,MAAM,sBAAsB,MAAM,KAChC,SAAS,oBAAoB,EAC3B,SACA,sBACA,aAKC;AACD,QAAO,oBAAC;EAA8B;EAAS,GAAI;GAAa;IAEjE,WAAW,cAAc;AAExB,KAAI,UAAU,QAAQ,OAAO,UAAU,QAAQ,GAAI,QAAO;AAC1D,KAAI,UAAU,QAAQ,YAAY,UAAU,QAAQ,QAAS,QAAO;AACpE,KAAI,UAAU,yBAAyB,UAAU,qBAC/C,QAAO;AAET,KAAI,UAAU,cAAc,UAAU,UAAW,QAAO;AACxD,QAAO;EAEV;;;;AAKD,MAAM,0BAA0B,MAAM,KACpC,SAAS,wBAAwB,EAC/B,SACA,yBAMC;AACD,QAAO,sBAAsB,QAAQ;IAEtC,WAAW,cAAc;AAExB,KAAI,UAAU,QAAQ,OAAO,UAAU,QAAQ,GAAI,QAAO;AAG1D,KAAI,UAAU,QAAQ,iBAAiB,UAAU,QAAQ,aACvD,QAAO;AAGT,KACE,KAAK,UAAU,UAAU,QAAQ,QAAQ,KACzC,KAAK,UAAU,UAAU,QAAQ,QAAQ,CAEzC,QAAO;AAET,QAAO;EAEV;;;;AAKD,MAAM,2BAA2B,MAAM,KACrC,SAAS,yBAAyB,EAChC,SACA,UACA,WACA,2BACA,aASC;AACD,QACE,oBAAC;EACU;EACC;EACC;EACX,GAAI;GACJ;IAGL,WAAW,cAAc;AAExB,KAAI,UAAU,QAAQ,OAAO,UAAU,QAAQ,GAAI,QAAO;AAC1D,KAAI,UAAU,QAAQ,YAAY,UAAU,QAAQ,QAAS,QAAO;CAIpE,MAAM,eACJ,UAAU,SAAS,UAAU,SAAS,SAAS,IAAI,OACnD,UAAU,QAAQ;CACpB,MAAM,eACJ,UAAU,SAAS,UAAU,SAAS,SAAS,IAAI,OACnD,UAAU,QAAQ;AACpB,KAAI,iBAAiB,aAAc,QAAO;AAG1C,KAAI,gBAAgB,UAAU,cAAc,UAAU,UACpD,QAAO;AAGT,KACE,UAAU,8BACV,UAAU,0BAEV,QAAO;AAGT,KAAI,UAAU,cAAc,UAAU,UAAW,QAAO;AAExD,QAAO;EAEV;;;;AAKD,MAAM,wBAAwB,MAAM,KAClC,SAAS,sBAAsB,EAC7B,SACA,UACA,uBASC;AACD,QAAO,oBAAoB;EAAE;EAAS;EAAU,CAAC;IAElD,WAAW,cAAc;AAExB,KAAI,UAAU,QAAQ,OAAO,UAAU,QAAQ,GAAI,QAAO;AAC1D,KAAI,UAAU,aAAa,UAAU,SAAU,QAAO;AAEtD,KAAI,UAAU,QAAQ,YAAY,UAAU,QAAQ,QAAS,QAAO;AACpE,KAAI,UAAU,QAAQ,SAAS,UAAU,QAAQ,KAAM,QAAO;AAE9D,KACE,KAAK,UAAU,UAAU,cAAc,KACvC,KAAK,UAAU,UAAU,cAAc,CAEvC,QAAO;AAGT,QAAO;EAEV;AAwBD,SAAgB,uBAAuB,EACrC,WAAW,EAAE,EACb,kBACA,aACA,kBACA,QACA,YAAY,OACZ,UACA,WACA,GAAG,SAC2B;CAC9B,MAAM,sBAAsB,yBAAyB;CACrD,MAAM,EAAE,0BAA0B,0BAA0B;CAC5D,MAAM,EAAE,eAAe,eAAe;CACtC,MAAM,SAAS,6BAA6B;CAC5C,MAAM,GAAG,eAAe,YAAY,MAAM,IAAI,GAAG,EAAE;AAGnD,iBAAgB;AACd,MAAI,CAAC,QAAQ,QAAS;EACtB,MAAM,QAAQ,WAAW,SAAS,OAAO,QAAQ;AACjD,MAAI,CAAC,MAAO;EAEZ,MAAM,eAAe,MAAM,UAAU,EACnC,gBAAgB,aACjB,CAAC;AACF,eAAa,aAAa,aAAa;IACtC;EAAC,QAAQ;EAAS;EAAY;EAAY,CAAC;CAG9C,MAAM,8BAA8B,cAA+B;AACjE,MAAI,CAAC,OAAQ,QAAO;EACpB,MAAM,gBACJ,WAAW,mBACT,OAAO,SACP,OAAO,UACP,UACD,IACD,WACG,mBAAmB,OAAO,SAAS,OAAO,SAAS,CACnD,MAAM,GAAG,CAAC;AACf,MAAI,CAAC,cAAe,QAAO;AAC3B,SAAO,WAAW,cAChB,OAAO,SACP,OAAO,UACP,cACD;;CAGH,MAAM,kBAAwC,SAC3C,SAAS,YAAY;EACpB,MAAM,WAAsD,EAAE;EAC9D,MAAM,gBAAgB,2BAA2B,QAAQ,GAAG;AAG5D,MAAI,oBACF,UAAS,KACP,oBAAC;GAEU;GACT,UAAS;GACY;GACN;KAJV,GAAG,QAAQ,GAAG,gBAKnB,CACH;AAIH,MAAI,QAAQ,SAAS,aAAa;GAEhC,IAAI,qBAAqBA;GACzB,IAAI;AAIJ,OAAI,qBAAqB,iBAAiB,CAExC,sBACE;YACO,OAAO,qBAAqB,SAErC,sBAAqB,EAAE,WAAW,kBAAkB;YAC3C,oBAAoB,OAAO,qBAAqB,SAEzD,sBAAqB;AAKvB,YAAS,KACP,oBAAC;IAEU;IACC;IACC;IACX,2BAA2B;IAC3B,WAAW;MALN,QAAQ,GAMb,CACH;aACQ,QAAQ,SAAS,QAAQ;GAElC,IAAI,gBAAgBC;GACpB,IAAI;AAIJ,OAAI,qBAAqB,YAAY,CAEnC,iBAAgB;YACP,OAAO,gBAAgB,SAEhC,iBAAgB,EAAE,WAAW,aAAa;YACjC,eAAe,OAAO,gBAAgB,SAE/C,iBAAgB;AAKlB,YAAS,KACP,oBAAC;IAEU;IACT,sBAAsB;IACtB,WAAW;MAHN,QAAQ,GAIb,CACH;aACQ,QAAQ,SAAS,YAAY;GAEtC,MAAM,cAAc;AACpB,YAAS,KACP,oBAAC;IAEC,SAAS;IACc;MAFlB,QAAQ,GAGb,CACH;aACQ,QAAQ,SAAS,aAAa;GAEvC,IAAI,qBAAqBC;GACzB,IAAI;AAIJ,OAAI,qBAAqB,iBAAiB,CACxC,sBACE;YACO,OAAO,qBAAqB,SACrC,sBAAqB,EAAE,WAAW,kBAAkB;YAC3C,oBAAoB,OAAO,qBAAqB,SACzD,sBAAqB;AAKvB,YAAS,KACP,oBAAC;IAEU;IACC;IACC;IACX,2BAA2B;IAC3B,WAAW;MALN,QAAQ,GAMb,CACH;;AAIH,MAAI,oBACF,UAAS,KACP,oBAAC;GAEU;GACT,UAAS;GACY;GACN;KAJV,GAAG,QAAQ,GAAG,eAKnB,CACH;AAGH,SAAO;GACP,CACD,OAAO,QAAQ;AAElB,KAAI,SACF,QAAO,SAAS;EAAE;EAAiB;EAAU;EAAW,CAAC;CAK3D,MAAM,cAAc,SAAS,SAAS,SAAS;CAC/C,MAAM,aAAa,aAAa,aAAa,SAAS;AAEtD,QACE,qBAAC;EAAI,WAAW,QAAQ,iBAAiB,UAAU;EAAE,GAAI;aACtD,iBACA,cACC,oBAAC;GAAI,WAAU;aACZ,WAAW,QAAQ,uBAAuB,QAAQ,EAAE,CAAC;IAClD;GAEJ;;AAIV,uBAAuB,SAAS,SAAS,OAAO,EAC9C,WACA,GAAG,SACoC;AACvC,QACE,oBAAC;EACC,WAAW,QACT,0EACA,UACD;EACD,GAAI;GACJ"}
|
|
1
|
+
{"version":3,"file":"CopilotChatMessageView.mjs","names":["CopilotChatAssistantMessage","CopilotChatUserMessage","CopilotChatReasoningMessage"],"sources":["../../../src/components/chat/CopilotChatMessageView.tsx"],"sourcesContent":["import React, { useEffect, useReducer } from \"react\";\nimport { WithSlots, renderSlot, isReactComponentType } from \"@/lib/slots\";\nimport CopilotChatAssistantMessage from \"./CopilotChatAssistantMessage\";\nimport CopilotChatUserMessage from \"./CopilotChatUserMessage\";\nimport CopilotChatReasoningMessage from \"./CopilotChatReasoningMessage\";\nimport {\n ActivityMessage,\n AssistantMessage,\n Message,\n ReasoningMessage,\n UserMessage,\n} from \"@ag-ui/core\";\nimport { twMerge } from \"tailwind-merge\";\nimport { useRenderActivityMessage, useRenderCustomMessages } from \"@/hooks\";\nimport { useCopilotKit } from \"@/providers/CopilotKitProvider\";\nimport { useCopilotChatConfiguration } from \"@/providers/CopilotChatConfigurationProvider\";\n\n/**\n * Memoized wrapper for assistant messages to prevent re-renders when other messages change.\n */\nconst MemoizedAssistantMessage = React.memo(\n function MemoizedAssistantMessage({\n message,\n messages,\n isRunning,\n AssistantMessageComponent,\n slotProps,\n }: {\n message: AssistantMessage;\n messages: Message[];\n isRunning: boolean;\n AssistantMessageComponent: typeof CopilotChatAssistantMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }) {\n return (\n <AssistantMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Compare tool calls if present\n const prevToolCalls = prevProps.message.toolCalls;\n const nextToolCalls = nextProps.message.toolCalls;\n if (prevToolCalls?.length !== nextToolCalls?.length) return false;\n if (prevToolCalls && nextToolCalls) {\n for (let i = 0; i < prevToolCalls.length; i++) {\n const prevTc = prevToolCalls[i];\n const nextTc = nextToolCalls[i];\n if (!prevTc || !nextTc) return false;\n if (prevTc.id !== nextTc.id) return false;\n if (prevTc.function.arguments !== nextTc.function.arguments)\n return false;\n }\n }\n\n // Check if tool results changed for this message's tool calls\n // Tool results are separate messages with role=\"tool\" that reference tool call IDs\n if (prevToolCalls && prevToolCalls.length > 0) {\n const toolCallIds = new Set(prevToolCalls.map((tc) => tc.id));\n\n const prevToolResults = prevProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n const nextToolResults = nextProps.messages.filter(\n (m) => m.role === \"tool\" && toolCallIds.has((m as any).toolCallId),\n );\n\n // If number of tool results changed, re-render\n if (prevToolResults.length !== nextToolResults.length) return false;\n\n // If any tool result content changed, re-render\n for (let i = 0; i < prevToolResults.length; i++) {\n if (\n (prevToolResults[i] as any).content !==\n (nextToolResults[i] as any).content\n )\n return false;\n }\n }\n\n // Only care about isRunning if this message is CURRENTLY the latest\n // (we don't need to re-render just because a message stopped being the latest)\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.AssistantMessageComponent !==\n nextProps.AssistantMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for user messages to prevent re-renders when other messages change.\n */\nconst MemoizedUserMessage = React.memo(\n function MemoizedUserMessage({\n message,\n UserMessageComponent,\n slotProps,\n }: {\n message: UserMessage;\n UserMessageComponent: typeof CopilotChatUserMessage;\n slotProps?: Partial<React.ComponentProps<typeof CopilotChatUserMessage>>;\n }) {\n return <UserMessageComponent message={message} {...slotProps} />;\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.UserMessageComponent !== nextProps.UserMessageComponent)\n return false;\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n return true;\n },\n);\n\n/**\n * Memoized wrapper for activity messages to prevent re-renders when other messages change.\n */\nconst MemoizedActivityMessage = React.memo(\n function MemoizedActivityMessage({\n message,\n renderActivityMessage,\n }: {\n message: ActivityMessage;\n renderActivityMessage: (\n message: ActivityMessage,\n ) => React.ReactElement | null;\n }) {\n return renderActivityMessage(message);\n },\n (prevProps, nextProps) => {\n // Message ID changed = different message, must re-render\n if (prevProps.message.id !== nextProps.message.id) return false;\n\n // Activity type changed = must re-render\n if (prevProps.message.activityType !== nextProps.message.activityType)\n return false;\n\n // Compare content using JSON.stringify (native code, handles deep comparison)\n if (\n JSON.stringify(prevProps.message.content) !==\n JSON.stringify(nextProps.message.content)\n )\n return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for reasoning messages to prevent re-renders when other messages change.\n */\nconst MemoizedReasoningMessage = React.memo(\n function MemoizedReasoningMessage({\n message,\n messages,\n isRunning,\n ReasoningMessageComponent,\n slotProps,\n }: {\n message: ReasoningMessage;\n messages: Message[];\n isRunning: boolean;\n ReasoningMessageComponent: typeof CopilotChatReasoningMessage;\n slotProps?: Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }) {\n return (\n <ReasoningMessageComponent\n message={message}\n messages={messages}\n isRunning={isRunning}\n {...slotProps}\n />\n );\n },\n (prevProps, nextProps) => {\n // Only re-render if this specific message changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.message.content !== nextProps.message.content) return false;\n\n // Re-render when \"latest\" status changes (e.g. reasoning message is no longer the last message\n // because a text message was added after it — this transitions isStreaming from true to false)\n const prevIsLatest =\n prevProps.messages[prevProps.messages.length - 1]?.id ===\n prevProps.message.id;\n const nextIsLatest =\n nextProps.messages[nextProps.messages.length - 1]?.id ===\n nextProps.message.id;\n if (prevIsLatest !== nextIsLatest) return false;\n\n // Only care about isRunning if this message is CURRENTLY the latest\n if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning)\n return false;\n\n // Check if component reference changed\n if (\n prevProps.ReasoningMessageComponent !==\n nextProps.ReasoningMessageComponent\n )\n return false;\n\n // Check if slot props changed\n if (prevProps.slotProps !== nextProps.slotProps) return false;\n\n return true;\n },\n);\n\n/**\n * Memoized wrapper for custom messages to prevent re-renders when other messages change.\n */\nconst MemoizedCustomMessage = React.memo(\n function MemoizedCustomMessage({\n message,\n position,\n renderCustomMessage,\n }: {\n message: Message;\n position: \"before\" | \"after\";\n renderCustomMessage: (params: {\n message: Message;\n position: \"before\" | \"after\";\n }) => React.ReactElement | null;\n stateSnapshot?: unknown;\n }) {\n return renderCustomMessage({ message, position });\n },\n (prevProps, nextProps) => {\n // Only re-render if the message or position changed\n if (prevProps.message.id !== nextProps.message.id) return false;\n if (prevProps.position !== nextProps.position) return false;\n // Compare message content - for assistant messages this is a string, for others may differ\n if (prevProps.message.content !== nextProps.message.content) return false;\n if (prevProps.message.role !== nextProps.message.role) return false;\n // Compare state snapshot - custom renderers may depend on state\n if (\n JSON.stringify(prevProps.stateSnapshot) !==\n JSON.stringify(nextProps.stateSnapshot)\n )\n return false;\n // Note: We don't compare renderCustomMessage function reference because it changes\n // frequently. The message and state comparison is sufficient to determine if a re-render is needed.\n return true;\n },\n);\n\nexport type CopilotChatMessageViewProps = Omit<\n WithSlots<\n {\n assistantMessage: typeof CopilotChatAssistantMessage;\n userMessage: typeof CopilotChatUserMessage;\n reasoningMessage: typeof CopilotChatReasoningMessage;\n cursor: typeof CopilotChatMessageView.Cursor;\n },\n {\n isRunning?: boolean;\n messages?: Message[];\n } & React.HTMLAttributes<HTMLDivElement>\n >,\n \"children\"\n> & {\n children?: (props: {\n isRunning: boolean;\n messages: Message[];\n messageElements: React.ReactElement[];\n }) => React.ReactElement;\n};\n\nexport function CopilotChatMessageView({\n messages = [],\n assistantMessage,\n userMessage,\n reasoningMessage,\n cursor,\n isRunning = false,\n children,\n className,\n ...props\n}: CopilotChatMessageViewProps) {\n const renderCustomMessage = useRenderCustomMessages();\n const { renderActivityMessage } = useRenderActivityMessage();\n const { copilotkit } = useCopilotKit();\n const config = useCopilotChatConfiguration();\n const [, forceUpdate] = useReducer((x) => x + 1, 0);\n\n // Subscribe to state changes so custom message renderers re-render when state updates.\n useEffect(() => {\n if (!config?.agentId) return;\n const agent = copilotkit.getAgent(config.agentId);\n if (!agent) return;\n\n const subscription = agent.subscribe({\n onStateChanged: forceUpdate,\n });\n return () => subscription.unsubscribe();\n }, [config?.agentId, copilotkit, forceUpdate]);\n\n // Helper to get state snapshot for a message (used for memoization)\n const getStateSnapshotForMessage = (messageId: string): unknown => {\n if (!config) return undefined;\n const resolvedRunId =\n copilotkit.getRunIdForMessage(\n config.agentId,\n config.threadId,\n messageId,\n ) ??\n copilotkit\n .getRunIdsForThread(config.agentId, config.threadId)\n .slice(-1)[0];\n if (!resolvedRunId) return undefined;\n return copilotkit.getStateByRun(\n config.agentId,\n config.threadId,\n resolvedRunId,\n );\n };\n\n const messageElements: React.ReactElement[] = messages\n .flatMap((message) => {\n const elements: (React.ReactElement | null | undefined)[] = [];\n const stateSnapshot = getStateSnapshotForMessage(message.id);\n\n // Render custom message before (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-before`}\n message={message}\n position=\"before\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n // Render the main message using memoized wrappers to prevent unnecessary re-renders\n if (message.role === \"assistant\") {\n // Determine the component and props from slot value\n let AssistantComponent = CopilotChatAssistantMessage;\n let assistantSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatAssistantMessage>>\n | undefined;\n\n if (isReactComponentType(assistantMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n AssistantComponent =\n assistantMessage as typeof CopilotChatAssistantMessage;\n } else if (typeof assistantMessage === \"string\") {\n // className string\n assistantSlotProps = { className: assistantMessage };\n } else if (assistantMessage && typeof assistantMessage === \"object\") {\n // Props object\n assistantSlotProps = assistantMessage as Partial<\n React.ComponentProps<typeof CopilotChatAssistantMessage>\n >;\n }\n\n elements.push(\n <MemoizedAssistantMessage\n key={message.id}\n message={message as AssistantMessage}\n messages={messages}\n isRunning={isRunning}\n AssistantMessageComponent={AssistantComponent}\n slotProps={assistantSlotProps}\n />,\n );\n } else if (message.role === \"user\") {\n // Determine the component and props from slot value\n let UserComponent = CopilotChatUserMessage;\n let userSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatUserMessage>>\n | undefined;\n\n if (isReactComponentType(userMessage)) {\n // Custom component (function, forwardRef, memo, etc.)\n UserComponent = userMessage as typeof CopilotChatUserMessage;\n } else if (typeof userMessage === \"string\") {\n // className string\n userSlotProps = { className: userMessage };\n } else if (userMessage && typeof userMessage === \"object\") {\n // Props object\n userSlotProps = userMessage as Partial<\n React.ComponentProps<typeof CopilotChatUserMessage>\n >;\n }\n\n elements.push(\n <MemoizedUserMessage\n key={message.id}\n message={message as UserMessage}\n UserMessageComponent={UserComponent}\n slotProps={userSlotProps}\n />,\n );\n } else if (message.role === \"activity\") {\n // Use memoized wrapper to prevent re-renders when other messages change\n const activityMsg = message as ActivityMessage;\n elements.push(\n <MemoizedActivityMessage\n key={message.id}\n message={activityMsg}\n renderActivityMessage={renderActivityMessage}\n />,\n );\n } else if (message.role === \"reasoning\") {\n // Determine the component and props from slot value\n let ReasoningComponent = CopilotChatReasoningMessage;\n let reasoningSlotProps:\n | Partial<React.ComponentProps<typeof CopilotChatReasoningMessage>>\n | undefined;\n\n if (isReactComponentType(reasoningMessage)) {\n ReasoningComponent =\n reasoningMessage as typeof CopilotChatReasoningMessage;\n } else if (typeof reasoningMessage === \"string\") {\n reasoningSlotProps = { className: reasoningMessage };\n } else if (reasoningMessage && typeof reasoningMessage === \"object\") {\n reasoningSlotProps = reasoningMessage as Partial<\n React.ComponentProps<typeof CopilotChatReasoningMessage>\n >;\n }\n\n elements.push(\n <MemoizedReasoningMessage\n key={message.id}\n message={message as ReasoningMessage}\n messages={messages}\n isRunning={isRunning}\n ReasoningMessageComponent={ReasoningComponent}\n slotProps={reasoningSlotProps}\n />,\n );\n }\n\n // Render custom message after (using memoized wrapper)\n if (renderCustomMessage) {\n elements.push(\n <MemoizedCustomMessage\n key={`${message.id}-custom-after`}\n message={message}\n position=\"after\"\n renderCustomMessage={renderCustomMessage}\n stateSnapshot={stateSnapshot}\n />,\n );\n }\n\n return elements;\n })\n .filter(Boolean) as React.ReactElement[];\n\n if (children) {\n return (\n <div data-copilotkit style={{ display: \"contents\" }}>\n {children({ messageElements, messages, isRunning })}\n </div>\n );\n }\n\n // Hide the chat-level loading cursor when the last message is a reasoning\n // message — the reasoning card already shows its own loading indicator.\n const lastMessage = messages[messages.length - 1];\n const showCursor = isRunning && lastMessage?.role !== \"reasoning\";\n\n return (\n <div\n data-copilotkit\n className={twMerge(\"cpk:flex cpk:flex-col\", className)}\n {...props}\n >\n {messageElements}\n {showCursor && (\n <div className=\"cpk:mt-2\">\n {renderSlot(cursor, CopilotChatMessageView.Cursor, {})}\n </div>\n )}\n </div>\n );\n}\n\nCopilotChatMessageView.Cursor = function Cursor({\n className,\n ...props\n}: React.HTMLAttributes<HTMLDivElement>) {\n return (\n <div\n className={twMerge(\n \"cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1\",\n className,\n )}\n {...props}\n />\n );\n};\n\nexport default CopilotChatMessageView;\n"],"mappings":"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"}
|
|
@@ -58,16 +58,20 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, header, con
|
|
|
58
58
|
isOpen,
|
|
59
59
|
children: boundContent
|
|
60
60
|
});
|
|
61
|
-
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
62
|
+
"data-copilotkit": true,
|
|
63
|
+
style: { display: "contents" },
|
|
64
|
+
children: children({
|
|
65
|
+
header: boundHeader,
|
|
66
|
+
contentView: boundContent,
|
|
67
|
+
toggle: boundToggle,
|
|
68
|
+
message,
|
|
69
|
+
messages,
|
|
70
|
+
isRunning
|
|
71
|
+
})
|
|
72
|
+
});
|
|
69
73
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
70
|
-
className: (0, tailwind_merge.twMerge)("my-1", className),
|
|
74
|
+
className: (0, tailwind_merge.twMerge)("cpk:my-1", className),
|
|
71
75
|
"data-message-id": message.id,
|
|
72
76
|
...props,
|
|
73
77
|
children: [boundHeader, boundToggle]
|
|
@@ -78,44 +82,44 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, header, con
|
|
|
78
82
|
const isExpandable = !!hasContent;
|
|
79
83
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
80
84
|
type: "button",
|
|
81
|
-
className: (0, tailwind_merge.twMerge)("inline-flex items-center gap-1 py-1 text-sm text-muted-foreground transition-colors select-none", isExpandable ? "hover:text-foreground cursor-pointer" : "cursor-default", className),
|
|
85
|
+
className: (0, tailwind_merge.twMerge)("cpk:inline-flex cpk:items-center cpk:gap-1 cpk:py-1 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:select-none", isExpandable ? "cpk:hover:text-foreground cpk:cursor-pointer" : "cpk:cursor-default", className),
|
|
82
86
|
"aria-expanded": isExpandable ? isOpen : void 0,
|
|
83
87
|
...headerProps,
|
|
84
88
|
children: [
|
|
85
89
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
86
|
-
className: "font-medium",
|
|
90
|
+
className: "cpk:font-medium",
|
|
87
91
|
children: label
|
|
88
92
|
}),
|
|
89
93
|
isStreaming && !hasContent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
90
|
-
className: "inline-flex items-center ml-1",
|
|
91
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w-1.5 h-1.5 rounded-full bg-muted-foreground animate-pulse" })
|
|
94
|
+
className: "cpk:inline-flex cpk:items-center cpk:ml-1",
|
|
95
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cpk:w-1.5 cpk:h-1.5 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse" })
|
|
92
96
|
}),
|
|
93
97
|
headerChildren,
|
|
94
|
-
isExpandable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { className: (0, tailwind_merge.twMerge)("size-3.5 shrink-0 transition-transform duration-200", isOpen && "rotate-90") })
|
|
98
|
+
isExpandable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRight, { className: (0, tailwind_merge.twMerge)("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", isOpen && "cpk:rotate-90") })
|
|
95
99
|
]
|
|
96
100
|
});
|
|
97
101
|
};
|
|
98
102
|
_CopilotChatReasoningMessage.Content = ({ isStreaming, hasContent, className, children: contentChildren, ...contentProps }) => {
|
|
99
103
|
if (!hasContent && !isStreaming) return null;
|
|
100
104
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
101
|
-
className: (0, tailwind_merge.twMerge)("pb-2 pt-1", className),
|
|
105
|
+
className: (0, tailwind_merge.twMerge)("cpk:pb-2 cpk:pt-1", className),
|
|
102
106
|
...contentProps,
|
|
103
107
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
104
|
-
className: "text-sm text-muted-foreground",
|
|
108
|
+
className: "cpk:text-sm cpk:text-muted-foreground",
|
|
105
109
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(streamdown.Streamdown, { children: typeof contentChildren === "string" ? contentChildren : "" }), isStreaming && hasContent && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
106
|
-
className: "inline-flex items-center ml-1 align-middle",
|
|
107
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "w-2 h-2 rounded-full bg-muted-foreground animate-pulse-cursor" })
|
|
110
|
+
className: "cpk:inline-flex cpk:items-center cpk:ml-1 cpk:align-middle",
|
|
111
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "cpk:w-2 cpk:h-2 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse-cursor" })
|
|
108
112
|
})]
|
|
109
113
|
})
|
|
110
114
|
});
|
|
111
115
|
};
|
|
112
116
|
_CopilotChatReasoningMessage.Toggle = ({ isOpen, className, children: toggleChildren, ...toggleProps }) => {
|
|
113
117
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
114
|
-
className: (0, tailwind_merge.twMerge)("grid transition-[grid-template-rows] duration-200 ease-in-out", className),
|
|
118
|
+
className: (0, tailwind_merge.twMerge)("cpk:grid cpk:transition-[grid-template-rows] cpk:duration-200 cpk:ease-in-out", className),
|
|
115
119
|
style: { gridTemplateRows: isOpen ? "1fr" : "0fr" },
|
|
116
120
|
...toggleProps,
|
|
117
121
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
118
|
-
className: "overflow-hidden",
|
|
122
|
+
className: "cpk:overflow-hidden",
|
|
119
123
|
children: toggleChildren
|
|
120
124
|
})
|
|
121
125
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatReasoningMessage.cjs","names":["renderSlot","ChevronRight","Streamdown"],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"sourcesContent":["import { ReasoningMessage, Message } from \"@ag-ui/core\";\nimport { useState, useEffect, useRef } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Streamdown } from \"streamdown\";\nimport { WithSlots, renderSlot } from \"@/lib/slots\";\n\nexport type CopilotChatReasoningMessageProps = WithSlots<\n {\n header: typeof CopilotChatReasoningMessage.Header;\n contentView: typeof CopilotChatReasoningMessage.Content;\n toggle: typeof CopilotChatReasoningMessage.Toggle;\n },\n {\n message: ReasoningMessage;\n messages?: Message[];\n isRunning?: boolean;\n } & React.HTMLAttributes<HTMLDivElement>\n>;\n\n/**\n * Formats an elapsed duration (in seconds) to a human-readable string.\n */\nfunction formatDuration(seconds: number): string {\n if (seconds < 1) return \"a few seconds\";\n if (seconds < 60) return `${Math.round(seconds)} seconds`;\n const mins = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n if (secs === 0) return `${mins} minute${mins > 1 ? \"s\" : \"\"}`;\n return `${mins}m ${secs}s`;\n}\n\nexport function CopilotChatReasoningMessage({\n message,\n messages,\n isRunning,\n header,\n contentView,\n toggle,\n children,\n className,\n ...props\n}: CopilotChatReasoningMessageProps) {\n const isLatest = messages?.[messages.length - 1]?.id === message.id;\n const isStreaming = !!(isRunning && isLatest);\n const hasContent = !!(message.content && message.content.length > 0);\n\n // Track elapsed time while streaming\n const startTimeRef = useRef<number | null>(null);\n const [elapsed, setElapsed] = useState(0);\n\n useEffect(() => {\n if (isStreaming && startTimeRef.current === null) {\n startTimeRef.current = Date.now();\n }\n\n if (!isStreaming && startTimeRef.current !== null) {\n // Final snapshot of elapsed time\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n return;\n }\n\n if (!isStreaming) return;\n\n // Tick every second while streaming\n const timer = setInterval(() => {\n if (startTimeRef.current !== null) {\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n }\n }, 1000);\n return () => clearInterval(timer);\n }, [isStreaming]);\n\n // Default to open while streaming, auto-collapse when streaming ends\n const [isOpen, setIsOpen] = useState(isStreaming);\n\n useEffect(() => {\n if (isStreaming) {\n setIsOpen(true);\n } else {\n // Auto-collapse when reasoning finishes\n setIsOpen(false);\n }\n }, [isStreaming]);\n\n const label = isStreaming\n ? \"Thinking…\"\n : `Thought for ${formatDuration(elapsed)}`;\n\n const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {\n isOpen,\n label,\n hasContent,\n isStreaming,\n onClick: hasContent ? () => setIsOpen((prev) => !prev) : undefined,\n });\n\n const boundContent = renderSlot(\n contentView,\n CopilotChatReasoningMessage.Content,\n {\n isStreaming,\n hasContent,\n children: message.content,\n },\n );\n\n const boundToggle = renderSlot(toggle, CopilotChatReasoningMessage.Toggle, {\n isOpen,\n children: boundContent,\n });\n\n if (children) {\n return (\n
|
|
1
|
+
{"version":3,"file":"CopilotChatReasoningMessage.cjs","names":["renderSlot","ChevronRight","Streamdown"],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"sourcesContent":["import { ReasoningMessage, Message } from \"@ag-ui/core\";\nimport { useState, useEffect, useRef } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Streamdown } from \"streamdown\";\nimport { WithSlots, renderSlot } from \"@/lib/slots\";\n\nexport type CopilotChatReasoningMessageProps = WithSlots<\n {\n header: typeof CopilotChatReasoningMessage.Header;\n contentView: typeof CopilotChatReasoningMessage.Content;\n toggle: typeof CopilotChatReasoningMessage.Toggle;\n },\n {\n message: ReasoningMessage;\n messages?: Message[];\n isRunning?: boolean;\n } & React.HTMLAttributes<HTMLDivElement>\n>;\n\n/**\n * Formats an elapsed duration (in seconds) to a human-readable string.\n */\nfunction formatDuration(seconds: number): string {\n if (seconds < 1) return \"a few seconds\";\n if (seconds < 60) return `${Math.round(seconds)} seconds`;\n const mins = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n if (secs === 0) return `${mins} minute${mins > 1 ? \"s\" : \"\"}`;\n return `${mins}m ${secs}s`;\n}\n\nexport function CopilotChatReasoningMessage({\n message,\n messages,\n isRunning,\n header,\n contentView,\n toggle,\n children,\n className,\n ...props\n}: CopilotChatReasoningMessageProps) {\n const isLatest = messages?.[messages.length - 1]?.id === message.id;\n const isStreaming = !!(isRunning && isLatest);\n const hasContent = !!(message.content && message.content.length > 0);\n\n // Track elapsed time while streaming\n const startTimeRef = useRef<number | null>(null);\n const [elapsed, setElapsed] = useState(0);\n\n useEffect(() => {\n if (isStreaming && startTimeRef.current === null) {\n startTimeRef.current = Date.now();\n }\n\n if (!isStreaming && startTimeRef.current !== null) {\n // Final snapshot of elapsed time\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n return;\n }\n\n if (!isStreaming) return;\n\n // Tick every second while streaming\n const timer = setInterval(() => {\n if (startTimeRef.current !== null) {\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n }\n }, 1000);\n return () => clearInterval(timer);\n }, [isStreaming]);\n\n // Default to open while streaming, auto-collapse when streaming ends\n const [isOpen, setIsOpen] = useState(isStreaming);\n\n useEffect(() => {\n if (isStreaming) {\n setIsOpen(true);\n } else {\n // Auto-collapse when reasoning finishes\n setIsOpen(false);\n }\n }, [isStreaming]);\n\n const label = isStreaming\n ? \"Thinking…\"\n : `Thought for ${formatDuration(elapsed)}`;\n\n const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {\n isOpen,\n label,\n hasContent,\n isStreaming,\n onClick: hasContent ? () => setIsOpen((prev) => !prev) : undefined,\n });\n\n const boundContent = renderSlot(\n contentView,\n CopilotChatReasoningMessage.Content,\n {\n isStreaming,\n hasContent,\n children: message.content,\n },\n );\n\n const boundToggle = renderSlot(toggle, CopilotChatReasoningMessage.Toggle, {\n isOpen,\n children: boundContent,\n });\n\n if (children) {\n return (\n <div data-copilotkit style={{ display: \"contents\" }}>\n {children({\n header: boundHeader,\n contentView: boundContent,\n toggle: boundToggle,\n message,\n messages,\n isRunning,\n })}\n </div>\n );\n }\n\n return (\n <div\n className={twMerge(\"cpk:my-1\", className)}\n data-message-id={message.id}\n {...props}\n >\n {boundHeader}\n {boundToggle}\n </div>\n );\n}\n\nexport namespace CopilotChatReasoningMessage {\n export const Header: React.FC<\n React.ButtonHTMLAttributes<HTMLButtonElement> & {\n isOpen?: boolean;\n label?: string;\n hasContent?: boolean;\n isStreaming?: boolean;\n }\n > = ({\n isOpen,\n label = \"Thoughts\",\n hasContent,\n isStreaming,\n className,\n children: headerChildren,\n ...headerProps\n }) => {\n const isExpandable = !!hasContent;\n\n return (\n <button\n type=\"button\"\n className={twMerge(\n \"cpk:inline-flex cpk:items-center cpk:gap-1 cpk:py-1 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:select-none\",\n isExpandable\n ? \"cpk:hover:text-foreground cpk:cursor-pointer\"\n : \"cpk:cursor-default\",\n className,\n )}\n aria-expanded={isExpandable ? isOpen : undefined}\n {...headerProps}\n >\n <span className=\"cpk:font-medium\">{label}</span>\n {isStreaming && !hasContent && (\n <span className=\"cpk:inline-flex cpk:items-center cpk:ml-1\">\n <span className=\"cpk:w-1.5 cpk:h-1.5 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse\" />\n </span>\n )}\n {headerChildren}\n {isExpandable && (\n <ChevronRight\n className={twMerge(\n \"cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200\",\n isOpen && \"cpk:rotate-90\",\n )}\n />\n )}\n </button>\n );\n };\n\n export const Content: React.FC<\n React.HTMLAttributes<HTMLDivElement> & {\n isStreaming?: boolean;\n hasContent?: boolean;\n }\n > = ({\n isStreaming,\n hasContent,\n className,\n children: contentChildren,\n ...contentProps\n }) => {\n // Don't render the content area at all when there's nothing to show\n if (!hasContent && !isStreaming) return null;\n\n return (\n <div\n className={twMerge(\"cpk:pb-2 cpk:pt-1\", className)}\n {...contentProps}\n >\n <div className=\"cpk:text-sm cpk:text-muted-foreground\">\n <Streamdown>\n {typeof contentChildren === \"string\" ? contentChildren : \"\"}\n </Streamdown>\n {isStreaming && hasContent && (\n <span className=\"cpk:inline-flex cpk:items-center cpk:ml-1 cpk:align-middle\">\n <span className=\"cpk:w-2 cpk:h-2 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse-cursor\" />\n </span>\n )}\n </div>\n </div>\n );\n };\n\n export const Toggle: React.FC<\n React.HTMLAttributes<HTMLDivElement> & {\n isOpen?: boolean;\n }\n > = ({ isOpen, className, children: toggleChildren, ...toggleProps }) => {\n return (\n <div\n className={twMerge(\n \"cpk:grid cpk:transition-[grid-template-rows] cpk:duration-200 cpk:ease-in-out\",\n className,\n )}\n style={{ gridTemplateRows: isOpen ? \"1fr\" : \"0fr\" }}\n {...toggleProps}\n >\n <div className=\"cpk:overflow-hidden\">{toggleChildren}</div>\n </div>\n );\n };\n}\n\nCopilotChatReasoningMessage.Header.displayName =\n \"CopilotChatReasoningMessage.Header\";\nCopilotChatReasoningMessage.Content.displayName =\n \"CopilotChatReasoningMessage.Content\";\nCopilotChatReasoningMessage.Toggle.displayName =\n \"CopilotChatReasoningMessage.Toggle\";\n\nexport default CopilotChatReasoningMessage;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatReasoningMessage.d.cts","names":[],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"mappings":";;;;;KAOY,gCAAA,GAAmC,SAAA;EAE3C,MAAA,SAAe,2BAAA,CAA4B,MAAA;EAC3C,WAAA,SAAoB,2BAAA,CAA4B,OAAA;EAChD,MAAA,SAAe,2BAAA,CAA4B,MAAA;AAAA;EAG3C,OAAA,EAAS,gBAAA;EACT,QAAA,GAAW,OAAA;EACX,SAAA;AAAA,IACE,KAAA,CAAM,cAAA,CAAe,cAAA;AAAA,iBAeX,2BAAA,CAAA;EACd,OAAA;EACA,QAAA;EACA,SAAA;EACA,MAAA;EACA,WAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,gCAAA,GAAgC,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAiGlB,2BAAA;EAAA,MACF,MAAA,EAAQ,KAAA,CAAM,EAAA,CACzB,KAAA,CAAM,oBAAA,CAAqB,iBAAA;IACzB,MAAA;IACA,KAAA;IACA,UAAA;IACA,WAAA;EAAA;EAAA,MA6CS,OAAA,EAAS,KAAA,CAAM,EAAA,CAC1B,KAAA,CAAM,cAAA,CAAe,cAAA;IACnB,WAAA;IACA,UAAA;EAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"CopilotChatReasoningMessage.d.cts","names":[],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"mappings":";;;;;KAOY,gCAAA,GAAmC,SAAA;EAE3C,MAAA,SAAe,2BAAA,CAA4B,MAAA;EAC3C,WAAA,SAAoB,2BAAA,CAA4B,OAAA;EAChD,MAAA,SAAe,2BAAA,CAA4B,MAAA;AAAA;EAG3C,OAAA,EAAS,gBAAA;EACT,QAAA,GAAW,OAAA;EACX,SAAA;AAAA,IACE,KAAA,CAAM,cAAA,CAAe,cAAA;AAAA,iBAeX,2BAAA,CAAA;EACd,OAAA;EACA,QAAA;EACA,SAAA;EACA,MAAA;EACA,WAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,gCAAA,GAAgC,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAiGlB,2BAAA;EAAA,MACF,MAAA,EAAQ,KAAA,CAAM,EAAA,CACzB,KAAA,CAAM,oBAAA,CAAqB,iBAAA;IACzB,MAAA;IACA,KAAA;IACA,UAAA;IACA,WAAA;EAAA;EAAA,MA6CS,OAAA,EAAS,KAAA,CAAM,EAAA,CAC1B,KAAA,CAAM,cAAA,CAAe,cAAA;IACnB,WAAA;IACA,UAAA;EAAA;EAAA,MA+BS,MAAA,EAAQ,KAAA,CAAM,EAAA,CACzB,KAAA,CAAM,cAAA,CAAe,cAAA;IACnB,MAAA;EAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatReasoningMessage.d.mts","names":[],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"mappings":";;;;;KAOY,gCAAA,GAAmC,SAAA;EAE3C,MAAA,SAAe,2BAAA,CAA4B,MAAA;EAC3C,WAAA,SAAoB,2BAAA,CAA4B,OAAA;EAChD,MAAA,SAAe,2BAAA,CAA4B,MAAA;AAAA;EAG3C,OAAA,EAAS,gBAAA;EACT,QAAA,GAAW,OAAA;EACX,SAAA;AAAA,IACE,KAAA,CAAM,cAAA,CAAe,cAAA;AAAA,iBAeX,2BAAA,CAAA;EACd,OAAA;EACA,QAAA;EACA,SAAA;EACA,MAAA;EACA,WAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,gCAAA,GAAgC,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAiGlB,2BAAA;EAAA,MACF,MAAA,EAAQ,KAAA,CAAM,EAAA,CACzB,KAAA,CAAM,oBAAA,CAAqB,iBAAA;IACzB,MAAA;IACA,KAAA;IACA,UAAA;IACA,WAAA;EAAA;EAAA,MA6CS,OAAA,EAAS,KAAA,CAAM,EAAA,CAC1B,KAAA,CAAM,cAAA,CAAe,cAAA;IACnB,WAAA;IACA,UAAA;EAAA;EAAA,
|
|
1
|
+
{"version":3,"file":"CopilotChatReasoningMessage.d.mts","names":[],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"mappings":";;;;;KAOY,gCAAA,GAAmC,SAAA;EAE3C,MAAA,SAAe,2BAAA,CAA4B,MAAA;EAC3C,WAAA,SAAoB,2BAAA,CAA4B,OAAA;EAChD,MAAA,SAAe,2BAAA,CAA4B,MAAA;AAAA;EAG3C,OAAA,EAAS,gBAAA;EACT,QAAA,GAAW,OAAA;EACX,SAAA;AAAA,IACE,KAAA,CAAM,cAAA,CAAe,cAAA;AAAA,iBAeX,2BAAA,CAAA;EACd,OAAA;EACA,QAAA;EACA,SAAA;EACA,MAAA;EACA,WAAA;EACA,MAAA;EACA,QAAA;EACA,SAAA;EAAA,GACG;AAAA,GACF,gCAAA,GAAgC,kBAAA,CAAA,GAAA,CAAA,OAAA;AAAA,kBAiGlB,2BAAA;EAAA,MACF,MAAA,EAAQ,KAAA,CAAM,EAAA,CACzB,KAAA,CAAM,oBAAA,CAAqB,iBAAA;IACzB,MAAA;IACA,KAAA;IACA,UAAA;IACA,WAAA;EAAA;EAAA,MA6CS,OAAA,EAAS,KAAA,CAAM,EAAA,CAC1B,KAAA,CAAM,cAAA,CAAe,cAAA;IACnB,WAAA;IACA,UAAA;EAAA;EAAA,MA+BS,MAAA,EAAQ,KAAA,CAAM,EAAA,CACzB,KAAA,CAAM,cAAA,CAAe,cAAA;IACnB,MAAA;EAAA;AAAA"}
|
|
@@ -2,7 +2,7 @@ import { renderSlot } from "../../lib/slots.mjs";
|
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
3
|
import { twMerge } from "tailwind-merge";
|
|
4
4
|
import { ChevronRight } from "lucide-react";
|
|
5
|
-
import {
|
|
5
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
6
6
|
import { Streamdown } from "streamdown";
|
|
7
7
|
|
|
8
8
|
//#region src/components/chat/CopilotChatReasoningMessage.tsx
|
|
@@ -57,16 +57,20 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, header, con
|
|
|
57
57
|
isOpen,
|
|
58
58
|
children: boundContent
|
|
59
59
|
});
|
|
60
|
-
if (children) return /* @__PURE__ */ jsx(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
60
|
+
if (children) return /* @__PURE__ */ jsx("div", {
|
|
61
|
+
"data-copilotkit": true,
|
|
62
|
+
style: { display: "contents" },
|
|
63
|
+
children: children({
|
|
64
|
+
header: boundHeader,
|
|
65
|
+
contentView: boundContent,
|
|
66
|
+
toggle: boundToggle,
|
|
67
|
+
message,
|
|
68
|
+
messages,
|
|
69
|
+
isRunning
|
|
70
|
+
})
|
|
71
|
+
});
|
|
68
72
|
return /* @__PURE__ */ jsxs("div", {
|
|
69
|
-
className: twMerge("my-1", className),
|
|
73
|
+
className: twMerge("cpk:my-1", className),
|
|
70
74
|
"data-message-id": message.id,
|
|
71
75
|
...props,
|
|
72
76
|
children: [boundHeader, boundToggle]
|
|
@@ -77,44 +81,44 @@ function CopilotChatReasoningMessage({ message, messages, isRunning, header, con
|
|
|
77
81
|
const isExpandable = !!hasContent;
|
|
78
82
|
return /* @__PURE__ */ jsxs("button", {
|
|
79
83
|
type: "button",
|
|
80
|
-
className: twMerge("inline-flex items-center gap-1 py-1 text-sm text-muted-foreground transition-colors select-none", isExpandable ? "hover:text-foreground cursor-pointer" : "cursor-default", className),
|
|
84
|
+
className: twMerge("cpk:inline-flex cpk:items-center cpk:gap-1 cpk:py-1 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:select-none", isExpandable ? "cpk:hover:text-foreground cpk:cursor-pointer" : "cpk:cursor-default", className),
|
|
81
85
|
"aria-expanded": isExpandable ? isOpen : void 0,
|
|
82
86
|
...headerProps,
|
|
83
87
|
children: [
|
|
84
88
|
/* @__PURE__ */ jsx("span", {
|
|
85
|
-
className: "font-medium",
|
|
89
|
+
className: "cpk:font-medium",
|
|
86
90
|
children: label
|
|
87
91
|
}),
|
|
88
92
|
isStreaming && !hasContent && /* @__PURE__ */ jsx("span", {
|
|
89
|
-
className: "inline-flex items-center ml-1",
|
|
90
|
-
children: /* @__PURE__ */ jsx("span", { className: "w-1.5 h-1.5 rounded-full bg-muted-foreground animate-pulse" })
|
|
93
|
+
className: "cpk:inline-flex cpk:items-center cpk:ml-1",
|
|
94
|
+
children: /* @__PURE__ */ jsx("span", { className: "cpk:w-1.5 cpk:h-1.5 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse" })
|
|
91
95
|
}),
|
|
92
96
|
headerChildren,
|
|
93
|
-
isExpandable && /* @__PURE__ */ jsx(ChevronRight, { className: twMerge("size-3.5 shrink-0 transition-transform duration-200", isOpen && "rotate-90") })
|
|
97
|
+
isExpandable && /* @__PURE__ */ jsx(ChevronRight, { className: twMerge("cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200", isOpen && "cpk:rotate-90") })
|
|
94
98
|
]
|
|
95
99
|
});
|
|
96
100
|
};
|
|
97
101
|
_CopilotChatReasoningMessage.Content = ({ isStreaming, hasContent, className, children: contentChildren, ...contentProps }) => {
|
|
98
102
|
if (!hasContent && !isStreaming) return null;
|
|
99
103
|
return /* @__PURE__ */ jsx("div", {
|
|
100
|
-
className: twMerge("pb-2 pt-1", className),
|
|
104
|
+
className: twMerge("cpk:pb-2 cpk:pt-1", className),
|
|
101
105
|
...contentProps,
|
|
102
106
|
children: /* @__PURE__ */ jsxs("div", {
|
|
103
|
-
className: "text-sm text-muted-foreground",
|
|
107
|
+
className: "cpk:text-sm cpk:text-muted-foreground",
|
|
104
108
|
children: [/* @__PURE__ */ jsx(Streamdown, { children: typeof contentChildren === "string" ? contentChildren : "" }), isStreaming && hasContent && /* @__PURE__ */ jsx("span", {
|
|
105
|
-
className: "inline-flex items-center ml-1 align-middle",
|
|
106
|
-
children: /* @__PURE__ */ jsx("span", { className: "w-2 h-2 rounded-full bg-muted-foreground animate-pulse-cursor" })
|
|
109
|
+
className: "cpk:inline-flex cpk:items-center cpk:ml-1 cpk:align-middle",
|
|
110
|
+
children: /* @__PURE__ */ jsx("span", { className: "cpk:w-2 cpk:h-2 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse-cursor" })
|
|
107
111
|
})]
|
|
108
112
|
})
|
|
109
113
|
});
|
|
110
114
|
};
|
|
111
115
|
_CopilotChatReasoningMessage.Toggle = ({ isOpen, className, children: toggleChildren, ...toggleProps }) => {
|
|
112
116
|
return /* @__PURE__ */ jsx("div", {
|
|
113
|
-
className: twMerge("grid transition-[grid-template-rows] duration-200 ease-in-out", className),
|
|
117
|
+
className: twMerge("cpk:grid cpk:transition-[grid-template-rows] cpk:duration-200 cpk:ease-in-out", className),
|
|
114
118
|
style: { gridTemplateRows: isOpen ? "1fr" : "0fr" },
|
|
115
119
|
...toggleProps,
|
|
116
120
|
children: /* @__PURE__ */ jsx("div", {
|
|
117
|
-
className: "overflow-hidden",
|
|
121
|
+
className: "cpk:overflow-hidden",
|
|
118
122
|
children: toggleChildren
|
|
119
123
|
})
|
|
120
124
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CopilotChatReasoningMessage.mjs","names":[],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"sourcesContent":["import { ReasoningMessage, Message } from \"@ag-ui/core\";\nimport { useState, useEffect, useRef } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Streamdown } from \"streamdown\";\nimport { WithSlots, renderSlot } from \"@/lib/slots\";\n\nexport type CopilotChatReasoningMessageProps = WithSlots<\n {\n header: typeof CopilotChatReasoningMessage.Header;\n contentView: typeof CopilotChatReasoningMessage.Content;\n toggle: typeof CopilotChatReasoningMessage.Toggle;\n },\n {\n message: ReasoningMessage;\n messages?: Message[];\n isRunning?: boolean;\n } & React.HTMLAttributes<HTMLDivElement>\n>;\n\n/**\n * Formats an elapsed duration (in seconds) to a human-readable string.\n */\nfunction formatDuration(seconds: number): string {\n if (seconds < 1) return \"a few seconds\";\n if (seconds < 60) return `${Math.round(seconds)} seconds`;\n const mins = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n if (secs === 0) return `${mins} minute${mins > 1 ? \"s\" : \"\"}`;\n return `${mins}m ${secs}s`;\n}\n\nexport function CopilotChatReasoningMessage({\n message,\n messages,\n isRunning,\n header,\n contentView,\n toggle,\n children,\n className,\n ...props\n}: CopilotChatReasoningMessageProps) {\n const isLatest = messages?.[messages.length - 1]?.id === message.id;\n const isStreaming = !!(isRunning && isLatest);\n const hasContent = !!(message.content && message.content.length > 0);\n\n // Track elapsed time while streaming\n const startTimeRef = useRef<number | null>(null);\n const [elapsed, setElapsed] = useState(0);\n\n useEffect(() => {\n if (isStreaming && startTimeRef.current === null) {\n startTimeRef.current = Date.now();\n }\n\n if (!isStreaming && startTimeRef.current !== null) {\n // Final snapshot of elapsed time\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n return;\n }\n\n if (!isStreaming) return;\n\n // Tick every second while streaming\n const timer = setInterval(() => {\n if (startTimeRef.current !== null) {\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n }\n }, 1000);\n return () => clearInterval(timer);\n }, [isStreaming]);\n\n // Default to open while streaming, auto-collapse when streaming ends\n const [isOpen, setIsOpen] = useState(isStreaming);\n\n useEffect(() => {\n if (isStreaming) {\n setIsOpen(true);\n } else {\n // Auto-collapse when reasoning finishes\n setIsOpen(false);\n }\n }, [isStreaming]);\n\n const label = isStreaming\n ? \"Thinking…\"\n : `Thought for ${formatDuration(elapsed)}`;\n\n const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {\n isOpen,\n label,\n hasContent,\n isStreaming,\n onClick: hasContent ? () => setIsOpen((prev) => !prev) : undefined,\n });\n\n const boundContent = renderSlot(\n contentView,\n CopilotChatReasoningMessage.Content,\n {\n isStreaming,\n hasContent,\n children: message.content,\n },\n );\n\n const boundToggle = renderSlot(toggle, CopilotChatReasoningMessage.Toggle, {\n isOpen,\n children: boundContent,\n });\n\n if (children) {\n return (\n
|
|
1
|
+
{"version":3,"file":"CopilotChatReasoningMessage.mjs","names":[],"sources":["../../../src/components/chat/CopilotChatReasoningMessage.tsx"],"sourcesContent":["import { ReasoningMessage, Message } from \"@ag-ui/core\";\nimport { useState, useEffect, useRef } from \"react\";\nimport { ChevronRight } from \"lucide-react\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Streamdown } from \"streamdown\";\nimport { WithSlots, renderSlot } from \"@/lib/slots\";\n\nexport type CopilotChatReasoningMessageProps = WithSlots<\n {\n header: typeof CopilotChatReasoningMessage.Header;\n contentView: typeof CopilotChatReasoningMessage.Content;\n toggle: typeof CopilotChatReasoningMessage.Toggle;\n },\n {\n message: ReasoningMessage;\n messages?: Message[];\n isRunning?: boolean;\n } & React.HTMLAttributes<HTMLDivElement>\n>;\n\n/**\n * Formats an elapsed duration (in seconds) to a human-readable string.\n */\nfunction formatDuration(seconds: number): string {\n if (seconds < 1) return \"a few seconds\";\n if (seconds < 60) return `${Math.round(seconds)} seconds`;\n const mins = Math.floor(seconds / 60);\n const secs = Math.round(seconds % 60);\n if (secs === 0) return `${mins} minute${mins > 1 ? \"s\" : \"\"}`;\n return `${mins}m ${secs}s`;\n}\n\nexport function CopilotChatReasoningMessage({\n message,\n messages,\n isRunning,\n header,\n contentView,\n toggle,\n children,\n className,\n ...props\n}: CopilotChatReasoningMessageProps) {\n const isLatest = messages?.[messages.length - 1]?.id === message.id;\n const isStreaming = !!(isRunning && isLatest);\n const hasContent = !!(message.content && message.content.length > 0);\n\n // Track elapsed time while streaming\n const startTimeRef = useRef<number | null>(null);\n const [elapsed, setElapsed] = useState(0);\n\n useEffect(() => {\n if (isStreaming && startTimeRef.current === null) {\n startTimeRef.current = Date.now();\n }\n\n if (!isStreaming && startTimeRef.current !== null) {\n // Final snapshot of elapsed time\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n return;\n }\n\n if (!isStreaming) return;\n\n // Tick every second while streaming\n const timer = setInterval(() => {\n if (startTimeRef.current !== null) {\n setElapsed((Date.now() - startTimeRef.current) / 1000);\n }\n }, 1000);\n return () => clearInterval(timer);\n }, [isStreaming]);\n\n // Default to open while streaming, auto-collapse when streaming ends\n const [isOpen, setIsOpen] = useState(isStreaming);\n\n useEffect(() => {\n if (isStreaming) {\n setIsOpen(true);\n } else {\n // Auto-collapse when reasoning finishes\n setIsOpen(false);\n }\n }, [isStreaming]);\n\n const label = isStreaming\n ? \"Thinking…\"\n : `Thought for ${formatDuration(elapsed)}`;\n\n const boundHeader = renderSlot(header, CopilotChatReasoningMessage.Header, {\n isOpen,\n label,\n hasContent,\n isStreaming,\n onClick: hasContent ? () => setIsOpen((prev) => !prev) : undefined,\n });\n\n const boundContent = renderSlot(\n contentView,\n CopilotChatReasoningMessage.Content,\n {\n isStreaming,\n hasContent,\n children: message.content,\n },\n );\n\n const boundToggle = renderSlot(toggle, CopilotChatReasoningMessage.Toggle, {\n isOpen,\n children: boundContent,\n });\n\n if (children) {\n return (\n <div data-copilotkit style={{ display: \"contents\" }}>\n {children({\n header: boundHeader,\n contentView: boundContent,\n toggle: boundToggle,\n message,\n messages,\n isRunning,\n })}\n </div>\n );\n }\n\n return (\n <div\n className={twMerge(\"cpk:my-1\", className)}\n data-message-id={message.id}\n {...props}\n >\n {boundHeader}\n {boundToggle}\n </div>\n );\n}\n\nexport namespace CopilotChatReasoningMessage {\n export const Header: React.FC<\n React.ButtonHTMLAttributes<HTMLButtonElement> & {\n isOpen?: boolean;\n label?: string;\n hasContent?: boolean;\n isStreaming?: boolean;\n }\n > = ({\n isOpen,\n label = \"Thoughts\",\n hasContent,\n isStreaming,\n className,\n children: headerChildren,\n ...headerProps\n }) => {\n const isExpandable = !!hasContent;\n\n return (\n <button\n type=\"button\"\n className={twMerge(\n \"cpk:inline-flex cpk:items-center cpk:gap-1 cpk:py-1 cpk:text-sm cpk:text-muted-foreground cpk:transition-colors cpk:select-none\",\n isExpandable\n ? \"cpk:hover:text-foreground cpk:cursor-pointer\"\n : \"cpk:cursor-default\",\n className,\n )}\n aria-expanded={isExpandable ? isOpen : undefined}\n {...headerProps}\n >\n <span className=\"cpk:font-medium\">{label}</span>\n {isStreaming && !hasContent && (\n <span className=\"cpk:inline-flex cpk:items-center cpk:ml-1\">\n <span className=\"cpk:w-1.5 cpk:h-1.5 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse\" />\n </span>\n )}\n {headerChildren}\n {isExpandable && (\n <ChevronRight\n className={twMerge(\n \"cpk:size-3.5 cpk:shrink-0 cpk:transition-transform cpk:duration-200\",\n isOpen && \"cpk:rotate-90\",\n )}\n />\n )}\n </button>\n );\n };\n\n export const Content: React.FC<\n React.HTMLAttributes<HTMLDivElement> & {\n isStreaming?: boolean;\n hasContent?: boolean;\n }\n > = ({\n isStreaming,\n hasContent,\n className,\n children: contentChildren,\n ...contentProps\n }) => {\n // Don't render the content area at all when there's nothing to show\n if (!hasContent && !isStreaming) return null;\n\n return (\n <div\n className={twMerge(\"cpk:pb-2 cpk:pt-1\", className)}\n {...contentProps}\n >\n <div className=\"cpk:text-sm cpk:text-muted-foreground\">\n <Streamdown>\n {typeof contentChildren === \"string\" ? contentChildren : \"\"}\n </Streamdown>\n {isStreaming && hasContent && (\n <span className=\"cpk:inline-flex cpk:items-center cpk:ml-1 cpk:align-middle\">\n <span className=\"cpk:w-2 cpk:h-2 cpk:rounded-full cpk:bg-muted-foreground cpk:animate-pulse-cursor\" />\n </span>\n )}\n </div>\n </div>\n );\n };\n\n export const Toggle: React.FC<\n React.HTMLAttributes<HTMLDivElement> & {\n isOpen?: boolean;\n }\n > = ({ isOpen, className, children: toggleChildren, ...toggleProps }) => {\n return (\n <div\n className={twMerge(\n \"cpk:grid cpk:transition-[grid-template-rows] cpk:duration-200 cpk:ease-in-out\",\n className,\n )}\n style={{ gridTemplateRows: isOpen ? \"1fr\" : \"0fr\" }}\n {...toggleProps}\n >\n <div className=\"cpk:overflow-hidden\">{toggleChildren}</div>\n </div>\n );\n };\n}\n\nCopilotChatReasoningMessage.Header.displayName =\n \"CopilotChatReasoningMessage.Header\";\nCopilotChatReasoningMessage.Content.displayName =\n \"CopilotChatReasoningMessage.Content\";\nCopilotChatReasoningMessage.Toggle.displayName =\n \"CopilotChatReasoningMessage.Toggle\";\n\nexport default CopilotChatReasoningMessage;\n"],"mappings":"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"}
|