@townco/ui 0.1.36 → 0.1.38

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.
Files changed (53) hide show
  1. package/dist/core/hooks/use-chat-input.d.ts +1 -1
  2. package/dist/core/hooks/use-chat-input.js +2 -2
  3. package/dist/core/hooks/use-chat-messages.d.ts +3 -1
  4. package/dist/core/hooks/use-chat-messages.js +36 -7
  5. package/dist/core/hooks/use-chat-session.d.ts +1 -1
  6. package/dist/core/hooks/use-chat-session.js +4 -7
  7. package/dist/core/hooks/use-tool-calls.d.ts +2 -0
  8. package/dist/core/lib/logger.d.ts +24 -0
  9. package/dist/core/lib/logger.js +108 -0
  10. package/dist/core/schemas/chat.d.ts +4 -0
  11. package/dist/core/schemas/tool-call.d.ts +2 -0
  12. package/dist/core/schemas/tool-call.js +4 -0
  13. package/dist/gui/components/ChatEmptyState.d.ts +6 -0
  14. package/dist/gui/components/ChatEmptyState.js +2 -2
  15. package/dist/gui/components/ChatInput.d.ts +4 -0
  16. package/dist/gui/components/ChatInput.js +12 -7
  17. package/dist/gui/components/ChatLayout.js +102 -8
  18. package/dist/gui/components/ChatPanelTabContent.d.ts +1 -1
  19. package/dist/gui/components/ChatPanelTabContent.js +10 -3
  20. package/dist/gui/components/ChatView.js +45 -14
  21. package/dist/gui/components/ContextUsageButton.d.ts +7 -0
  22. package/dist/gui/components/ContextUsageButton.js +18 -0
  23. package/dist/gui/components/FileSystemItem.js +6 -7
  24. package/dist/gui/components/FileSystemView.js +3 -3
  25. package/dist/gui/components/InlineToolCallSummary.d.ts +14 -0
  26. package/dist/gui/components/InlineToolCallSummary.js +110 -0
  27. package/dist/gui/components/InlineToolCallSummaryACP.d.ts +15 -0
  28. package/dist/gui/components/InlineToolCallSummaryACP.js +90 -0
  29. package/dist/gui/components/MessageContent.js +1 -1
  30. package/dist/gui/components/Response.js +2 -2
  31. package/dist/gui/components/SourceListItem.js +9 -1
  32. package/dist/gui/components/TodoListItem.js +19 -2
  33. package/dist/gui/components/ToolCall.js +21 -2
  34. package/dist/gui/components/Tooltip.d.ts +7 -0
  35. package/dist/gui/components/Tooltip.js +10 -0
  36. package/dist/gui/components/index.d.ts +4 -0
  37. package/dist/gui/components/index.js +5 -0
  38. package/dist/gui/components/tool-call-summary.d.ts +44 -0
  39. package/dist/gui/components/tool-call-summary.js +67 -0
  40. package/dist/gui/data/mockSourceData.d.ts +10 -0
  41. package/dist/gui/data/mockSourceData.js +40 -0
  42. package/dist/gui/data/mockTodoData.d.ts +10 -0
  43. package/dist/gui/data/mockTodoData.js +35 -0
  44. package/dist/gui/examples/FileSystemDemo.d.ts +5 -0
  45. package/dist/gui/examples/FileSystemDemo.js +24 -0
  46. package/dist/gui/examples/FileSystemExample.d.ts +17 -0
  47. package/dist/gui/examples/FileSystemExample.js +94 -0
  48. package/dist/sdk/schemas/session.d.ts +2 -0
  49. package/dist/sdk/transports/http.d.ts +1 -0
  50. package/dist/sdk/transports/http.js +40 -8
  51. package/dist/sdk/transports/stdio.js +13 -0
  52. package/dist/tui/components/ChatView.js +5 -5
  53. package/package.json +3 -3
@@ -1,10 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
+ import { mockSourceData } from "../data/mockSourceData.js";
4
+ import { mockTodoData } from "../data/mockTodoData.js";
3
5
  import { cn } from "../lib/utils.js";
4
6
  import { FileSystemView } from "./FileSystemView.js";
5
7
  import { SourceListItem } from "./SourceListItem.js";
8
+ import { TodoList } from "./TodoList.js";
6
9
  export const TodoTabContent = React.forwardRef(({ todos, className, ...props }, ref) => {
7
- return (_jsx("div", { ref: ref, className: cn("space-y-2", className), ...props, children: todos.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-full min-h-[200px]", children: _jsx("p", { className: "text-paragraph-sm text-muted-foreground", children: "No todos yet" }) })) : (todos.map((todo) => (_jsx("div", { className: "text-paragraph-sm", children: todo.text }, todo.id)))) }));
10
+ // Use mock data if no todos provided or if empty array
11
+ const displayTodos = todos && todos.length > 0 ? todos : mockTodoData;
12
+ return (_jsx("div", { ref: ref, className: cn("space-y-2", className), ...props, children: _jsx(TodoList, { todos: displayTodos }) }));
8
13
  });
9
14
  TodoTabContent.displayName = "TodoTabContent";
10
15
  export const FilesTabContent = React.forwardRef(({ files = [], provider, onFileSelect, className, ...props }, ref) => {
@@ -29,8 +34,10 @@ export const FilesTabContent = React.forwardRef(({ files = [], provider, onFileS
29
34
  }, onDownload: handleDownload, onRename: handleRename, onDelete: handleDelete, className: "h-full" }) }));
30
35
  });
31
36
  FilesTabContent.displayName = "FilesTabContent";
32
- export const SourcesTabContent = React.forwardRef(({ sources = [], className, ...props }, ref) => {
33
- return (_jsx("div", { ref: ref, className: cn("space-y-2", className), ...props, children: sources.length === 0 ? (_jsx("div", { className: "flex items-center justify-center h-full min-h-[200px]", children: _jsx("p", { className: "text-paragraph-sm text-muted-foreground", children: "No sources available" }) })) : (sources.map((source) => (_jsx(SourceListItem, { source: source }, source.id)))) }));
37
+ export const SourcesTabContent = React.forwardRef(({ sources, className, ...props }, ref) => {
38
+ // Use mock data if no sources provided or if empty array
39
+ const displaySources = sources && sources.length > 0 ? sources : mockSourceData;
40
+ return (_jsx("div", { ref: ref, className: cn("space-y-2", className), ...props, children: displaySources.map((source) => (_jsx(SourceListItem, { source: source }, source.id))) }));
34
41
  });
35
42
  SourcesTabContent.displayName = "SourcesTabContent";
36
43
  export const DatabaseTabContent = React.forwardRef(({ data, className, ...props }, ref) => {
@@ -1,12 +1,27 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { createLogger } from "@townco/core";
3
3
  import { ArrowUp, ChevronUp, Code, PanelRight, Settings, Sparkles, } from "lucide-react";
4
4
  import { useEffect, useState } from "react";
5
5
  import { useChatMessages, useChatSession, useToolCalls, } from "../../core/hooks/index.js";
6
6
  import { useChatStore } from "../../core/store/chat-store.js";
7
+ import { calculateTokenPercentage, formatTokenPercentage, } from "../../core/utils/model-context.js";
7
8
  import { cn } from "../lib/utils.js";
8
- import { ChatEmptyState, ChatHeader, ChatInputActions, ChatInputAttachment, ChatInputCommandMenu, ChatInputField, ChatInputRoot, ChatInputSubmit, ChatInputToolbar, ChatInputVoiceInput, ChatLayout, FilesTabContent, Message, MessageContent, PanelTabsHeader, SourcesTabContent, Tabs, TabsContent, TodoTabContent, } from "./index.js";
9
+ import { ChatEmptyState, ChatHeader, ChatInputActions, ChatInputAttachment, ChatInputCommandMenu, ChatInputField, ChatInputRoot, ChatInputSubmit, ChatInputToolbar, ChatInputVoiceInput, ChatLayout, ContextUsageButton, FilesTabContent, Message, MessageContent, PanelTabsHeader, SourcesTabContent, Tabs, TabsContent, TodoTabContent, } from "./index.js";
9
10
  const logger = createLogger("gui");
11
+ // Helper component to provide openFiles callback
12
+ function OpenFilesButton({ children, }) {
13
+ const { setPanelSize, setActiveTab } = ChatLayout.useChatLayoutContext();
14
+ const openFiles = () => {
15
+ setPanelSize("small");
16
+ setActiveTab("files");
17
+ };
18
+ return _jsx(_Fragment, { children: children({ openFiles }) });
19
+ }
20
+ // Controlled Tabs component for the aside panel
21
+ function AsideTabs() {
22
+ const { activeTab, setActiveTab } = ChatLayout.useChatLayoutContext();
23
+ return (_jsxs(Tabs, { value: activeTab, onValueChange: (value) => setActiveTab(value), className: "flex flex-col h-full", children: [_jsx("div", { className: cn("border-b border-border bg-card", "px-6 py-2 h-16", "flex items-center", "[border-bottom-width:0.5px]"), children: _jsx(PanelTabsHeader, { showIcons: true, visibleTabs: ["todo", "files", "sources"], variant: "compact" }) }), _jsx(TabsContent, { value: "todo", className: "flex-1 p-4 mt-0", children: _jsx(TodoTabContent, {}) }), _jsx(TabsContent, { value: "files", className: "flex-1 p-4 mt-0", children: _jsx(FilesTabContent, {}) }), _jsx(TabsContent, { value: "sources", className: "flex-1 p-4 mt-0", children: _jsx(SourcesTabContent, {}) })] }));
24
+ }
10
25
  // Mobile header component that uses ChatHeader context
11
26
  function MobileHeader({ agentName, showHeader, }) {
12
27
  const { isExpanded, setIsExpanded } = ChatHeader.useChatHeaderContext();
@@ -21,12 +36,14 @@ function AppChatHeader({ agentName, todos, sources, showHeader, }) {
21
36
  }
22
37
  export function ChatView({ client, initialSessionId, error: initError, }) {
23
38
  // Use shared hooks from @townco/ui/core - MUST be called before any early returns
24
- const { connectionStatus, connect, sessionId } = useChatSession(client, initialSessionId);
25
- const { messages, sendMessage } = useChatMessages(client);
39
+ const { connectionStatus, connect, sessionId, startSession } = useChatSession(client, initialSessionId);
40
+ const { messages, sendMessage } = useChatMessages(client, startSession);
26
41
  useToolCalls(client); // Still need to subscribe to tool call events
27
42
  const error = useChatStore((state) => state.error);
43
+ const currentModel = useChatStore((state) => state.currentModel);
28
44
  const [agentName, setAgentName] = useState("Agent");
29
45
  const [isLargeScreen, setIsLargeScreen] = useState(typeof window !== "undefined" ? window.innerWidth >= 1024 : true);
46
+ const [placeholder, setPlaceholder] = useState("Type a message or / for commands...");
30
47
  // Log connection status changes
31
48
  useEffect(() => {
32
49
  logger.debug("Connection status changed", { status: connectionStatus });
@@ -57,6 +74,14 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
57
74
  mediaQuery.removeEventListener("change", handleChange);
58
75
  };
59
76
  }, []);
77
+ // Handle prompt hover - temporarily show the full prompt as placeholder
78
+ const handlePromptHover = (prompt) => {
79
+ setPlaceholder(prompt);
80
+ };
81
+ // Handle prompt leave - restore the default placeholder
82
+ const handlePromptLeave = () => {
83
+ setPlaceholder("Type a message or / for commands...");
84
+ };
60
85
  // If there's an initialization error, show error UI (after all hooks have been called)
61
86
  if (initError) {
62
87
  return (_jsx("div", { className: "flex items-center justify-center h-screen bg-background", children: _jsxs("div", { className: "text-center p-8 max-w-md", children: [_jsx("h1", { className: "text-2xl font-bold text-destructive mb-4", children: "Initialization Error" }), _jsx("p", { className: "text-foreground mb-4", children: initError }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Failed to initialize the ACP client. Check the console for details." })] }) }));
@@ -98,6 +123,11 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
98
123
  favicon: "https://www.google.com/s2/favicons?domain=theverge.com&sz=32",
99
124
  },
100
125
  ];
126
+ // Get the latest token usage from the most recent assistant message
127
+ const latestTokenUsage = messages
128
+ .slice()
129
+ .reverse()
130
+ .find((msg) => msg.role === "assistant" && msg.tokenUsage)?.tokenUsage;
101
131
  // Command menu items for chat input
102
132
  const commandMenuItems = [
103
133
  {
@@ -141,15 +171,16 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
141
171
  },
142
172
  },
143
173
  ];
144
- return (_jsxs(ChatLayout.Root, { defaultPanelSize: "hidden", defaultActiveTab: "todo", children: [_jsxs(ChatLayout.Main, { children: [_jsx(AppChatHeader, { agentName: agentName, todos: todos, sources: sources, showHeader: messages.length > 0 }), connectionStatus === "error" && error && (_jsx("div", { className: "border-b border-destructive/20 bg-destructive/10 px-6 py-4", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "mb-1 text-paragraph-sm font-semibold text-destructive", children: "Connection Error" }), _jsx("p", { className: "whitespace-pre-line text-paragraph-sm text-foreground", children: error })] }), _jsx("button", { type: "button", onClick: connect, className: "rounded-lg bg-destructive px-4 py-2 text-paragraph-sm font-medium text-destructive-foreground transition-colors hover:bg-destructive-hover", children: "Retry" })] }) })), _jsxs(ChatLayout.Body, { children: [_jsx(ChatLayout.Messages, { children: messages.length === 0 ? (_jsx("div", { className: "flex flex-1 items-center px-4", children: _jsx(ChatEmptyState, { title: agentName, description: "This agent can help you with your tasks. Start a conversation by typing a message below.", suggestedPrompts: [
145
- "Search the web for the latest news on top tech company earnings, produce a summary for each company, and then a macro trend analysis of the tech industry. Use your todo list",
146
- "Explain how this works",
147
- "Create a new feature",
148
- "Review my changes",
149
- ], onPromptClick: (prompt) => {
150
- sendMessage(prompt);
151
- logger.info("Prompt clicked", { prompt });
152
- } }) })) : (_jsx("div", { className: "flex flex-col px-4", children: messages.map((message, index) => {
174
+ return (_jsxs(ChatLayout.Root, { defaultPanelSize: "hidden", defaultActiveTab: "todo", children: [_jsxs(ChatLayout.Main, { children: [_jsx(AppChatHeader, { agentName: agentName, todos: todos, sources: sources, showHeader: messages.length > 0 }), connectionStatus === "error" && error && (_jsx("div", { className: "border-b border-destructive/20 bg-destructive/10 px-6 py-4", children: _jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "mb-1 text-paragraph-sm font-semibold text-destructive", children: "Connection Error" }), _jsx("p", { className: "whitespace-pre-line text-paragraph-sm text-foreground", children: error })] }), _jsx("button", { type: "button", onClick: connect, className: "rounded-lg bg-destructive px-4 py-2 text-paragraph-sm font-medium text-destructive-foreground transition-colors hover:bg-destructive-hover", children: "Retry" })] }) })), _jsxs(ChatLayout.Body, { children: [_jsx(ChatLayout.Messages, { children: messages.length === 0 ? (_jsx(OpenFilesButton, { children: ({ openFiles }) => (_jsx("div", { className: "flex flex-1 items-center px-4", children: _jsx(ChatEmptyState, { title: agentName, description: "This agent can help you with your tasks. Start a conversation by typing a message below.", suggestedPrompts: [
175
+ "Search the web for the latest news on top tech company earnings, produce a summary for each company, and then a macro trend analysis of the tech industry. Use your todo list",
176
+ "Explain how this works",
177
+ "Create a new feature",
178
+ "Review my changes",
179
+ ], onPromptClick: (prompt) => {
180
+ sendMessage(prompt);
181
+ setPlaceholder("Type a message or / for commands...");
182
+ logger.info("Prompt clicked", { prompt });
183
+ }, onPromptHover: handlePromptHover, onPromptLeave: handlePromptLeave, onOpenFiles: openFiles }) })) })) : (_jsx("div", { className: "flex flex-col px-4", children: messages.map((message, index) => {
153
184
  // Calculate dynamic spacing based on message sequence
154
185
  const isFirst = index === 0;
155
186
  const previousMessage = isFirst ? null : messages[index - 1];
@@ -168,5 +199,5 @@ export function ChatView({ client, initialSessionId, error: initError, }) {
168
199
  previousMessage?.role === "assistant" ? "mt-2" : "mt-6";
169
200
  }
170
201
  return (_jsx(Message, { message: message, className: spacingClass, isLastMessage: index === messages.length - 1, children: _jsx(MessageContent, { message: message, thinkingDisplayStyle: "collapsible" }) }, message.id));
171
- }) })) }), _jsx(ChatLayout.Footer, { children: _jsxs(ChatInputRoot, { client: client, children: [_jsx(ChatInputCommandMenu, { commands: commandMenuItems }), _jsx(ChatInputField, { placeholder: "Type a message or / for commands...", autoFocus: true }), _jsxs(ChatInputToolbar, { children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(ChatInputActions, {}), _jsx(ChatInputAttachment, {})] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(ChatInputVoiceInput, {}), _jsx(ChatInputSubmit, { children: _jsx(ArrowUp, { className: "size-4" }) })] })] })] }) })] })] }), isLargeScreen && (_jsx(ChatLayout.Aside, { breakpoint: "lg", children: _jsxs(Tabs, { defaultValue: "todo", className: "flex flex-col h-full", children: [_jsx("div", { className: cn("border-b border-border bg-card", "px-6 py-2 h-16", "flex items-center", "[border-bottom-width:0.5px]"), children: _jsx(PanelTabsHeader, { showIcons: true, visibleTabs: ["todo", "files", "sources"], variant: "compact" }) }), _jsx(TabsContent, { value: "todo", className: "flex-1 p-4 mt-0", children: _jsx(TodoTabContent, { todos: todos }) }), _jsx(TabsContent, { value: "files", className: "flex-1 p-4 mt-0", children: _jsx(FilesTabContent, {}) }), _jsx(TabsContent, { value: "sources", className: "flex-1 p-4 mt-0", children: _jsx(SourcesTabContent, { sources: sources }) })] }) }))] }));
202
+ }) })) }), _jsx(ChatLayout.Footer, { children: _jsxs(ChatInputRoot, { client: client, startSession: startSession, children: [_jsx(ChatInputCommandMenu, { commands: commandMenuItems }), _jsx(ChatInputField, { placeholder: placeholder, autoFocus: true }), _jsxs(ChatInputToolbar, { children: [_jsxs("div", { className: "flex items-baseline gap-1", children: [_jsx(ChatInputActions, {}), _jsx(ChatInputAttachment, {}), latestTokenUsage && (_jsx(ContextUsageButton, { percentage: calculateTokenPercentage(latestTokenUsage.totalTokens ?? 0, currentModel ?? undefined), tokens: latestTokenUsage.totalTokens ?? 0, formattedPercentage: formatTokenPercentage(latestTokenUsage.totalTokens ?? 0, currentModel ?? undefined) }))] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(ChatInputVoiceInput, {}), _jsx(ChatInputSubmit, { children: _jsx(ArrowUp, { className: "size-4" }) })] })] })] }) })] })] }), isLargeScreen && (_jsx(ChatLayout.Aside, { breakpoint: "lg", children: _jsx(AsideTabs, {}) }))] }));
172
203
  }
@@ -0,0 +1,7 @@
1
+ import * as React from "react";
2
+ export interface ContextUsageButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
3
+ percentage: number;
4
+ tokens: number;
5
+ formattedPercentage: string;
6
+ }
7
+ export declare const ContextUsageButton: React.ForwardRefExoticComponent<ContextUsageButtonProps & React.RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { cn } from "../lib/utils.js";
4
+ import { Button } from "./Button.js";
5
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./Tooltip.js";
6
+ export const ContextUsageButton = React.forwardRef(({ percentage, tokens, formattedPercentage, className, ...props }, ref) => {
7
+ // Clamp percentage between 0 and 100
8
+ const clampedPercentage = Math.min(100, Math.max(0, percentage));
9
+ // SVG parameters
10
+ const size = 16;
11
+ const strokeWidth = 2;
12
+ const radius = (size - strokeWidth) / 2;
13
+ const center = size / 2;
14
+ const circumference = 2 * Math.PI * radius;
15
+ const offset = circumference - (clampedPercentage / 100) * circumference;
16
+ return (_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Button, { ref: ref, type: "button", variant: "ghost", size: "icon", className: cn("rounded-full cursor-default", className), ...props, children: _jsxs("svg", { width: size, height: size, viewBox: `0 0 ${size} ${size}`, className: "transform -rotate-90", children: [_jsx("circle", { cx: center, cy: center, r: radius, stroke: "currentColor", strokeWidth: strokeWidth, fill: "transparent", className: "opacity-20" }), _jsx("circle", { cx: center, cy: center, r: radius, stroke: "currentColor", strokeWidth: strokeWidth, fill: "transparent", strokeDasharray: circumference, strokeDashoffset: offset, strokeLinecap: "round", className: "transition-all duration-300 ease-in-out" })] }) }) }), _jsx(TooltipContent, { side: "top", align: "center", children: _jsxs("p", { children: ["Context: ", formattedPercentage, " (", tokens.toLocaleString(), " tokens)"] }) })] }) }));
17
+ });
18
+ ContextUsageButton.displayName = "ContextUsageButton";
@@ -10,7 +10,6 @@ import { cn } from "../lib/utils.js";
10
10
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "./DropdownMenu.js";
11
11
  export function FileSystemItem({ item, level = 0, onSelect, selectedId, isDropTarget = false, onDownload, onRename, onDelete, }) {
12
12
  const [isExpanded, setIsExpanded] = React.useState(true);
13
- const [isFocused, setIsFocused] = React.useState(false);
14
13
  const isSelected = selectedId === item.id;
15
14
  const handleToggle = () => {
16
15
  if (item.type === "folder") {
@@ -49,7 +48,7 @@ export function FileSystemItem({ item, level = 0, onSelect, selectedId, isDropTa
49
48
  };
50
49
  return (_jsxs("div", { className: "flex flex-col w-full", children: [_jsxs("div", { role: "button", tabIndex: 0, "aria-expanded": item.type === "folder" ? isExpanded : undefined, className: cn(
51
50
  // Base styles with semantic typography
52
- "group flex items-center gap-2 rounded-md cursor-pointer transition-colors text-paragraph-sm", getPaddingClass(),
51
+ "group/item flex items-center gap-2 rounded-md cursor-pointer transition-colors text-paragraph-sm", getPaddingClass(),
53
52
  // State styles using semantic colors
54
53
  // Default: transparent background
55
54
  // Hover: accent-hover background
@@ -62,11 +61,11 @@ export function FileSystemItem({ item, level = 0, onSelect, selectedId, isDropTa
62
61
  isDropTarget && [
63
62
  "bg-accent",
64
63
  "border border-dashed border-border-dark",
65
- ]), onClick: handleClick, onKeyDown: handleKeyDown, onFocus: () => setIsFocused(true), onBlur: () => setIsFocused(false), children: [_jsx("div", { className: "shrink-0 size-4 flex items-center justify-center text-foreground", children: item.type === "folder" ? (isExpanded ? (_jsx(FolderOpen, { className: "size-4" })) : (_jsx(Folder, { className: "size-4" }))) : (_jsx(FileCode, { className: "size-4" })) }), _jsx("p", { className: "flex-1 text-foreground whitespace-nowrap overflow-hidden text-ellipsis", children: item.name }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: cn("shrink-0 size-4 transition-opacity text-muted-foreground hover:text-foreground",
66
- // Hidden by default, shown on group hover
67
- "opacity-0 group-hover:opacity-100",
68
- // Always hidden when focused (to show clean state)
69
- isFocused && "opacity-0"), onClick: (e) => {
64
+ ]), onClick: handleClick, onKeyDown: handleKeyDown, children: [_jsx("div", { className: "shrink-0 size-4 flex items-center justify-center text-foreground", children: item.type === "folder" ? (isExpanded ? (_jsx(FolderOpen, { className: "size-4" })) : (_jsx(Folder, { className: "size-4" }))) : (_jsx(FileCode, { className: "size-4" })) }), _jsx("p", { className: "flex-1 text-foreground whitespace-nowrap overflow-hidden text-ellipsis", children: item.name }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { className: cn("shrink-0 size-4 transition-opacity text-muted-foreground hover:text-foreground",
65
+ // Hidden by default, shown on group hover only
66
+ "opacity-0 group-hover/item:opacity-100",
67
+ // Force visible when menu is open
68
+ "data-[state=open]:opacity-100"), onClick: (e) => {
70
69
  e.stopPropagation();
71
70
  }, "aria-label": "More options", type: "button", tabIndex: -1, children: _jsx(MoreVertical, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", side: "bottom", sideOffset: 5, alignOffset: 0, collisionPadding: 8, className: "w-40 z-[100]", onClick: (e) => e.stopPropagation(), children: [onDownload && (_jsx(DropdownMenuItem, { onClick: (e) => {
72
71
  e.stopPropagation();
@@ -37,10 +37,10 @@ export function FileSystemView({ className, provider = defaultProvider, onItemSe
37
37
  onItemSelect?.(item);
38
38
  };
39
39
  if (isLoading) {
40
- return (_jsx("div", { className: cn("p-4", className), children: _jsx("p", { className: "text-sm text-muted-foreground", children: "Loading..." }) }));
40
+ return (_jsx("div", { className: cn("", className), children: _jsx("p", { className: "text-sm text-muted-foreground", children: "Loading..." }) }));
41
41
  }
42
42
  if (error) {
43
- return (_jsx("div", { className: cn("p-4", className), children: _jsxs("p", { className: "text-sm text-destructive", children: ["Error: ", error] }) }));
43
+ return (_jsx("div", { className: cn("", className), children: _jsxs("p", { className: "text-sm text-destructive", children: ["Error: ", error] }) }));
44
44
  }
45
- return (_jsx("div", { className: cn("flex flex-col px-4 py-3", className), children: items.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No items found" })) : (items.map((item) => (_jsx(FileSystemItem, { item: item, onSelect: handleItemSelect, ...(selectedId && { selectedId }), ...(onDownload && { onDownload }), ...(onRename && { onRename }), ...(onDelete && { onDelete }) }, item.id)))) }));
45
+ return (_jsx("div", { className: cn("flex flex-col", className), children: items.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: "No items found" })) : (items.map((item) => (_jsx(FileSystemItem, { item: item, onSelect: handleItemSelect, ...(selectedId && { selectedId }), ...(onDownload && { onDownload }), ...(onRename && { onRename }), ...(onDelete && { onDelete }) }, item.id)))) }));
46
46
  }
@@ -0,0 +1,14 @@
1
+ import type { AcpClient } from "../../sdk/client/acp-client.js";
2
+ import type { ToolCall } from "../../core/schemas/tool-call.js";
3
+ export interface InlineToolCallSummaryProps {
4
+ /** The tool call to summarize */
5
+ toolCall: ToolCall;
6
+ /** ACP client for making requests */
7
+ client: AcpClient | null;
8
+ }
9
+ /**
10
+ * Inline tool call summary that appears beneath completed tool calls
11
+ * Shows a brief AI-generated summary of what the tool accomplished
12
+ * Uses the ACP client to make the request
13
+ */
14
+ export declare function InlineToolCallSummary({ toolCall, client, }: InlineToolCallSummaryProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,110 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CheckCircle, Sparkles } from "lucide-react";
3
+ import { useEffect, useState } from "react";
4
+ /**
5
+ * Inline tool call summary that appears beneath completed tool calls
6
+ * Shows a brief AI-generated summary of what the tool accomplished
7
+ * Uses the ACP client to make the request
8
+ */
9
+ export function InlineToolCallSummary({ toolCall, client, }) {
10
+ const [summary, setSummary] = useState(null);
11
+ const [loading, setLoading] = useState(false);
12
+ useEffect(() => {
13
+ // Only generate summary for completed tool calls
14
+ if (toolCall.status !== "completed" || !client) {
15
+ return;
16
+ }
17
+ // Don't fetch if we already have a summary
18
+ if (summary) {
19
+ return;
20
+ }
21
+ const fetchSummary = async () => {
22
+ setLoading(true);
23
+ try {
24
+ // Create a minimal Anthropic-format request with just this tool call
25
+ const request = {
26
+ messages: [
27
+ {
28
+ role: "assistant",
29
+ content: [
30
+ {
31
+ type: "tool_use",
32
+ id: toolCall.id,
33
+ name: toolCall.title.toLowerCase().replace(/\s+/g, "_"),
34
+ input: toolCall.rawInput || {},
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ role: "user",
40
+ content: [
41
+ {
42
+ type: "tool_result",
43
+ tool_use_id: toolCall.id,
44
+ content: extractToolOutput(toolCall),
45
+ },
46
+ ],
47
+ },
48
+ ],
49
+ };
50
+ // Make ACP RPC request to agent/summarize method
51
+ const response = await client.request("agent/summarize", request);
52
+ // Extract the description from the first tool call
53
+ if (response.toolCalls && response.toolCalls.length > 0) {
54
+ const firstToolCall = response.toolCalls[0];
55
+ if (firstToolCall) {
56
+ setSummary(firstToolCall.description);
57
+ }
58
+ }
59
+ }
60
+ catch (error) {
61
+ // Silently fail - summaries are optional enhancements
62
+ console.debug("Failed to fetch tool call summary via ACP:", error);
63
+ }
64
+ finally {
65
+ setLoading(false);
66
+ }
67
+ };
68
+ fetchSummary();
69
+ }, [toolCall, client, summary]);
70
+ // Don't show anything if not completed or still loading
71
+ if (toolCall.status !== "completed" || loading) {
72
+ return null;
73
+ }
74
+ // Don't show if no summary
75
+ if (!summary) {
76
+ return null;
77
+ }
78
+ return (_jsxs("div", { className: "flex items-start gap-2 mt-2 px-3 py-2 rounded-lg bg-emerald-50/50 border border-emerald-100", children: [_jsx(CheckCircle, { className: "h-3.5 w-3.5 text-emerald-600 shrink-0 mt-0.5" }), _jsx("div", { className: "flex-1 min-w-0", children: _jsx("p", { className: "text-[11px] text-emerald-900 font-medium", children: summary }) }), _jsx(Sparkles, { className: "h-3 w-3 text-emerald-600/50 shrink-0 mt-0.5" })] }));
79
+ }
80
+ /**
81
+ * Extract output text from a tool call for summarization
82
+ */
83
+ function extractToolOutput(toolCall) {
84
+ // Try to extract text from content blocks
85
+ if (toolCall.content && toolCall.content.length > 0) {
86
+ const textBlocks = [];
87
+ for (const block of toolCall.content) {
88
+ if (block.type === "text" && "text" in block) {
89
+ textBlocks.push(block.text);
90
+ }
91
+ else if (block.type === "content" && "content" in block) {
92
+ const innerContent = block.content;
93
+ if (innerContent.type === "text" && innerContent.text) {
94
+ textBlocks.push(innerContent.text);
95
+ }
96
+ }
97
+ }
98
+ if (textBlocks.length > 0) {
99
+ // Truncate very long outputs for summary purposes
100
+ const output = textBlocks.join("\n");
101
+ return output.length > 500 ? `${output.slice(0, 500)}...` : output;
102
+ }
103
+ }
104
+ // Fallback to raw output
105
+ if (toolCall.rawOutput) {
106
+ const output = JSON.stringify(toolCall.rawOutput);
107
+ return output.length > 500 ? `${output.slice(0, 500)}...` : output;
108
+ }
109
+ return "No output";
110
+ }
@@ -0,0 +1,15 @@
1
+ import type { AcpClient } from "../../sdk/client/acp-client.js";
2
+ import type { ToolCall } from "../../core/schemas/tool-call.js";
3
+ export interface InlineToolCallSummaryACPProps {
4
+ /** The tool call to summarize */
5
+ toolCall: ToolCall;
6
+ /** ACP client for making requests */
7
+ client: AcpClient | null;
8
+ }
9
+ /**
10
+ * Inline tool call summary using ACP protocol
11
+ *
12
+ * This version uses the ACP client to make a custom method call
13
+ * instead of direct HTTP fetch
14
+ */
15
+ export declare function InlineToolCallSummaryACP({ toolCall, client, }: InlineToolCallSummaryACPProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CheckCircle, Sparkles } from "lucide-react";
3
+ import { useEffect, useState } from "react";
4
+ /**
5
+ * Inline tool call summary using ACP protocol
6
+ *
7
+ * This version uses the ACP client to make a custom method call
8
+ * instead of direct HTTP fetch
9
+ */
10
+ export function InlineToolCallSummaryACP({ toolCall, client, }) {
11
+ const [summary, setSummary] = useState(null);
12
+ const [loading, setLoading] = useState(false);
13
+ useEffect(() => {
14
+ if (toolCall.status !== "completed" || !client || summary) {
15
+ return;
16
+ }
17
+ const fetchSummary = async () => {
18
+ setLoading(true);
19
+ try {
20
+ // This would require adding a custom ACP method to the server
21
+ // For now, this shows the concept - you'd need to implement
22
+ // the corresponding handler in the ACP adapter
23
+ // Example of what it might look like:
24
+ // const response = await client.request({
25
+ // method: "agent/summarize",
26
+ // params: {
27
+ // messages: [
28
+ // {
29
+ // role: "assistant",
30
+ // content: [{
31
+ // type: "tool_use",
32
+ // id: toolCall.id,
33
+ // name: toolCall.title,
34
+ // input: toolCall.rawInput || {}
35
+ // }]
36
+ // },
37
+ // {
38
+ // role: "user",
39
+ // content: [{
40
+ // type: "tool_result",
41
+ // tool_use_id: toolCall.id,
42
+ // content: extractToolOutput(toolCall)
43
+ // }]
44
+ // }
45
+ // ]
46
+ // }
47
+ // });
48
+ // For now, fall back to direct fetch since the ACP method
49
+ // isn't implemented
50
+ console.log("ACP-based summary not yet implemented");
51
+ }
52
+ catch (error) {
53
+ console.debug("Failed to fetch tool call summary via ACP:", error);
54
+ }
55
+ finally {
56
+ setLoading(false);
57
+ }
58
+ };
59
+ fetchSummary();
60
+ }, [toolCall, client, summary]);
61
+ if (toolCall.status !== "completed" || loading || !summary) {
62
+ return null;
63
+ }
64
+ return (_jsxs("div", { className: "flex items-start gap-2 mt-2 px-3 py-2 rounded-lg bg-emerald-50/50 border border-emerald-100", children: [_jsx(CheckCircle, { className: "h-3.5 w-3.5 text-emerald-600 shrink-0 mt-0.5" }), _jsx("div", { className: "flex-1 min-w-0", children: _jsx("p", { className: "text-[11px] text-emerald-900 font-medium", children: summary }) }), _jsx(Sparkles, { className: "h-3 w-3 text-emerald-600/50 shrink-0 mt-0.5" })] }));
65
+ }
66
+ function extractToolOutput(toolCall) {
67
+ if (toolCall.content && toolCall.content.length > 0) {
68
+ const textBlocks = [];
69
+ for (const block of toolCall.content) {
70
+ if (block.type === "text" && "text" in block) {
71
+ textBlocks.push(block.text);
72
+ }
73
+ else if (block.type === "content" && "content" in block) {
74
+ const innerContent = block.content;
75
+ if (innerContent.type === "text" && innerContent.text) {
76
+ textBlocks.push(innerContent.text);
77
+ }
78
+ }
79
+ }
80
+ if (textBlocks.length > 0) {
81
+ const output = textBlocks.join("\n");
82
+ return output.length > 500 ? `${output.slice(0, 500)}...` : output;
83
+ }
84
+ }
85
+ if (toolCall.rawOutput) {
86
+ const output = JSON.stringify(toolCall.rawOutput);
87
+ return output.length > 500 ? `${output.slice(0, 500)}...` : output;
88
+ }
89
+ return "No output";
90
+ }
@@ -135,7 +135,7 @@ export const MessageContent = React.forwardRef(({ role: roleProp, variant, isStr
135
135
  }
136
136
  }
137
137
  return _jsx(_Fragment, { children: elements });
138
- })()) : (_jsx("div", { className: "whitespace-pre-wrap", children: message.content })), message.role === "assistant" && message.tokenUsage && (_jsx("div", { className: "mt-3 pt-2 border-t border-border/30 text-caption text-muted-foreground/60", children: _jsxs("span", { children: ["Context:", " ", formatTokenPercentage(message.tokenUsage.totalTokens ?? 0, currentModel ?? undefined), " ", "(", (message.tokenUsage.totalTokens ?? 0).toLocaleString(), " ", "tokens)"] }) }))] }));
138
+ })()) : (_jsx("div", { className: "whitespace-pre-wrap", children: message.content }))] }));
139
139
  }
140
140
  return (_jsx("div", { ref: ref, className: cn(messageContentVariants({ role, variant }), isStreaming && "animate-pulse-subtle", className), ...props, children: content }));
141
141
  });
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import ReactMarkdown from "react-markdown";
4
4
  import remarkGfm from "remark-gfm";
@@ -92,6 +92,6 @@ export const Response = React.forwardRef(({ content, isStreaming = false, showEm
92
92
  // Horizontal rule
93
93
  hr: ({ node, ...props }) => (_jsx("hr", { className: "my-6 border-t border-border opacity-50", ...props })),
94
94
  };
95
- return (_jsxs("div", { ref: ref, className: cn("markdown-content prose prose-sm max-w-none dark:prose-invert", className), ...props, children: [_jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: components, children: content }), isStreaming && content && (_jsx("span", { className: "inline-block ml-1 animate-typing text-primary", children: "..." }))] }));
95
+ return (_jsx("div", { ref: ref, className: cn("markdown-content prose prose-sm max-w-none dark:prose-invert", className), ...props, children: _jsx(ReactMarkdown, { remarkPlugins: [remarkGfm], components: components, children: content }) }));
96
96
  });
97
97
  Response.displayName = "Response";
@@ -2,6 +2,14 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "../lib/utils.js";
4
4
  export const SourceListItem = React.forwardRef(({ source, isSelected, className, ...props }, ref) => {
5
- return (_jsxs("button", { ref: ref, type: "button", className: cn("flex w-full text-left gap-2 items-start p-3 rounded-lg transition-colors cursor-pointer border border-transparent", "hover:bg-accent-hover hover:border-border/50", isSelected && "bg-accent-hover border-border/50", className), onClick: () => window.open(source.url, "_blank"), ...props, children: [_jsx("div", { className: "flex gap-2 items-center py-[2px] shrink-0", children: _jsx("div", { className: "relative rounded-[3px] shrink-0 size-4 overflow-hidden bg-muted", children: source.favicon ? (_jsx("img", { alt: source.sourceName, className: "size-full object-cover", src: source.favicon })) : (_jsx("div", { className: "size-full bg-muted" })) }) }), _jsxs("div", { className: "flex flex-1 flex-col gap-1 min-w-0", children: [_jsxs("div", { className: "text-caption leading-normal text-foreground", children: [_jsx("span", { className: "font-medium", children: source.sourceName }), _jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", source.title] })] }), _jsx("p", { className: "text-caption leading-relaxed text-muted-foreground line-clamp-3", children: source.snippet })] })] }));
5
+ return (_jsxs("button", { ref: ref, type: "button", className: cn(
6
+ // Base styles matching FileSystemItem
7
+ "group flex w-full text-left gap-2 items-start p-2 rounded-md cursor-pointer transition-colors text-paragraph-sm",
8
+ // Hover state - matching FileSystemItem
9
+ "hover:bg-accent-hover",
10
+ // Focus state - matching FileSystemItem
11
+ "focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-border-dark",
12
+ // Selected state - matching FileSystemItem
13
+ isSelected && "bg-accent", className), onClick: () => window.open(source.url, "_blank"), ...props, children: [_jsx("div", { className: "shrink-0 flex items-center h-5", children: _jsx("div", { className: "relative rounded-[3px] size-4 overflow-hidden bg-muted", children: source.favicon ? (_jsx("img", { alt: source.sourceName, className: "size-full object-cover", src: source.favicon })) : (_jsx("div", { className: "size-full bg-muted" })) }) }), _jsxs("div", { className: "flex flex-1 flex-col gap-1 min-w-0", children: [_jsxs("div", { className: "text-paragraph-sm text-foreground", children: [_jsx("span", { className: "font-medium", children: source.sourceName }), _jsxs("span", { className: "text-muted-foreground", children: [" \u00B7 ", source.title] })] }), _jsx("p", { className: "text-paragraph-sm text-muted-foreground line-clamp-3", children: source.snippet })] })] }));
6
14
  });
7
15
  SourceListItem.displayName = "SourceListItem";
@@ -1,7 +1,24 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Circle, CircleCheck } from "lucide-react";
2
3
  import * as React from "react";
3
4
  import { cn } from "../lib/utils.js";
4
5
  export const TodoListItem = React.forwardRef(({ todo, className, ...props }, ref) => {
5
- return (_jsx("div", { ref: ref, className: cn("flex items-center gap-3 px-3 py-2 rounded-lg", className), ...props, children: _jsx("span", { className: cn("flex-1 text-[var(--font-size)] font-[var(--font-family)]", todo.status === "completed" && "line-through opacity-60", todo.status === "in_progress" && "shimmer-animation"), children: todo.text }) }));
6
+ const isCompleted = todo.status === "completed";
7
+ const isSelected = false; // TODO: Add selection support later if needed
8
+ return (
9
+ /* biome-ignore lint/a11y/useSemanticElements: Keeping div for consistency with FileSystemItem pattern */
10
+ _jsxs("div", { ref: ref, className: cn(
11
+ // Base styles matching FileSystemItem
12
+ "group flex items-center gap-2 p-2 rounded-md cursor-pointer transition-colors text-paragraph-sm",
13
+ // Hover state - matching FileSystemItem
14
+ "hover:bg-accent-hover",
15
+ // Focus state - matching FileSystemItem
16
+ "focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-border-dark",
17
+ // Selected state (if needed later)
18
+ isSelected && "bg-accent", className), role: "button", tabIndex: 0, ...props, children: [_jsx("div", { className: "shrink-0 size-4 flex items-center justify-center text-foreground", children: isCompleted ? (_jsx(CircleCheck, { className: "size-4 text-muted-foreground" })) : (_jsx(Circle, { className: "size-4 text-foreground" })) }), _jsx("p", { className: cn("flex-1 text-foreground",
19
+ // Completed state: strikethrough + muted color
20
+ isCompleted && "line-through text-muted-foreground",
21
+ // In-progress state: medium weight for emphasis
22
+ todo.status === "in_progress" && "font-medium"), children: todo.text })] }));
6
23
  });
7
24
  TodoListItem.displayName = "TodoListItem";
@@ -1,7 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import JsonView from "@uiw/react-json-view";
3
- import { ChevronDown, Wrench } from "lucide-react";
3
+ import { CheckSquare, ChevronDown, Cloud, Edit, FileText, Globe, Link, Search, Wrench, } from "lucide-react";
4
4
  import { useState } from "react";
5
+ /**
6
+ * Map of icon names to Lucide components
7
+ */
8
+ const ICON_MAP = {
9
+ Globe: Globe,
10
+ Link: Link,
11
+ Cloud: Cloud,
12
+ CheckSquare: CheckSquare,
13
+ Search: Search,
14
+ FileText: FileText,
15
+ Edit: Edit,
16
+ Wrench: Wrench,
17
+ };
5
18
  /**
6
19
  * Tool call kind icons (using emoji for simplicity)
7
20
  */
@@ -22,7 +35,13 @@ const _kindIcons = {
22
35
  */
23
36
  export function ToolCall({ toolCall }) {
24
37
  const [isExpanded, setIsExpanded] = useState(false);
25
- return (_jsxs("div", { className: "flex flex-col my-2", children: [_jsx("button", { type: "button", className: "flex items-center gap-2 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: _jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium text-zinc-500", children: [_jsx("div", { className: "text-zinc-500", children: _jsx(Wrench, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-zinc-500", children: toolCall.title }), _jsx(ChevronDown, { className: `h-3 w-3 text-zinc-400 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })] }) }), isExpanded && (_jsxs("div", { className: "mt-2 text-sm border border-zinc-200 rounded-lg bg-zinc-50 overflow-hidden w-full", children: [toolCall.locations && toolCall.locations.length > 0 && (_jsxs("div", { className: "p-3 border-b border-zinc-200", children: [_jsx("div", { className: "text-[10px] font-bold text-zinc-400 uppercase tracking-wider mb-1.5 font-sans", children: "Files" }), _jsx("ul", { className: "space-y-1", children: toolCall.locations.map((loc) => (_jsxs("li", { className: "font-mono text-[11px] text-zinc-700 bg-zinc-200/50 px-1.5 py-0.5 rounded w-fit", children: [loc.path, loc.line !== null &&
38
+ // Determine which icon to show
39
+ const IconComponent = toolCall.icon && ICON_MAP[toolCall.icon]
40
+ ? ICON_MAP[toolCall.icon]
41
+ : Wrench;
42
+ // Determine display name
43
+ const displayName = toolCall.prettyName || toolCall.title;
44
+ return (_jsxs("div", { className: "flex flex-col my-4", children: [_jsx("button", { type: "button", className: "flex items-center gap-2 cursor-pointer bg-transparent border-none p-0 text-left group w-fit", onClick: () => setIsExpanded(!isExpanded), "aria-expanded": isExpanded, children: _jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-medium text-zinc-500", children: [_jsx("div", { className: "text-zinc-500", children: _jsx(IconComponent, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-zinc-500", children: displayName }), _jsx(ChevronDown, { className: `h-3 w-3 text-zinc-400 transition-transform duration-200 ${isExpanded ? "rotate-180" : ""}` })] }) }), isExpanded && (_jsxs("div", { className: "mt-2 text-sm border border-zinc-200 rounded-lg bg-zinc-50 overflow-hidden w-full", children: [toolCall.locations && toolCall.locations.length > 0 && (_jsxs("div", { className: "p-3 border-b border-zinc-200", children: [_jsx("div", { className: "text-[10px] font-bold text-zinc-400 uppercase tracking-wider mb-1.5 font-sans", children: "Files" }), _jsx("ul", { className: "space-y-1", children: toolCall.locations.map((loc) => (_jsxs("li", { className: "font-mono text-[11px] text-zinc-700 bg-zinc-200/50 px-1.5 py-0.5 rounded w-fit", children: [loc.path, loc.line !== null &&
26
45
  loc.line !== undefined &&
27
46
  `:${loc.line}`] }, `${loc.path}:${loc.line ?? ""}`))) })] })), toolCall.rawInput && Object.keys(toolCall.rawInput).length > 0 && (_jsxs("div", { className: "p-3 border-b border-zinc-200", children: [_jsx("div", { className: "text-[10px] font-bold text-zinc-400 uppercase tracking-wider mb-1.5 font-sans", children: "Input" }), _jsx("div", { className: "text-[11px] font-mono text-zinc-700", children: _jsx(JsonView, { value: toolCall.rawInput, collapsed: false, displayDataTypes: false, displayObjectSize: false, enableClipboard: true, style: {
28
47
  fontSize: "11px",
@@ -0,0 +1,7 @@
1
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
2
+ import * as React from "react";
3
+ declare const TooltipProvider: React.FC<TooltipPrimitive.TooltipProviderProps>;
4
+ declare const Tooltip: React.FC<TooltipPrimitive.TooltipProps>;
5
+ declare const TooltipTrigger: React.ForwardRefExoticComponent<TooltipPrimitive.TooltipTriggerProps & React.RefAttributes<HTMLButtonElement>>;
6
+ declare const TooltipContent: React.ForwardRefExoticComponent<Omit<TooltipPrimitive.TooltipContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
7
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as TooltipPrimitive from "@radix-ui/react-tooltip";
3
+ import * as React from "react";
4
+ import { cn } from "../lib/utils.js";
5
+ const TooltipProvider = TooltipPrimitive.Provider;
6
+ const Tooltip = TooltipPrimitive.Root;
7
+ const TooltipTrigger = TooltipPrimitive.Trigger;
8
+ const TooltipContent = React.forwardRef(({ className, sideOffset = 4, ...props }, ref) => (_jsx(TooltipPrimitive.Content, { ref: ref, sideOffset: sideOffset, className: cn("z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2", className), ...props })));
9
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
10
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };