@gugacoder/agentic-chat 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Chat.d.ts +21 -0
- package/dist/components/Chat.js +13 -0
- package/dist/components/ErrorNote.d.ts +5 -0
- package/dist/components/ErrorNote.js +6 -0
- package/dist/components/LazyRender.d.ts +8 -0
- package/dist/components/LazyRender.js +22 -0
- package/dist/components/Markdown.d.ts +5 -0
- package/dist/components/Markdown.js +65 -0
- package/dist/components/MessageBubble.d.ts +10 -0
- package/dist/components/MessageBubble.js +39 -0
- package/dist/components/MessageInput.d.ts +19 -0
- package/dist/components/MessageInput.js +214 -0
- package/dist/components/MessageList.d.ts +12 -0
- package/dist/components/MessageList.js +68 -0
- package/dist/components/StreamingIndicator.d.ts +1 -0
- package/dist/components/StreamingIndicator.js +9 -0
- package/dist/conversations/CollapsibleGroup.d.ts +11 -0
- package/dist/conversations/CollapsibleGroup.js +9 -0
- package/dist/conversations/ConversationBar.d.ts +27 -0
- package/dist/conversations/ConversationBar.js +53 -0
- package/dist/conversations/ConversationList.d.ts +33 -0
- package/dist/conversations/ConversationList.js +48 -0
- package/dist/conversations/ConversationListItem.d.ts +20 -0
- package/dist/conversations/ConversationListItem.js +22 -0
- package/dist/conversations/DeleteDialog.d.ts +13 -0
- package/dist/conversations/DeleteDialog.js +8 -0
- package/dist/conversations/RenameDialog.d.ts +15 -0
- package/dist/conversations/RenameDialog.js +15 -0
- package/dist/conversations/index.d.ts +9 -0
- package/dist/conversations/index.js +5 -0
- package/dist/conversations/types.d.ts +21 -0
- package/dist/conversations/types.js +1 -0
- package/dist/conversations/useConversations.d.ts +19 -0
- package/dist/conversations/useConversations.js +102 -0
- package/dist/conversations/utils.d.ts +8 -0
- package/dist/conversations/utils.js +134 -0
- package/dist/display/AlertRenderer.d.ts +2 -0
- package/dist/display/AlertRenderer.js +13 -0
- package/dist/display/CarouselRenderer.d.ts +2 -0
- package/dist/display/CarouselRenderer.js +41 -0
- package/dist/display/ChartRenderer.d.ts +2 -0
- package/dist/display/ChartRenderer.js +76 -0
- package/dist/display/ChoiceButtonsRenderer.d.ts +6 -0
- package/dist/display/ChoiceButtonsRenderer.js +23 -0
- package/dist/display/CodeBlockRenderer.d.ts +2 -0
- package/dist/display/CodeBlockRenderer.js +17 -0
- package/dist/display/ComparisonTableRenderer.d.ts +2 -0
- package/dist/display/ComparisonTableRenderer.js +26 -0
- package/dist/display/DataTableRenderer.d.ts +2 -0
- package/dist/display/DataTableRenderer.js +74 -0
- package/dist/display/FileCardRenderer.d.ts +2 -0
- package/dist/display/FileCardRenderer.js +31 -0
- package/dist/display/GalleryRenderer.d.ts +2 -0
- package/dist/display/GalleryRenderer.js +11 -0
- package/dist/display/ImageViewerRenderer.d.ts +2 -0
- package/dist/display/ImageViewerRenderer.js +15 -0
- package/dist/display/LinkPreviewRenderer.d.ts +2 -0
- package/dist/display/LinkPreviewRenderer.js +20 -0
- package/dist/display/MapViewRenderer.d.ts +2 -0
- package/dist/display/MapViewRenderer.js +20 -0
- package/dist/display/MetricCardRenderer.d.ts +2 -0
- package/dist/display/MetricCardRenderer.js +12 -0
- package/dist/display/PriceHighlightRenderer.d.ts +2 -0
- package/dist/display/PriceHighlightRenderer.js +13 -0
- package/dist/display/ProductCardRenderer.d.ts +2 -0
- package/dist/display/ProductCardRenderer.js +23 -0
- package/dist/display/ProgressStepsRenderer.d.ts +2 -0
- package/dist/display/ProgressStepsRenderer.js +14 -0
- package/dist/display/SourcesListRenderer.d.ts +2 -0
- package/dist/display/SourcesListRenderer.js +5 -0
- package/dist/display/SpreadsheetRenderer.d.ts +2 -0
- package/dist/display/SpreadsheetRenderer.js +32 -0
- package/dist/display/StepTimelineRenderer.d.ts +2 -0
- package/dist/display/StepTimelineRenderer.js +21 -0
- package/dist/display/index.d.ts +21 -0
- package/dist/display/index.js +20 -0
- package/dist/display/registry.d.ts +5 -0
- package/dist/display/registry.js +50 -0
- package/dist/hooks/ChatProvider.d.ts +10 -0
- package/dist/hooks/ChatProvider.js +14 -0
- package/dist/hooks/useBackboneChat.d.ts +37 -0
- package/dist/hooks/useBackboneChat.js +121 -0
- package/dist/hooks/useIsMobile.d.ts +1 -0
- package/dist/hooks/useIsMobile.js +12 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +40 -0
- package/dist/lib/utils.d.ts +2 -0
- package/dist/lib/utils.js +5 -0
- package/dist/parts/PartRenderer.d.ts +40 -0
- package/dist/parts/PartRenderer.js +97 -0
- package/dist/parts/ReasoningBlock.d.ts +6 -0
- package/dist/parts/ReasoningBlock.js +18 -0
- package/dist/parts/ToolActivity.d.ts +11 -0
- package/dist/parts/ToolActivity.js +52 -0
- package/dist/parts/ToolResult.d.ts +7 -0
- package/dist/parts/ToolResult.js +38 -0
- package/dist/styles.css +2 -0
- package/dist/ui/alert.d.ts +12 -0
- package/dist/ui/alert.js +28 -0
- package/dist/ui/badge.d.ts +9 -0
- package/dist/ui/badge.js +20 -0
- package/dist/ui/button.d.ts +11 -0
- package/dist/ui/button.js +31 -0
- package/dist/ui/card.d.ts +8 -0
- package/dist/ui/card.js +21 -0
- package/dist/ui/collapsible.d.ts +1 -0
- package/dist/ui/collapsible.js +2 -0
- package/dist/ui/dialog.d.ts +19 -0
- package/dist/ui/dialog.js +23 -0
- package/dist/ui/dropdown-menu.d.ts +11 -0
- package/dist/ui/dropdown-menu.js +15 -0
- package/dist/ui/input.d.ts +3 -0
- package/dist/ui/input.js +6 -0
- package/dist/ui/progress.d.ts +7 -0
- package/dist/ui/progress.js +9 -0
- package/dist/ui/scroll-area.d.ts +5 -0
- package/dist/ui/scroll-area.js +12 -0
- package/dist/ui/separator.d.ts +4 -0
- package/dist/ui/separator.js +8 -0
- package/dist/ui/skeleton.d.ts +3 -0
- package/dist/ui/skeleton.js +6 -0
- package/dist/ui/table.d.ts +10 -0
- package/dist/ui/table.js +27 -0
- package/package.json +53 -0
- package/src/components/Chat.tsx +80 -0
- package/src/components/ErrorNote.tsx +32 -0
- package/src/components/LazyRender.tsx +42 -0
- package/src/components/Markdown.tsx +114 -0
- package/src/components/MessageBubble.tsx +102 -0
- package/src/components/MessageInput.tsx +421 -0
- package/src/components/MessageList.tsx +139 -0
- package/src/components/StreamingIndicator.tsx +19 -0
- package/src/conversations/CollapsibleGroup.tsx +41 -0
- package/src/conversations/ConversationBar.tsx +200 -0
- package/src/conversations/ConversationList.tsx +234 -0
- package/src/conversations/ConversationListItem.tsx +123 -0
- package/src/conversations/DeleteDialog.tsx +55 -0
- package/src/conversations/RenameDialog.tsx +74 -0
- package/src/conversations/index.ts +14 -0
- package/src/conversations/types.ts +17 -0
- package/src/conversations/useConversations.ts +148 -0
- package/src/conversations/utils.ts +159 -0
- package/src/display/AlertRenderer.tsx +27 -0
- package/src/display/CarouselRenderer.tsx +141 -0
- package/src/display/ChartRenderer.tsx +195 -0
- package/src/display/ChoiceButtonsRenderer.tsx +114 -0
- package/src/display/CodeBlockRenderer.tsx +49 -0
- package/src/display/ComparisonTableRenderer.tsx +132 -0
- package/src/display/DataTableRenderer.tsx +144 -0
- package/src/display/FileCardRenderer.tsx +55 -0
- package/src/display/GalleryRenderer.tsx +65 -0
- package/src/display/ImageViewerRenderer.tsx +114 -0
- package/src/display/LinkPreviewRenderer.tsx +74 -0
- package/src/display/MapViewRenderer.tsx +75 -0
- package/src/display/MetricCardRenderer.tsx +29 -0
- package/src/display/PriceHighlightRenderer.tsx +44 -0
- package/src/display/ProductCardRenderer.tsx +112 -0
- package/src/display/ProgressStepsRenderer.tsx +59 -0
- package/src/display/SourcesListRenderer.tsx +47 -0
- package/src/display/SpreadsheetRenderer.tsx +86 -0
- package/src/display/StepTimelineRenderer.tsx +75 -0
- package/src/display/index.ts +21 -0
- package/src/display/registry.ts +81 -0
- package/src/hooks/ChatProvider.tsx +22 -0
- package/src/hooks/useBackboneChat.ts +148 -0
- package/src/hooks/useIsMobile.ts +15 -0
- package/src/index.ts +80 -0
- package/src/lib/utils.ts +6 -0
- package/src/parts/PartRenderer.tsx +198 -0
- package/src/parts/ReasoningBlock.tsx +41 -0
- package/src/parts/ToolActivity.tsx +79 -0
- package/src/parts/ToolResult.tsx +79 -0
- package/src/styles.css +2 -0
- package/src/ui/alert.tsx +77 -0
- package/src/ui/badge.tsx +36 -0
- package/src/ui/button.tsx +54 -0
- package/src/ui/card.tsx +68 -0
- package/src/ui/collapsible.tsx +7 -0
- package/src/ui/dialog.tsx +122 -0
- package/src/ui/dropdown-menu.tsx +76 -0
- package/src/ui/input.tsx +24 -0
- package/src/ui/progress.tsx +36 -0
- package/src/ui/scroll-area.tsx +48 -0
- package/src/ui/separator.tsx +31 -0
- package/src/ui/skeleton.tsx +9 -0
- package/src/ui/table.tsx +114 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { AlertRenderer } from "./AlertRenderer.js";
|
|
2
|
+
export { MetricCardRenderer } from "./MetricCardRenderer.js";
|
|
3
|
+
export { PriceHighlightRenderer } from "./PriceHighlightRenderer.js";
|
|
4
|
+
export { FileCardRenderer } from "./FileCardRenderer.js";
|
|
5
|
+
export { CodeBlockRenderer } from "./CodeBlockRenderer.js";
|
|
6
|
+
export { SourcesListRenderer } from "./SourcesListRenderer.js";
|
|
7
|
+
export { StepTimelineRenderer } from "./StepTimelineRenderer.js";
|
|
8
|
+
export { ProgressStepsRenderer } from "./ProgressStepsRenderer.js";
|
|
9
|
+
export { ChartRenderer } from "./ChartRenderer.js";
|
|
10
|
+
export { CarouselRenderer } from "./CarouselRenderer.js";
|
|
11
|
+
export { ProductCardRenderer } from "./ProductCardRenderer.js";
|
|
12
|
+
export { ComparisonTableRenderer } from "./ComparisonTableRenderer.js";
|
|
13
|
+
export { DataTableRenderer } from "./DataTableRenderer.js";
|
|
14
|
+
export { SpreadsheetRenderer } from "./SpreadsheetRenderer.js";
|
|
15
|
+
export { GalleryRenderer } from "./GalleryRenderer.js";
|
|
16
|
+
export { ImageViewerRenderer } from "./ImageViewerRenderer.js";
|
|
17
|
+
export { LinkPreviewRenderer } from "./LinkPreviewRenderer.js";
|
|
18
|
+
export { MapViewRenderer } from "./MapViewRenderer.js";
|
|
19
|
+
export { ChoiceButtonsRenderer } from "./ChoiceButtonsRenderer.js";
|
|
20
|
+
export { defaultDisplayRenderers, resolveDisplayRenderer } from "./registry.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ComponentType } from "react";
|
|
2
|
+
export type DisplayActionName = "metric" | "price" | "alert" | "choices" | "table" | "spreadsheet" | "comparison" | "carousel" | "gallery" | "sources" | "product" | "link" | "file" | "image" | "chart" | "map" | "code" | "progress" | "steps";
|
|
3
|
+
export type DisplayRendererMap = Partial<Record<string, ComponentType<any>>>;
|
|
4
|
+
export declare const defaultDisplayRenderers: Record<DisplayActionName, ComponentType<any>>;
|
|
5
|
+
export declare function resolveDisplayRenderer(action: string, overrides?: DisplayRendererMap): ComponentType<any> | null;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { AlertRenderer } from "./AlertRenderer.js";
|
|
2
|
+
import { MetricCardRenderer } from "./MetricCardRenderer.js";
|
|
3
|
+
import { PriceHighlightRenderer } from "./PriceHighlightRenderer.js";
|
|
4
|
+
import { FileCardRenderer } from "./FileCardRenderer.js";
|
|
5
|
+
import { CodeBlockRenderer } from "./CodeBlockRenderer.js";
|
|
6
|
+
import { SourcesListRenderer } from "./SourcesListRenderer.js";
|
|
7
|
+
import { StepTimelineRenderer } from "./StepTimelineRenderer.js";
|
|
8
|
+
import { ProgressStepsRenderer } from "./ProgressStepsRenderer.js";
|
|
9
|
+
import { ChartRenderer } from "./ChartRenderer.js";
|
|
10
|
+
import { CarouselRenderer } from "./CarouselRenderer.js";
|
|
11
|
+
import { ProductCardRenderer } from "./ProductCardRenderer.js";
|
|
12
|
+
import { ComparisonTableRenderer } from "./ComparisonTableRenderer.js";
|
|
13
|
+
import { DataTableRenderer } from "./DataTableRenderer.js";
|
|
14
|
+
import { SpreadsheetRenderer } from "./SpreadsheetRenderer.js";
|
|
15
|
+
import { GalleryRenderer } from "./GalleryRenderer.js";
|
|
16
|
+
import { ImageViewerRenderer } from "./ImageViewerRenderer.js";
|
|
17
|
+
import { LinkPreviewRenderer } from "./LinkPreviewRenderer.js";
|
|
18
|
+
import { MapViewRenderer } from "./MapViewRenderer.js";
|
|
19
|
+
import { ChoiceButtonsRenderer } from "./ChoiceButtonsRenderer.js";
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
21
|
+
export const defaultDisplayRenderers = {
|
|
22
|
+
// highlight
|
|
23
|
+
metric: MetricCardRenderer,
|
|
24
|
+
price: PriceHighlightRenderer,
|
|
25
|
+
alert: AlertRenderer,
|
|
26
|
+
choices: ChoiceButtonsRenderer,
|
|
27
|
+
// collection
|
|
28
|
+
table: DataTableRenderer,
|
|
29
|
+
spreadsheet: SpreadsheetRenderer,
|
|
30
|
+
comparison: ComparisonTableRenderer,
|
|
31
|
+
carousel: CarouselRenderer,
|
|
32
|
+
gallery: GalleryRenderer,
|
|
33
|
+
sources: SourcesListRenderer,
|
|
34
|
+
// card
|
|
35
|
+
product: ProductCardRenderer,
|
|
36
|
+
link: LinkPreviewRenderer,
|
|
37
|
+
file: FileCardRenderer,
|
|
38
|
+
image: ImageViewerRenderer,
|
|
39
|
+
// visual
|
|
40
|
+
chart: ChartRenderer,
|
|
41
|
+
map: MapViewRenderer,
|
|
42
|
+
code: CodeBlockRenderer,
|
|
43
|
+
progress: ProgressStepsRenderer,
|
|
44
|
+
steps: StepTimelineRenderer,
|
|
45
|
+
};
|
|
46
|
+
export function resolveDisplayRenderer(action, overrides) {
|
|
47
|
+
if (overrides?.[action])
|
|
48
|
+
return overrides[action];
|
|
49
|
+
return defaultDisplayRenderers[action] ?? null;
|
|
50
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { useBackboneChat, type UseBackboneChatOptions } from "./useBackboneChat.js";
|
|
3
|
+
type ChatContextValue = ReturnType<typeof useBackboneChat>;
|
|
4
|
+
export interface ChatProviderProps extends Omit<UseBackboneChatOptions, "initialMessages"> {
|
|
5
|
+
initialMessages?: UseBackboneChatOptions["initialMessages"];
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}
|
|
8
|
+
export declare function ChatProvider({ endpoint, token, sessionId, initialMessages, children }: ChatProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export declare function useChatContext(): ChatContextValue;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { useBackboneChat } from "./useBackboneChat.js";
|
|
4
|
+
const ChatContext = createContext(null);
|
|
5
|
+
export function ChatProvider({ endpoint, token, sessionId, initialMessages, children }) {
|
|
6
|
+
const chat = useBackboneChat({ endpoint, token, sessionId, initialMessages });
|
|
7
|
+
return _jsx(ChatContext.Provider, { value: chat, children: children });
|
|
8
|
+
}
|
|
9
|
+
export function useChatContext() {
|
|
10
|
+
const ctx = useContext(ChatContext);
|
|
11
|
+
if (!ctx)
|
|
12
|
+
throw new Error("useChatContext must be used within ChatProvider");
|
|
13
|
+
return ctx;
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Message } from "@ai-sdk/react";
|
|
2
|
+
import type React from "react";
|
|
3
|
+
export type { Message };
|
|
4
|
+
export interface UseBackboneChatOptions {
|
|
5
|
+
endpoint: string;
|
|
6
|
+
token: string;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
initialMessages?: Message[];
|
|
9
|
+
enableRichContent?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function useBackboneChat(options: UseBackboneChatOptions): {
|
|
12
|
+
error: Error;
|
|
13
|
+
handleSubmit: (e: React.FormEvent, attachments?: Array<{
|
|
14
|
+
file: File;
|
|
15
|
+
}>) => void;
|
|
16
|
+
reload: (chatRequestOptions?: import("@ai-sdk/ui-utils").ChatRequestOptions) => Promise<string>;
|
|
17
|
+
isUploading: boolean;
|
|
18
|
+
buildAttachmentUrl: (ref: string) => string;
|
|
19
|
+
messages: import("@ai-sdk/ui-utils").UIMessage[];
|
|
20
|
+
append: (message: Message | import("@ai-sdk/ui-utils").CreateMessage, chatRequestOptions?: import("@ai-sdk/ui-utils").ChatRequestOptions) => Promise<string | null | undefined>;
|
|
21
|
+
stop: () => void;
|
|
22
|
+
experimental_resume: () => void;
|
|
23
|
+
setMessages: (messages: Message[] | ((messages: Message[]) => Message[])) => void;
|
|
24
|
+
input: string;
|
|
25
|
+
setInput: React.Dispatch<React.SetStateAction<string>>;
|
|
26
|
+
handleInputChange: (e: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => void;
|
|
27
|
+
metadata?: Object;
|
|
28
|
+
isLoading: boolean;
|
|
29
|
+
status: "submitted" | "streaming" | "ready" | "error";
|
|
30
|
+
data?: import("@ai-sdk/ui-utils").JSONValue[];
|
|
31
|
+
setData: (data: import("@ai-sdk/ui-utils").JSONValue[] | undefined | ((data: import("@ai-sdk/ui-utils").JSONValue[] | undefined) => import("@ai-sdk/ui-utils").JSONValue[] | undefined)) => void;
|
|
32
|
+
id: string;
|
|
33
|
+
addToolResult: ({ toolCallId, result, }: {
|
|
34
|
+
toolCallId: string;
|
|
35
|
+
result: any;
|
|
36
|
+
}) => void;
|
|
37
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { useChat } from "@ai-sdk/react";
|
|
2
|
+
import { useRef, useEffect, useState, useCallback } from "react";
|
|
3
|
+
const RESPONSE_TIMEOUT_MS = 30_000;
|
|
4
|
+
export function useBackboneChat(options) {
|
|
5
|
+
const [syntheticError, setSyntheticError] = useState(null);
|
|
6
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
7
|
+
const timeoutRef = useRef(null);
|
|
8
|
+
const prevLoadingRef = useRef(false);
|
|
9
|
+
const pendingFilesRef = useRef([]);
|
|
10
|
+
const clearTimer = useCallback(() => {
|
|
11
|
+
if (timeoutRef.current) {
|
|
12
|
+
clearTimeout(timeoutRef.current);
|
|
13
|
+
timeoutRef.current = null;
|
|
14
|
+
}
|
|
15
|
+
}, []);
|
|
16
|
+
const rich = options.enableRichContent !== false;
|
|
17
|
+
const chat = useChat({
|
|
18
|
+
api: `${options.endpoint}/api/v1/ai/conversations/${options.sessionId}/messages?format=datastream${rich ? "&rich=true" : ""}`,
|
|
19
|
+
headers: { Authorization: `Bearer ${options.token}` },
|
|
20
|
+
initialMessages: options.initialMessages,
|
|
21
|
+
fetch: async (url, init) => {
|
|
22
|
+
if (pendingFilesRef.current.length > 0) {
|
|
23
|
+
const files = pendingFilesRef.current;
|
|
24
|
+
pendingFilesRef.current = [];
|
|
25
|
+
const body = JSON.parse(init?.body || "{}");
|
|
26
|
+
const messages = body.messages ?? [];
|
|
27
|
+
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
28
|
+
const messageText = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "";
|
|
29
|
+
const formData = new FormData();
|
|
30
|
+
if (messageText)
|
|
31
|
+
formData.append("message", messageText);
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
formData.append("files", file);
|
|
34
|
+
}
|
|
35
|
+
const initHeaders = new Headers((init?.headers ?? {}));
|
|
36
|
+
initHeaders.delete("Content-Type");
|
|
37
|
+
const res = await fetch(url, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: initHeaders,
|
|
40
|
+
body: formData,
|
|
41
|
+
});
|
|
42
|
+
if (!res.ok) {
|
|
43
|
+
let errorMsg = `Erro ${res.status}`;
|
|
44
|
+
try {
|
|
45
|
+
const text = await res.text();
|
|
46
|
+
if (text)
|
|
47
|
+
errorMsg = text;
|
|
48
|
+
}
|
|
49
|
+
catch { /* ignore */ }
|
|
50
|
+
throw new Error(errorMsg);
|
|
51
|
+
}
|
|
52
|
+
return res;
|
|
53
|
+
}
|
|
54
|
+
return fetch(url, init);
|
|
55
|
+
},
|
|
56
|
+
onError: () => {
|
|
57
|
+
clearTimer();
|
|
58
|
+
setIsUploading(false);
|
|
59
|
+
setSyntheticError(null);
|
|
60
|
+
},
|
|
61
|
+
onFinish: () => {
|
|
62
|
+
clearTimer();
|
|
63
|
+
setIsUploading(false);
|
|
64
|
+
setSyntheticError(null);
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
const buildAttachmentUrl = useCallback((ref) => {
|
|
68
|
+
const base = `${options.endpoint}/api/v1/ai/conversations/${options.sessionId}/attachments/${encodeURIComponent(ref)}`;
|
|
69
|
+
return options.token ? `${base}?token=${encodeURIComponent(options.token)}` : base;
|
|
70
|
+
}, [options.endpoint, options.sessionId, options.token]);
|
|
71
|
+
const { isLoading, messages, stop } = chat;
|
|
72
|
+
// --- Camada 2: timeout de resposta ---
|
|
73
|
+
// Inicia timer quando começa a carregar; reseta a cada chunk recebido
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (isLoading) {
|
|
76
|
+
clearTimer();
|
|
77
|
+
timeoutRef.current = setTimeout(() => {
|
|
78
|
+
stop();
|
|
79
|
+
setSyntheticError(new Error("Tempo limite atingido. O servidor nao respondeu."));
|
|
80
|
+
}, RESPONSE_TIMEOUT_MS);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
clearTimer();
|
|
84
|
+
}
|
|
85
|
+
return clearTimer;
|
|
86
|
+
}, [isLoading, stop, clearTimer]);
|
|
87
|
+
// Reset timeout quando mensagens mudam (dados chegando)
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (isLoading && messages.length > 0) {
|
|
90
|
+
clearTimer();
|
|
91
|
+
timeoutRef.current = setTimeout(() => {
|
|
92
|
+
stop();
|
|
93
|
+
setSyntheticError(new Error("Tempo limite atingido. O servidor nao respondeu."));
|
|
94
|
+
}, RESPONSE_TIMEOUT_MS);
|
|
95
|
+
}
|
|
96
|
+
}, [messages, isLoading, stop, clearTimer]);
|
|
97
|
+
// --- Camada 3: deteccao de resposta vazia ---
|
|
98
|
+
useEffect(() => {
|
|
99
|
+
if (prevLoadingRef.current && !isLoading) {
|
|
100
|
+
const lastMessage = messages[messages.length - 1];
|
|
101
|
+
if (!chat.error && lastMessage?.role === "user") {
|
|
102
|
+
setSyntheticError(new Error("Nenhuma resposta recebida. Tente novamente."));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
prevLoadingRef.current = isLoading;
|
|
106
|
+
}, [isLoading, messages, chat.error]);
|
|
107
|
+
const error = chat.error ?? syntheticError;
|
|
108
|
+
const handleSubmit = useCallback((e, attachments) => {
|
|
109
|
+
setSyntheticError(null);
|
|
110
|
+
if (attachments && attachments.length > 0) {
|
|
111
|
+
pendingFilesRef.current = attachments.map((a) => a.file);
|
|
112
|
+
setIsUploading(true);
|
|
113
|
+
}
|
|
114
|
+
return chat.handleSubmit(e);
|
|
115
|
+
}, [chat.handleSubmit]);
|
|
116
|
+
const reload = useCallback((...args) => {
|
|
117
|
+
setSyntheticError(null);
|
|
118
|
+
return chat.reload(...args);
|
|
119
|
+
}, [chat.reload]);
|
|
120
|
+
return { ...chat, error, handleSubmit, reload, isUploading, buildAttachmentUrl };
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function useIsMobile(breakpoint?: number): boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
export function useIsMobile(breakpoint = 768) {
|
|
3
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
const mql = window.matchMedia(`(max-width: ${breakpoint - 1}px)`);
|
|
6
|
+
const onChange = () => setIsMobile(mql.matches);
|
|
7
|
+
onChange();
|
|
8
|
+
mql.addEventListener("change", onChange);
|
|
9
|
+
return () => mql.removeEventListener("change", onChange);
|
|
10
|
+
}, [breakpoint]);
|
|
11
|
+
return isMobile;
|
|
12
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export { Chat } from "./components/Chat.js";
|
|
2
|
+
export type { ChatProps } from "./components/Chat.js";
|
|
3
|
+
export { useBackboneChat } from "./hooks/useBackboneChat.js";
|
|
4
|
+
export type { UseBackboneChatOptions, Message } from "./hooks/useBackboneChat.js";
|
|
5
|
+
export { ChatProvider, useChatContext } from "./hooks/ChatProvider.js";
|
|
6
|
+
export type { ChatProviderProps } from "./hooks/ChatProvider.js";
|
|
7
|
+
export { Markdown } from "./components/Markdown.js";
|
|
8
|
+
export { StreamingIndicator } from "./components/StreamingIndicator.js";
|
|
9
|
+
export { ErrorNote } from "./components/ErrorNote.js";
|
|
10
|
+
export type { ErrorNoteProps } from "./components/ErrorNote.js";
|
|
11
|
+
export { MessageBubble } from "./components/MessageBubble.js";
|
|
12
|
+
export type { MessageBubbleProps } from "./components/MessageBubble.js";
|
|
13
|
+
export { MessageList } from "./components/MessageList.js";
|
|
14
|
+
export type { MessageListProps } from "./components/MessageList.js";
|
|
15
|
+
export { MessageInput } from "./components/MessageInput.js";
|
|
16
|
+
export type { MessageInputProps, Attachment } from "./components/MessageInput.js";
|
|
17
|
+
export { PartRenderer } from "./parts/PartRenderer.js";
|
|
18
|
+
export type { PartRendererProps } from "./parts/PartRenderer.js";
|
|
19
|
+
export { ReasoningBlock } from "./parts/ReasoningBlock.js";
|
|
20
|
+
export type { ReasoningBlockProps } from "./parts/ReasoningBlock.js";
|
|
21
|
+
export { ToolActivity, defaultToolIconMap } from "./parts/ToolActivity.js";
|
|
22
|
+
export type { ToolActivityProps, ToolActivityState } from "./parts/ToolActivity.js";
|
|
23
|
+
export { ToolResult } from "./parts/ToolResult.js";
|
|
24
|
+
export type { ToolResultProps } from "./parts/ToolResult.js";
|
|
25
|
+
export { AlertRenderer } from "./display/AlertRenderer.js";
|
|
26
|
+
export { MetricCardRenderer } from "./display/MetricCardRenderer.js";
|
|
27
|
+
export { PriceHighlightRenderer } from "./display/PriceHighlightRenderer.js";
|
|
28
|
+
export { FileCardRenderer } from "./display/FileCardRenderer.js";
|
|
29
|
+
export { CodeBlockRenderer } from "./display/CodeBlockRenderer.js";
|
|
30
|
+
export { SourcesListRenderer } from "./display/SourcesListRenderer.js";
|
|
31
|
+
export { StepTimelineRenderer } from "./display/StepTimelineRenderer.js";
|
|
32
|
+
export { ProgressStepsRenderer } from "./display/ProgressStepsRenderer.js";
|
|
33
|
+
export { ChartRenderer } from "./display/ChartRenderer.js";
|
|
34
|
+
export { CarouselRenderer } from "./display/CarouselRenderer.js";
|
|
35
|
+
export { ProductCardRenderer } from "./display/ProductCardRenderer.js";
|
|
36
|
+
export { ComparisonTableRenderer } from "./display/ComparisonTableRenderer.js";
|
|
37
|
+
export { DataTableRenderer } from "./display/DataTableRenderer.js";
|
|
38
|
+
export { SpreadsheetRenderer } from "./display/SpreadsheetRenderer.js";
|
|
39
|
+
export { GalleryRenderer } from "./display/GalleryRenderer.js";
|
|
40
|
+
export { ImageViewerRenderer } from "./display/ImageViewerRenderer.js";
|
|
41
|
+
export { LinkPreviewRenderer } from "./display/LinkPreviewRenderer.js";
|
|
42
|
+
export { MapViewRenderer } from "./display/MapViewRenderer.js";
|
|
43
|
+
export { ChoiceButtonsRenderer } from "./display/ChoiceButtonsRenderer.js";
|
|
44
|
+
export { defaultDisplayRenderers, resolveDisplayRenderer } from "./display/registry.js";
|
|
45
|
+
export type { DisplayRendererMap, DisplayActionName } from "./display/registry.js";
|
|
46
|
+
export { ConversationList, ConversationBar, useConversations, useIsMobile, formatRelativeTime, buildInitialMessages, groupConversations, } from "./conversations/index.js";
|
|
47
|
+
export type { ConversationListProps, ConversationBarProps, UseConversationsOptions, UseConversationsReturn, Conversation, BackendMessage, } from "./conversations/index.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// @codrstudio/agentic-chat
|
|
2
|
+
// Barrel exports
|
|
3
|
+
// Componente principal
|
|
4
|
+
export { Chat } from "./components/Chat.js";
|
|
5
|
+
export { useBackboneChat } from "./hooks/useBackboneChat.js";
|
|
6
|
+
export { ChatProvider, useChatContext } from "./hooks/ChatProvider.js";
|
|
7
|
+
export { Markdown } from "./components/Markdown.js";
|
|
8
|
+
export { StreamingIndicator } from "./components/StreamingIndicator.js";
|
|
9
|
+
export { ErrorNote } from "./components/ErrorNote.js";
|
|
10
|
+
export { MessageBubble } from "./components/MessageBubble.js";
|
|
11
|
+
export { MessageList } from "./components/MessageList.js";
|
|
12
|
+
export { MessageInput } from "./components/MessageInput.js";
|
|
13
|
+
export { PartRenderer } from "./parts/PartRenderer.js";
|
|
14
|
+
export { ReasoningBlock } from "./parts/ReasoningBlock.js";
|
|
15
|
+
export { ToolActivity, defaultToolIconMap } from "./parts/ToolActivity.js";
|
|
16
|
+
export { ToolResult } from "./parts/ToolResult.js";
|
|
17
|
+
// Display Renderers
|
|
18
|
+
export { AlertRenderer } from "./display/AlertRenderer.js";
|
|
19
|
+
export { MetricCardRenderer } from "./display/MetricCardRenderer.js";
|
|
20
|
+
export { PriceHighlightRenderer } from "./display/PriceHighlightRenderer.js";
|
|
21
|
+
export { FileCardRenderer } from "./display/FileCardRenderer.js";
|
|
22
|
+
export { CodeBlockRenderer } from "./display/CodeBlockRenderer.js";
|
|
23
|
+
export { SourcesListRenderer } from "./display/SourcesListRenderer.js";
|
|
24
|
+
export { StepTimelineRenderer } from "./display/StepTimelineRenderer.js";
|
|
25
|
+
export { ProgressStepsRenderer } from "./display/ProgressStepsRenderer.js";
|
|
26
|
+
export { ChartRenderer } from "./display/ChartRenderer.js";
|
|
27
|
+
export { CarouselRenderer } from "./display/CarouselRenderer.js";
|
|
28
|
+
export { ProductCardRenderer } from "./display/ProductCardRenderer.js";
|
|
29
|
+
export { ComparisonTableRenderer } from "./display/ComparisonTableRenderer.js";
|
|
30
|
+
export { DataTableRenderer } from "./display/DataTableRenderer.js";
|
|
31
|
+
export { SpreadsheetRenderer } from "./display/SpreadsheetRenderer.js";
|
|
32
|
+
export { GalleryRenderer } from "./display/GalleryRenderer.js";
|
|
33
|
+
export { ImageViewerRenderer } from "./display/ImageViewerRenderer.js";
|
|
34
|
+
export { LinkPreviewRenderer } from "./display/LinkPreviewRenderer.js";
|
|
35
|
+
export { MapViewRenderer } from "./display/MapViewRenderer.js";
|
|
36
|
+
export { ChoiceButtonsRenderer } from "./display/ChoiceButtonsRenderer.js";
|
|
37
|
+
// Display Registry
|
|
38
|
+
export { defaultDisplayRenderers, resolveDisplayRenderer } from "./display/registry.js";
|
|
39
|
+
// Conversation management
|
|
40
|
+
export { ConversationList, ConversationBar, useConversations, useIsMobile, formatRelativeTime, buildInitialMessages, groupConversations, } from "./conversations/index.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { DisplayRendererMap } from "../display/registry.js";
|
|
2
|
+
type TextPart = {
|
|
3
|
+
type: "text";
|
|
4
|
+
text: string;
|
|
5
|
+
};
|
|
6
|
+
type ReasoningPart = {
|
|
7
|
+
type: "reasoning";
|
|
8
|
+
reasoning: string;
|
|
9
|
+
};
|
|
10
|
+
type ToolInvocationPart = {
|
|
11
|
+
type: "tool-invocation";
|
|
12
|
+
toolInvocation: {
|
|
13
|
+
toolName: string;
|
|
14
|
+
toolCallId?: string;
|
|
15
|
+
state: "call" | "partial-call" | "result";
|
|
16
|
+
args?: Record<string, unknown>;
|
|
17
|
+
result?: unknown;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
type ImageAttachmentPart = {
|
|
21
|
+
type: "image";
|
|
22
|
+
_ref?: string;
|
|
23
|
+
mimeType?: string;
|
|
24
|
+
};
|
|
25
|
+
type FileAttachmentPart = {
|
|
26
|
+
type: "file";
|
|
27
|
+
_ref?: string;
|
|
28
|
+
mimeType?: string;
|
|
29
|
+
};
|
|
30
|
+
type MessagePart = TextPart | ReasoningPart | ToolInvocationPart | ImageAttachmentPart | FileAttachmentPart | {
|
|
31
|
+
type: string;
|
|
32
|
+
};
|
|
33
|
+
export interface PartRendererProps {
|
|
34
|
+
part: MessagePart;
|
|
35
|
+
isStreaming?: boolean;
|
|
36
|
+
displayRenderers?: DisplayRendererMap;
|
|
37
|
+
attachmentUrl?: (ref: string) => string;
|
|
38
|
+
}
|
|
39
|
+
export declare const PartRenderer: import("react").NamedExoticComponent<PartRendererProps>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { memo, useState } from "react";
|
|
3
|
+
import { Paperclip, FileText, ChevronDown } from "lucide-react";
|
|
4
|
+
import { Markdown } from "../components/Markdown.js";
|
|
5
|
+
import { LazyRender } from "../components/LazyRender.js";
|
|
6
|
+
import { ReasoningBlock } from "./ReasoningBlock.js";
|
|
7
|
+
import { ToolActivity } from "./ToolActivity.js";
|
|
8
|
+
import { ToolResult } from "./ToolResult.js";
|
|
9
|
+
import { resolveDisplayRenderer } from "../display/registry.js";
|
|
10
|
+
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from "../ui/collapsible.js";
|
|
11
|
+
import { cn } from "../lib/utils.js";
|
|
12
|
+
const HEAVY_RENDERERS = new Set([
|
|
13
|
+
"chart", "map", "table",
|
|
14
|
+
"spreadsheet", "gallery", "image",
|
|
15
|
+
]);
|
|
16
|
+
// ─── Attachment sub-components ────────────────────────────────────────────────
|
|
17
|
+
function AttachmentImage({ src }) {
|
|
18
|
+
const [open, setOpen] = useState(false);
|
|
19
|
+
return (_jsxs(_Fragment, { children: [_jsx("img", { src: src, loading: "lazy", alt: "Imagem anexada", className: "max-w-xs rounded-lg border cursor-pointer object-cover", onClick: () => setOpen(true) }), open && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/80", onClick: () => setOpen(false), children: _jsx("img", { src: src, alt: "Imagem em tamanho real", className: "max-w-[90vw] max-h-[90vh] rounded-lg", onClick: (e) => e.stopPropagation() }) }))] }));
|
|
20
|
+
}
|
|
21
|
+
function AudioAttachment({ src }) {
|
|
22
|
+
return (_jsx("audio", { controls: true, src: src, className: "max-w-xs w-full" }));
|
|
23
|
+
}
|
|
24
|
+
function PdfChip({ src, filename }) {
|
|
25
|
+
return (_jsxs("a", { href: src, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-2 rounded-lg border bg-muted px-3 py-2 text-sm hover:bg-muted/80 transition-colors", children: [_jsx(FileText, { className: "h-4 w-4 shrink-0 text-muted-foreground" }), _jsx("span", { className: "max-w-[200px] truncate", children: filename })] }));
|
|
26
|
+
}
|
|
27
|
+
function AttachmentTextBlock({ filename, content }) {
|
|
28
|
+
const [open, setOpen] = useState(false);
|
|
29
|
+
const lines = content.split("\n");
|
|
30
|
+
const preview = lines.slice(0, 3).join("\n") + (lines.length > 3 && !open ? "\n…" : "");
|
|
31
|
+
return (_jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: "rounded-lg border text-xs overflow-hidden", children: [_jsxs("div", { className: "flex items-center gap-2 px-3 py-2 bg-muted/50 border-b", children: [_jsx(Paperclip, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }), _jsx("span", { className: "flex-1 truncate font-medium", children: filename }), _jsx(CollapsibleTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "text-muted-foreground hover:text-foreground transition-colors", "aria-label": open ? "Recolher" : "Expandir", children: _jsx(ChevronDown, { className: cn("h-3.5 w-3.5 transition-transform", open && "rotate-180") }) }) })] }), _jsx("pre", { className: "px-3 py-2 text-muted-foreground whitespace-pre-wrap break-words", children: preview }), _jsx(CollapsibleContent, { children: _jsx("pre", { className: "px-3 py-2 max-h-40 overflow-auto whitespace-pre-wrap break-words border-t", children: content }) })] }));
|
|
32
|
+
}
|
|
33
|
+
// ─── Main renderer ────────────────────────────────────────────────────────────
|
|
34
|
+
export const PartRenderer = memo(function PartRenderer({ part, isStreaming, displayRenderers, attachmentUrl }) {
|
|
35
|
+
switch (part.type) {
|
|
36
|
+
case "text": {
|
|
37
|
+
const p = part;
|
|
38
|
+
// Pre-processed attachment text: "[📎 filename]\ncontent"
|
|
39
|
+
if (p.text.startsWith("[📎")) {
|
|
40
|
+
const firstNewline = p.text.indexOf("\n");
|
|
41
|
+
const header = firstNewline >= 0 ? p.text.slice(0, firstNewline) : p.text;
|
|
42
|
+
const body = firstNewline >= 0 ? p.text.slice(firstNewline + 1) : "";
|
|
43
|
+
const match = header.match(/^\[📎\s+(.+?)\]?$/);
|
|
44
|
+
const filename = match?.[1] ?? header;
|
|
45
|
+
return _jsx(AttachmentTextBlock, { filename: filename, content: body });
|
|
46
|
+
}
|
|
47
|
+
return _jsx(Markdown, { children: p.text });
|
|
48
|
+
}
|
|
49
|
+
case "reasoning": {
|
|
50
|
+
const p = part;
|
|
51
|
+
return _jsx(ReasoningBlock, { content: p.reasoning, isStreaming: isStreaming });
|
|
52
|
+
}
|
|
53
|
+
case "tool-invocation": {
|
|
54
|
+
const { toolInvocation } = part;
|
|
55
|
+
const isDisplay = toolInvocation.toolName.startsWith("display_");
|
|
56
|
+
if (isDisplay && toolInvocation.state === "result") {
|
|
57
|
+
const result = toolInvocation.result;
|
|
58
|
+
const action = toolInvocation.toolName.replace("display_", "");
|
|
59
|
+
const Renderer = resolveDisplayRenderer(action, displayRenderers);
|
|
60
|
+
if (Renderer) {
|
|
61
|
+
const rendered = _jsx(Renderer, { ...result });
|
|
62
|
+
if (!isStreaming && action && HEAVY_RENDERERS.has(action)) {
|
|
63
|
+
return _jsx(LazyRender, { children: rendered });
|
|
64
|
+
}
|
|
65
|
+
return rendered;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (toolInvocation.state === "result") {
|
|
69
|
+
return (_jsx(ToolResult, { toolName: toolInvocation.toolName, result: toolInvocation.result }));
|
|
70
|
+
}
|
|
71
|
+
return (_jsx(ToolActivity, { toolName: toolInvocation.toolName, state: toolInvocation.state, args: toolInvocation.args }));
|
|
72
|
+
}
|
|
73
|
+
case "image": {
|
|
74
|
+
const p = part;
|
|
75
|
+
if (!p._ref || !attachmentUrl)
|
|
76
|
+
return null;
|
|
77
|
+
return _jsx(AttachmentImage, { src: attachmentUrl(p._ref) });
|
|
78
|
+
}
|
|
79
|
+
case "file": {
|
|
80
|
+
const p = part;
|
|
81
|
+
if (!p._ref || !attachmentUrl)
|
|
82
|
+
return null;
|
|
83
|
+
const src = attachmentUrl(p._ref);
|
|
84
|
+
if (p.mimeType?.startsWith("audio/")) {
|
|
85
|
+
return _jsx(AudioAttachment, { src: src });
|
|
86
|
+
}
|
|
87
|
+
if (p.mimeType === "application/pdf") {
|
|
88
|
+
// Extract a human-readable filename from the att_ts_hex.ext pattern
|
|
89
|
+
const filename = p._ref.replace(/^att_\d+_[0-9a-f]+\./, "") || p._ref;
|
|
90
|
+
return _jsx(PdfChip, { src: src, filename: filename });
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
default:
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { Brain, ChevronDown, ChevronRight } from "lucide-react";
|
|
4
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "../ui/collapsible.js";
|
|
5
|
+
export function ReasoningBlock({ content, isStreaming = false, className }) {
|
|
6
|
+
const [expanded, setExpanded] = useState(isStreaming);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
if (isStreaming) {
|
|
9
|
+
setExpanded(true);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
setExpanded(false);
|
|
13
|
+
}
|
|
14
|
+
}, [isStreaming]);
|
|
15
|
+
return (_jsx(Collapsible, { open: expanded, onOpenChange: setExpanded, className: className, children: _jsxs("div", { className: "border border-purple-500/30 bg-purple-500/5 rounded-md text-sm overflow-hidden", children: [_jsxs(CollapsibleTrigger, { className: "flex items-center gap-2 px-3 py-2 w-full text-left font-medium text-purple-400 hover:bg-purple-500/10 cursor-pointer", children: [_jsx(Brain, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), _jsx("span", { className: "font-mono text-xs", children: "reasoning:" }), expanded
|
|
16
|
+
? _jsx(ChevronDown, { className: "h-3.5 w-3.5 ml-auto", "aria-hidden": "true" })
|
|
17
|
+
: _jsx(ChevronRight, { className: "h-3.5 w-3.5 ml-auto", "aria-hidden": "true" })] }), _jsx(CollapsibleContent, { children: _jsx("div", { className: "max-h-96 overflow-y-auto px-3 py-3 text-purple-300/80 whitespace-pre-wrap break-words border-t border-purple-500/20 text-xs", children: content }) })] }) }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type LucideIcon } from "lucide-react";
|
|
2
|
+
export type ToolActivityState = "call" | "partial-call" | "result";
|
|
3
|
+
export interface ToolActivityProps {
|
|
4
|
+
toolName: string;
|
|
5
|
+
state: ToolActivityState;
|
|
6
|
+
args?: Record<string, unknown>;
|
|
7
|
+
className?: string;
|
|
8
|
+
iconMap?: Partial<Record<string, LucideIcon>>;
|
|
9
|
+
}
|
|
10
|
+
export declare const defaultToolIconMap: Record<string, LucideIcon>;
|
|
11
|
+
export declare function ToolActivity({ toolName, state, args, className, iconMap }: ToolActivityProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Check, Download, FileText, FilePlus, FolderSearch, Globe, Loader2, Pencil, Search, Terminal, Wrench } from "lucide-react";
|
|
3
|
+
import { cn } from "../lib/utils.js";
|
|
4
|
+
export const defaultToolIconMap = {
|
|
5
|
+
WebSearch: Globe,
|
|
6
|
+
Bash: Terminal,
|
|
7
|
+
Read: FileText,
|
|
8
|
+
Edit: Pencil,
|
|
9
|
+
Write: FilePlus,
|
|
10
|
+
Grep: Search,
|
|
11
|
+
Glob: FolderSearch,
|
|
12
|
+
WebFetch: Download,
|
|
13
|
+
ListDir: FolderSearch,
|
|
14
|
+
};
|
|
15
|
+
function formatToolName(name) {
|
|
16
|
+
return name
|
|
17
|
+
.replace(/_/g, " ")
|
|
18
|
+
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
19
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
20
|
+
}
|
|
21
|
+
function resolveIcon(toolName, iconMap) {
|
|
22
|
+
return iconMap[toolName] ?? Wrench;
|
|
23
|
+
}
|
|
24
|
+
function formatArgs(toolName, args) {
|
|
25
|
+
if (!args)
|
|
26
|
+
return null;
|
|
27
|
+
if (toolName === "Bash" && args.command)
|
|
28
|
+
return String(args.command);
|
|
29
|
+
if (toolName === "Read" && args.path)
|
|
30
|
+
return String(args.path);
|
|
31
|
+
if (toolName === "Edit" && args.file_path)
|
|
32
|
+
return String(args.file_path);
|
|
33
|
+
if (toolName === "Write" && args.file_path)
|
|
34
|
+
return String(args.file_path);
|
|
35
|
+
if (toolName === "Grep" && args.pattern)
|
|
36
|
+
return String(args.pattern);
|
|
37
|
+
if (toolName === "Glob" && args.pattern)
|
|
38
|
+
return String(args.pattern);
|
|
39
|
+
if (toolName === "WebSearch" && args.query)
|
|
40
|
+
return String(args.query);
|
|
41
|
+
if (toolName === "ListDir" && args.path)
|
|
42
|
+
return String(args.path);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
export function ToolActivity({ toolName, state, args, className, iconMap }) {
|
|
46
|
+
const mergedIconMap = iconMap ? { ...defaultToolIconMap, ...iconMap } : defaultToolIconMap;
|
|
47
|
+
const Icon = resolveIcon(toolName, mergedIconMap);
|
|
48
|
+
const isActive = state === "call" || state === "partial-call";
|
|
49
|
+
const displayName = formatToolName(toolName);
|
|
50
|
+
const argsPreview = formatArgs(toolName, args);
|
|
51
|
+
return (_jsxs("div", { className: cn("flex items-center gap-2 rounded-md border px-3 py-2 text-sm", "border-amber-500/30 bg-amber-500/5", className), children: [_jsx("span", { className: "text-amber-500 shrink-0", "aria-hidden": "true", children: _jsx(Icon, { size: 14 }) }), _jsx("span", { className: "font-medium font-mono text-amber-500", children: displayName }), argsPreview && (_jsx("span", { className: "truncate text-xs text-muted-foreground font-mono max-w-[300px]", children: argsPreview })), _jsx("span", { className: "ml-auto text-muted-foreground", children: isActive ? (_jsx(Loader2, { size: 14, className: "animate-spin text-amber-500", "aria-label": "Executando..." })) : (_jsx(Check, { size: 14, className: "text-green-500", "aria-label": "Concluido" })) })] }));
|
|
52
|
+
}
|