@townco/ui 0.1.68 → 0.1.69

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 (56) hide show
  1. package/dist/core/hooks/use-chat-messages.d.ts +6 -1
  2. package/dist/core/hooks/use-chat-session.d.ts +1 -1
  3. package/dist/core/hooks/use-tool-calls.d.ts +6 -1
  4. package/dist/core/schemas/chat.d.ts +10 -0
  5. package/dist/core/schemas/tool-call.d.ts +5 -0
  6. package/dist/core/schemas/tool-call.js +8 -0
  7. package/dist/core/utils/tool-call-state.d.ts +30 -0
  8. package/dist/core/utils/tool-call-state.js +73 -0
  9. package/dist/core/utils/tool-summary.d.ts +13 -0
  10. package/dist/core/utils/tool-summary.js +172 -0
  11. package/dist/core/utils/tool-verbiage.d.ts +28 -0
  12. package/dist/core/utils/tool-verbiage.js +185 -0
  13. package/dist/gui/components/AppSidebar.d.ts +22 -0
  14. package/dist/gui/components/AppSidebar.js +22 -0
  15. package/dist/gui/components/Button.d.ts +1 -1
  16. package/dist/gui/components/ChatLayout.d.ts +5 -0
  17. package/dist/gui/components/ChatLayout.js +239 -132
  18. package/dist/gui/components/ChatView.js +42 -118
  19. package/dist/gui/components/MessageContent.js +151 -39
  20. package/dist/gui/components/SessionHistory.d.ts +10 -0
  21. package/dist/gui/components/SessionHistory.js +101 -0
  22. package/dist/gui/components/SessionHistoryItem.d.ts +11 -0
  23. package/dist/gui/components/SessionHistoryItem.js +24 -0
  24. package/dist/gui/components/Sheet.d.ts +25 -0
  25. package/dist/gui/components/Sheet.js +36 -0
  26. package/dist/gui/components/Sidebar.d.ts +65 -0
  27. package/dist/gui/components/Sidebar.js +231 -0
  28. package/dist/gui/components/SidebarToggle.d.ts +3 -0
  29. package/dist/gui/components/SidebarToggle.js +9 -0
  30. package/dist/gui/components/ToolCallList.js +3 -3
  31. package/dist/gui/components/ToolOperation.d.ts +11 -0
  32. package/dist/gui/components/ToolOperation.js +289 -0
  33. package/dist/gui/components/WorkProgress.d.ts +20 -0
  34. package/dist/gui/components/WorkProgress.js +79 -0
  35. package/dist/gui/components/index.d.ts +8 -1
  36. package/dist/gui/components/index.js +9 -1
  37. package/dist/gui/hooks/index.d.ts +1 -0
  38. package/dist/gui/hooks/index.js +1 -0
  39. package/dist/gui/hooks/use-mobile.d.ts +1 -0
  40. package/dist/gui/hooks/use-mobile.js +15 -0
  41. package/dist/gui/index.d.ts +1 -0
  42. package/dist/gui/index.js +2 -0
  43. package/dist/gui/lib/motion.d.ts +55 -0
  44. package/dist/gui/lib/motion.js +217 -0
  45. package/dist/sdk/schemas/session.d.ts +11 -6
  46. package/dist/sdk/transports/types.d.ts +5 -0
  47. package/package.json +8 -7
  48. package/src/styles/global.css +128 -1
  49. package/dist/gui/components/InvokingGroup.d.ts +0 -9
  50. package/dist/gui/components/InvokingGroup.js +0 -16
  51. package/dist/gui/components/SubagentStream.d.ts +0 -23
  52. package/dist/gui/components/SubagentStream.js +0 -98
  53. package/dist/gui/components/ToolCall.d.ts +0 -8
  54. package/dist/gui/components/ToolCall.js +0 -234
  55. package/dist/gui/components/ToolCallGroup.d.ts +0 -8
  56. package/dist/gui/components/ToolCallGroup.js +0 -29
@@ -0,0 +1,289 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import JsonView from "@uiw/react-json-view";
3
+ import { AnimatePresence, motion, useReducedMotion } from "framer-motion";
4
+ import { AlertCircle, BrainCircuit, CheckSquare, ChevronDown, ChevronRight, CircleDot, Cloud, Edit, FileText, Globe, Image, Link, ListVideo, Search, Wrench, } from "lucide-react";
5
+ import React, { useEffect, useState } from "react";
6
+ import { areAllToolCallsCompleted, getGroupDisplayState, getToolCallDisplayState, getToolCallStateVerbiage, hasAnyToolCallFailed, isPreliminaryToolCall, } from "../../core/utils/tool-call-state.js";
7
+ import { generateSmartSummary } from "../../core/utils/tool-summary.js";
8
+ import { expandCollapseVariants, fadeInVariants, getDuration, getTransition, motionDuration, motionEasing, rotateVariants, shimmerTransition, standardTransition, } from "../lib/motion.js";
9
+ import { cn } from "../lib/utils.js";
10
+ import * as ChatLayout from "./ChatLayout.js";
11
+ import { useTheme } from "./ThemeProvider.js";
12
+ /**
13
+ * Map of icon names to Lucide components
14
+ */
15
+ const ICON_MAP = {
16
+ Globe: Globe,
17
+ Image: Image,
18
+ Link: Link,
19
+ Cloud: Cloud,
20
+ CheckSquare: CheckSquare,
21
+ Search: Search,
22
+ FileText: FileText,
23
+ Edit: Edit,
24
+ Wrench: Wrench,
25
+ BrainCircuit: BrainCircuit,
26
+ CircleDot: CircleDot,
27
+ };
28
+ /**
29
+ * ToolOperation component - unified display for tool calls
30
+ * Handles both individual and grouped tool calls with smooth transitions
31
+ */
32
+ export function ToolOperation({ toolCalls, isGrouped = false, autoMinimize = true, }) {
33
+ const [isExpanded, setIsExpanded] = useState(false);
34
+ const [isMinimized, setIsMinimized] = useState(false);
35
+ const [userInteracted, setUserInteracted] = useState(false);
36
+ const { resolvedTheme } = useTheme();
37
+ const shouldReduceMotion = useReducedMotion();
38
+ // For single tool call, extract it
39
+ const singleToolCall = toolCalls.length === 1 ? toolCalls[0] : null;
40
+ const isTodoWrite = singleToolCall?.title === "todo_write";
41
+ // Safely access ChatLayout context
42
+ const layoutContext = React.useContext(ChatLayout.Context);
43
+ // Determine display state
44
+ const displayState = isGrouped
45
+ ? getGroupDisplayState(toolCalls)
46
+ : singleToolCall
47
+ ? getToolCallDisplayState(singleToolCall)
48
+ : "executing";
49
+ const isCompleted = displayState === "completed";
50
+ const isFailed = displayState === "failed";
51
+ const isSelecting = displayState === "selecting";
52
+ const isActive = displayState === "executing";
53
+ // Auto-minimize when completed (only if user hasn't manually interacted)
54
+ useEffect(() => {
55
+ if (autoMinimize && isCompleted && !isMinimized && !userInteracted) {
56
+ // Small delay to show the completed state briefly
57
+ const timer = setTimeout(() => {
58
+ setIsMinimized(true);
59
+ setIsExpanded(false);
60
+ }, 500);
61
+ return () => clearTimeout(timer);
62
+ }
63
+ return undefined;
64
+ }, [autoMinimize, isCompleted, isMinimized, userInteracted]);
65
+ // Click handler for header
66
+ const handleHeaderClick = React.useCallback(() => {
67
+ if (isTodoWrite && layoutContext && singleToolCall) {
68
+ // Toggle sidepanel for TodoWrite
69
+ if (layoutContext.panelSize !== "hidden" &&
70
+ layoutContext.activeTab === "todo") {
71
+ layoutContext.setPanelSize("hidden");
72
+ }
73
+ else {
74
+ layoutContext.setPanelSize("small");
75
+ layoutContext.setActiveTab("todo");
76
+ }
77
+ }
78
+ else {
79
+ // Normal expand/collapse
80
+ setUserInteracted(true); // Mark as user-interacted to prevent auto-minimize
81
+ setIsExpanded(!isExpanded);
82
+ if (isMinimized) {
83
+ setIsMinimized(false);
84
+ }
85
+ }
86
+ }, [isTodoWrite, layoutContext, isExpanded, isMinimized, singleToolCall]);
87
+ // Get icon for display
88
+ const getIcon = () => {
89
+ if (isGrouped) {
90
+ return ListVideo;
91
+ }
92
+ if (singleToolCall?.icon && ICON_MAP[singleToolCall.icon]) {
93
+ return ICON_MAP[singleToolCall.icon];
94
+ }
95
+ return CircleDot;
96
+ };
97
+ const IconComponent = getIcon();
98
+ // Get verbiage/summary
99
+ const getDisplayText = () => {
100
+ if (isGrouped) {
101
+ const tense = isCompleted ? "past" : "active";
102
+ return generateSmartSummary(toolCalls, tense);
103
+ }
104
+ if (singleToolCall) {
105
+ return getToolCallStateVerbiage(singleToolCall);
106
+ }
107
+ return "Tool operation";
108
+ };
109
+ const displayText = getDisplayText();
110
+ // For preliminary/selecting states, show simple non-expandable text
111
+ if (isSelecting && !isGrouped) {
112
+ return (_jsx(motion.div, { className: "flex flex-col my-4 rounded-md px-1 -mx-1 w-fit", animate: {
113
+ backgroundPosition: ["200% 0", "-200% 0"],
114
+ }, transition: {
115
+ ...shimmerTransition,
116
+ duration: getDuration(shouldReduceMotion ?? false, 1.5),
117
+ }, style: {
118
+ backgroundImage: "linear-gradient(90deg, transparent 5%, rgba(255, 255, 255, 0.75) 25%, transparent 35%)",
119
+ backgroundSize: "200% 100%",
120
+ }, children: _jsx("span", { className: "text-paragraph-sm text-text-secondary/50", children: displayText }) }));
121
+ }
122
+ // If it's a grouped preliminary (selecting) state
123
+ if (isSelecting && isGrouped) {
124
+ return (_jsxs(motion.div, { className: "flex flex-col my-4 rounded-md px-1 -mx-1 w-fit", animate: {
125
+ backgroundPosition: ["200% 0", "-200% 0"],
126
+ }, transition: {
127
+ ...shimmerTransition,
128
+ duration: getDuration(shouldReduceMotion ?? false, 1.5),
129
+ }, style: {
130
+ backgroundImage: "linear-gradient(90deg, transparent 5%, rgba(255, 255, 255, 0.75) 25%, transparent 35%)",
131
+ backgroundSize: "200% 100%",
132
+ }, children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "text-text-secondary/70", children: _jsx(ListVideo, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-paragraph-sm text-text-secondary/70", children: "Selecting tools" }), _jsx("span", { className: "text-[10px] bg-muted px-1.5 py-0.5 rounded text-text-secondary/70", children: toolCalls.length })] }), _jsx("span", { className: "text-paragraph-sm text-text-secondary/70 pl-4.5", children: displayText })] }));
133
+ }
134
+ // Full display (for single tool call or expanded group, includes minimized state)
135
+ return (_jsxs(motion.div, { className: "flex flex-col my-4", initial: {
136
+ filter: "blur(12px)",
137
+ opacity: 0,
138
+ y: 12,
139
+ }, animate: {
140
+ filter: "blur(0px)",
141
+ opacity: 1,
142
+ y: 0,
143
+ }, exit: {
144
+ filter: "blur(12px)",
145
+ opacity: 0,
146
+ y: -12,
147
+ }, transition: getTransition(shouldReduceMotion ?? false, {
148
+ duration: 0.5,
149
+ ease: motionEasing.smooth,
150
+ }), children: [_jsxs(motion.button, { type: "button", className: "flex flex-col items-start gap-0.5 cursor-pointer bg-transparent border-none p-0 text-left group w-fit rounded-md px-1 -mx-1", onClick: handleHeaderClick, "aria-expanded": isTodoWrite ? undefined : isExpanded, animate: isActive || isSelecting
151
+ ? {
152
+ backgroundPosition: ["200% 0", "-200% 0"],
153
+ }
154
+ : {}, transition: isActive || isSelecting
155
+ ? {
156
+ ...shimmerTransition,
157
+ duration: getDuration(shouldReduceMotion ?? false, 1.5),
158
+ }
159
+ : {}, style: isActive || isSelecting
160
+ ? {
161
+ backgroundImage: "linear-gradient(90deg, transparent 5%, rgba(255, 255, 255, 0.75) 25%, transparent 35%)",
162
+ backgroundSize: "200% 100%",
163
+ }
164
+ : {}, children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: _jsx(IconComponent, { className: "h-3 w-3" }) }), _jsxs("span", { className: "text-paragraph-sm text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: [isGrouped && _jsx("span", { className: "mr-1", children: "Parallel operation" }), !isGrouped && displayText] }), isGrouped && (_jsx("span", { className: "text-[10px] bg-muted px-1.5 py-0.5 rounded text-text-secondary/70", children: toolCalls.length })), isFailed && _jsx(AlertCircle, { className: "h-3 w-3 text-destructive" }), isTodoWrite ? (_jsx(ChevronRight, { className: "h-3 w-3 text-text-secondary/70 group-hover:text-text-secondary transition-colors" })) : (_jsx(motion.div, { animate: {
165
+ rotate: isExpanded ? 180 : 0,
166
+ }, transition: getTransition(shouldReduceMotion ?? false, {
167
+ duration: motionDuration.normal,
168
+ ease: motionEasing.smooth,
169
+ }), className: "h-3 w-3 text-text-secondary/70 group-hover:text-text-secondary transition-colors", children: _jsx(ChevronDown, { className: "h-3 w-3" }) }))] }), !isGrouped && singleToolCall?.subline && (_jsx("span", { className: "text-paragraph-sm text-text-secondary/70 pl-4.5", children: singleToolCall.subline })), isGrouped && !isExpanded && (_jsx("span", { className: "text-paragraph-sm text-text-secondary/70 pl-4.5", children: displayText }))] }), _jsx(AnimatePresence, { initial: false, children: !isTodoWrite && isExpanded && (_jsx(motion.div, { initial: "collapsed", animate: "expanded", exit: "collapsed", variants: expandCollapseVariants, transition: getTransition(shouldReduceMotion ?? false, {
170
+ duration: motionDuration.normal,
171
+ ease: motionEasing.smooth,
172
+ }), className: "mt-1", children: isGrouped ? (
173
+ // Render individual tool calls in group
174
+ _jsx("div", { className: "flex flex-col gap-4 mt-2", children: toolCalls.map((toolCall) => (_jsx("div", { className: "flex items-start gap-1.5", children: _jsx("div", { className: "flex-1 ml-5", children: _jsx(ToolOperationDetails, { toolCall: toolCall }) }) }, toolCall.id))) })) : (
175
+ // Render single tool call details
176
+ singleToolCall && (_jsx(ToolOperationDetails, { toolCall: singleToolCall }))) }, "expanded-content")) })] }));
177
+ }
178
+ /**
179
+ * Component to display detailed tool call information
180
+ */
181
+ function ToolOperationDetails({ toolCall }) {
182
+ const { resolvedTheme } = useTheme();
183
+ // Don't show details for preliminary tool calls
184
+ if (isPreliminaryToolCall(toolCall)) {
185
+ return (_jsx("div", { className: "text-paragraph-sm text-text-secondary/70 ml-2", children: getToolCallStateVerbiage(toolCall) }));
186
+ }
187
+ // JSON View style based on theme
188
+ const jsonStyle = {
189
+ fontSize: "11px",
190
+ backgroundColor: "transparent",
191
+ fontFamily: "inherit",
192
+ "--w-rjv-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
193
+ "--w-rjv-key-string": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
194
+ "--w-rjv-background-color": "transparent",
195
+ "--w-rjv-line-color": resolvedTheme === "dark" ? "#27272a" : "#e4e4e7",
196
+ "--w-rjv-arrow-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
197
+ "--w-rjv-edit-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
198
+ "--w-rjv-info-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
199
+ "--w-rjv-update-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
200
+ "--w-rjv-copied-color": resolvedTheme === "dark" ? "#fafafa" : "#09090b",
201
+ "--w-rjv-copied-success-color": resolvedTheme === "dark" ? "#22c55e" : "#16a34a",
202
+ "--w-rjv-curlybraces-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
203
+ "--w-rjv-colon-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
204
+ "--w-rjv-brackets-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
205
+ "--w-rjv-quotes-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
206
+ "--w-rjv-quotes-string-color": resolvedTheme === "dark" ? "#a1a1aa" : "#71717a",
207
+ "--w-rjv-type-string-color": resolvedTheme === "dark" ? "#22c55e" : "#16a34a",
208
+ "--w-rjv-type-int-color": resolvedTheme === "dark" ? "#f59e0b" : "#d97706",
209
+ "--w-rjv-type-float-color": resolvedTheme === "dark" ? "#f59e0b" : "#d97706",
210
+ "--w-rjv-type-bigint-color": resolvedTheme === "dark" ? "#f59e0b" : "#d97706",
211
+ "--w-rjv-type-boolean-color": resolvedTheme === "dark" ? "#3b82f6" : "#2563eb",
212
+ "--w-rjv-type-date-color": resolvedTheme === "dark" ? "#ec4899" : "#db2777",
213
+ "--w-rjv-type-url-color": resolvedTheme === "dark" ? "#3b82f6" : "#2563eb",
214
+ "--w-rjv-type-null-color": resolvedTheme === "dark" ? "#ef4444" : "#dc2626",
215
+ "--w-rjv-type-nan-color": resolvedTheme === "dark" ? "#ef4444" : "#dc2626",
216
+ "--w-rjv-type-undefined-color": resolvedTheme === "dark" ? "#ef4444" : "#dc2626",
217
+ };
218
+ return (_jsxs("div", { className: "text-sm border border-border rounded-lg bg-card overflow-hidden w-full", children: [toolCall.locations && toolCall.locations.length > 0 && (_jsxs("div", { className: "p-3 border-b border-border", children: [_jsx("div", { className: "text-[10px] font-bold text-text-secondary 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-foreground bg-muted px-1.5 py-0.5 rounded w-fit", children: [loc.path, loc.line !== null && loc.line !== undefined && `:${loc.line}`] }, `${loc.path}:${loc.line ?? ""}`))) })] })), toolCall.rawInput && Object.keys(toolCall.rawInput).length > 0 && (_jsxs("div", { className: "p-3 border-b border-border", children: [_jsx("div", { className: "text-[10px] font-bold text-text-secondary uppercase tracking-wider mb-1.5 font-sans", children: "Input" }), _jsx("div", { className: "text-[11px] font-mono text-foreground", children: _jsx(JsonView, { value: toolCall.rawInput, collapsed: false, displayDataTypes: false, displayObjectSize: false, enableClipboard: true, style: jsonStyle }) })] })), ((toolCall.content && toolCall.content.length > 0) ||
219
+ toolCall.error) && (_jsxs("div", { className: "p-3 border-b border-border last:border-0", children: [_jsx("div", { className: "text-[10px] font-bold text-text-secondary uppercase tracking-wider mb-1.5 font-sans", children: "Output" }), _jsxs("div", { className: "space-y-2 text-[11px] text-foreground", children: [toolCall.content?.map((block, idx) => {
220
+ // Generate a stable key based on content
221
+ const getBlockKey = () => {
222
+ if (block.type === "diff" && "path" in block) {
223
+ return `diff-${block.path}-${idx}`;
224
+ }
225
+ if (block.type === "terminal" && "terminalId" in block) {
226
+ return `terminal-${block.terminalId}`;
227
+ }
228
+ if (block.type === "text" && "text" in block) {
229
+ return `text-${block.text.substring(0, 20)}-${idx}`;
230
+ }
231
+ if (block.type === "content" && "content" in block) {
232
+ const innerContent = block.content;
233
+ return `content-${innerContent.text?.substring(0, 20)}-${idx}`;
234
+ }
235
+ return `block-${idx}`;
236
+ };
237
+ // Helper to render text content
238
+ const renderTextContent = (text, key) => {
239
+ try {
240
+ const parsed = JSON.parse(text);
241
+ if (typeof parsed === "object" && parsed !== null) {
242
+ return (_jsx("div", { className: "text-[11px]", children: _jsx(JsonView, { value: parsed, collapsed: false, displayDataTypes: false, displayObjectSize: false, enableClipboard: true, style: jsonStyle }) }, key));
243
+ }
244
+ }
245
+ catch {
246
+ // Not valid JSON
247
+ }
248
+ return (_jsx("pre", { className: "whitespace-pre-wrap font-mono text-[11px] text-foreground overflow-x-auto", children: text }, key));
249
+ };
250
+ // Handle nested content blocks
251
+ if (block.type === "content" && "content" in block) {
252
+ const innerContent = block.content;
253
+ if (innerContent.type === "text" && innerContent.text) {
254
+ return renderTextContent(innerContent.text, getBlockKey());
255
+ }
256
+ }
257
+ // Handle direct text blocks
258
+ if (block.type === "text" && "text" in block) {
259
+ return renderTextContent(block.text, getBlockKey());
260
+ }
261
+ // Handle image blocks
262
+ if (block.type === "image") {
263
+ const alt = block.alt || "Generated image";
264
+ let imageSrc;
265
+ if ("data" in block) {
266
+ const mimeType = block.mimeType || "image/png";
267
+ imageSrc = `data:${mimeType};base64,${block.data}`;
268
+ }
269
+ else if ("url" in block) {
270
+ imageSrc = block.url;
271
+ }
272
+ else {
273
+ return null;
274
+ }
275
+ return (_jsx("div", { className: "my-2", children: _jsx("img", { src: imageSrc, alt: alt, className: "max-w-full h-auto rounded-md border border-border" }) }, getBlockKey()));
276
+ }
277
+ // Handle diff blocks
278
+ if (block.type === "diff" &&
279
+ "path" in block &&
280
+ "oldText" in block &&
281
+ "newText" in block) {
282
+ return (_jsxs("div", { className: "border border-border rounded bg-card", children: [_jsxs("div", { className: "bg-muted px-2 py-1 text-[10px] font-mono text-text-secondary border-b border-border", children: [block.path, "line" in block &&
283
+ block.line !== null &&
284
+ block.line !== undefined &&
285
+ `:${block.line}`] }), _jsxs("div", { className: "p-2 font-mono text-[11px]", children: [_jsxs("div", { className: "text-red-500 dark:text-red-400", children: ["- ", block.oldText] }), _jsxs("div", { className: "text-green-500 dark:text-green-400", children: ["+ ", block.newText] })] })] }, getBlockKey()));
286
+ }
287
+ return null;
288
+ }), toolCall.error && (_jsxs("div", { className: "text-destructive font-mono text-[11px] mt-2", children: ["Error: ", toolCall.error] }))] })] })), toolCall._meta?.truncationWarning && (_jsxs("div", { className: "mx-3 mt-3 mb-0 flex items-center gap-2 rounded-md bg-yellow-50 dark:bg-yellow-950/20 px-3 py-2 text-[11px] text-yellow-800 dark:text-yellow-200 border border-yellow-200 dark:border-yellow-900", children: [_jsx("span", { className: "text-yellow-600 dark:text-yellow-500", children: "\u26A0\uFE0F" }), _jsx("span", { children: toolCall._meta.truncationWarning })] })), (toolCall.tokenUsage || toolCall.startedAt) && (_jsxs("div", { className: "p-2 bg-muted/50 border-b border-border last:border-0 flex flex-wrap gap-4 text-[10px] text-text-secondary font-sans", children: [toolCall.tokenUsage && (_jsxs("div", { className: "flex gap-3", children: [toolCall.tokenUsage.inputTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Input:" }), toolCall.tokenUsage.inputTokens.toLocaleString()] })), toolCall.tokenUsage.outputTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Output:" }), toolCall.tokenUsage.outputTokens.toLocaleString()] })), toolCall.tokenUsage.totalTokens !== undefined && (_jsxs("div", { children: [_jsx("span", { className: "uppercase tracking-wide font-semibold mr-1", children: "Total:" }), toolCall.tokenUsage.totalTokens.toLocaleString()] }))] })), toolCall.startedAt && (_jsxs("div", { className: "flex gap-3 ml-auto", children: [_jsxs("span", { children: ["Started: ", new Date(toolCall.startedAt).toLocaleTimeString()] }), toolCall.completedAt && (_jsxs("span", { children: ["Completed:", " ", new Date(toolCall.completedAt).toLocaleTimeString(), " (", Math.round((toolCall.completedAt - toolCall.startedAt) / 1000), "s)"] }))] }))] }))] }));
289
+ }
@@ -0,0 +1,20 @@
1
+ import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
+ export interface WorkProgressProps {
3
+ /** Optional thinking/reasoning content */
4
+ thinking?: string;
5
+ /** Whether thinking is currently streaming */
6
+ isThinkingStreaming?: boolean;
7
+ /** Tool calls to display */
8
+ toolCalls?: ToolCallType[];
9
+ /** Display style for thinking */
10
+ thinkingDisplayStyle?: "collapsible" | "inline";
11
+ /** Whether to auto-collapse thinking when done */
12
+ autoCollapseThinking?: boolean;
13
+ /** Additional CSS class */
14
+ className?: string;
15
+ }
16
+ /**
17
+ * WorkProgress component - coordinates display of thinking and tool execution
18
+ * Provides a unified view of the agent's work from thinking through execution
19
+ */
20
+ export declare function WorkProgress({ thinking, isThinkingStreaming, toolCalls, thinkingDisplayStyle, autoCollapseThinking, className, }: WorkProgressProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,79 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { getGroupDisplayState, isPreliminaryToolCall, } from "../../core/utils/tool-call-state.js";
4
+ import { cn } from "../lib/utils.js";
5
+ import { Reasoning } from "./Reasoning.js";
6
+ import { ToolOperation } from "./ToolOperation.js";
7
+ /**
8
+ * WorkProgress component - coordinates display of thinking and tool execution
9
+ * Provides a unified view of the agent's work from thinking through execution
10
+ */
11
+ export function WorkProgress({ thinking, isThinkingStreaming = false, toolCalls = [], thinkingDisplayStyle = "collapsible", autoCollapseThinking = true, className, }) {
12
+ // Group tool calls by batchId and type
13
+ const groupedToolCalls = React.useMemo(() => {
14
+ if (toolCalls.length === 0)
15
+ return [];
16
+ const result = [];
17
+ const batchMap = new Map();
18
+ let selectingGroup = [];
19
+ const flushSelectingGroup = () => {
20
+ if (selectingGroup.length > 0) {
21
+ if (selectingGroup.length > 1) {
22
+ result.push({ type: "selecting", toolCalls: selectingGroup });
23
+ }
24
+ else {
25
+ result.push({ type: "single", toolCall: selectingGroup[0] });
26
+ }
27
+ selectingGroup = [];
28
+ }
29
+ };
30
+ for (const toolCall of toolCalls) {
31
+ // Handle batch groups (parallel operations)
32
+ if (toolCall.batchId) {
33
+ flushSelectingGroup();
34
+ const existing = batchMap.get(toolCall.batchId);
35
+ if (existing) {
36
+ existing.push(toolCall);
37
+ }
38
+ else {
39
+ const group = [toolCall];
40
+ batchMap.set(toolCall.batchId, group);
41
+ result.push({
42
+ type: "batch",
43
+ batchId: toolCall.batchId,
44
+ toolCalls: group,
45
+ });
46
+ }
47
+ }
48
+ // Handle consecutive preliminary (selecting) tool calls
49
+ else if (isPreliminaryToolCall(toolCall)) {
50
+ selectingGroup.push(toolCall);
51
+ }
52
+ // Regular tool call
53
+ else {
54
+ flushSelectingGroup();
55
+ result.push({ type: "single", toolCall });
56
+ }
57
+ }
58
+ // Flush any remaining selecting group
59
+ flushSelectingGroup();
60
+ return result;
61
+ }, [toolCalls]);
62
+ const hasThinking = !!thinking;
63
+ const hasToolCalls = toolCalls.length > 0;
64
+ if (!hasThinking && !hasToolCalls) {
65
+ return null;
66
+ }
67
+ return (_jsxs("div", { className: cn("work-progress", className), children: [hasThinking && (_jsx(Reasoning, { content: thinking, isStreaming: isThinkingStreaming, mode: thinkingDisplayStyle, autoCollapse: autoCollapseThinking })), groupedToolCalls.map((group, index) => {
68
+ if (group.type === "batch") {
69
+ // Parallel operations group
70
+ return (_jsx(ToolOperation, { toolCalls: group.toolCalls, isGrouped: true, autoMinimize: true }, `batch-${group.batchId}`));
71
+ }
72
+ if (group.type === "selecting") {
73
+ // Multiple selecting operations
74
+ return (_jsx(ToolOperation, { toolCalls: group.toolCalls, isGrouped: true, autoMinimize: false }, `selecting-${index}`));
75
+ }
76
+ // Single tool call
77
+ return (_jsx(ToolOperation, { toolCalls: [group.toolCall], isGrouped: false, autoMinimize: true }, group.toolCall.id));
78
+ })] }));
79
+ }
@@ -2,6 +2,7 @@ export { toast } from "sonner";
2
2
  export { MockFileSystemProvider, mockFileSystemData, } from "../data/mockFileSystemData.js";
3
3
  export { mockSourceData } from "../data/mockSourceData.js";
4
4
  export type { FileSystemData, FileSystemItem as FileSystemItemData, FileSystemItemType, FileSystemProvider, } from "../types/filesystem.js";
5
+ export { AppSidebar, type AppSidebarProps, } from "./AppSidebar.js";
5
6
  export { Button, type ButtonProps, buttonVariants } from "./Button.js";
6
7
  export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "./Card.js";
7
8
  export { ChatEmptyState, type ChatEmptyStateProps } from "./ChatEmptyState.js";
@@ -32,6 +33,11 @@ export { PanelTabsHeader, type PanelTabsHeaderProps, } from "./PanelTabsHeader.j
32
33
  export { Reasoning, type ReasoningProps } from "./Reasoning.js";
33
34
  export { Response, type ResponseProps } from "./Response.js";
34
35
  export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./Select.js";
36
+ export { SessionHistory, type SessionHistoryProps, } from "./SessionHistory.js";
37
+ export { SessionHistoryItem, type SessionHistoryItemProps, } from "./SessionHistoryItem.js";
38
+ export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, } from "./Sheet.js";
39
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, } from "./Sidebar.js";
40
+ export { SidebarToggle } from "./SidebarToggle.js";
35
41
  export { Toaster } from "./Sonner.js";
36
42
  export { type SourceItem, SourceListItem, type SourceListItemProps, } from "./SourceListItem.js";
37
43
  export { SubAgentDetails, type SubAgentDetailsProps, } from "./SubAgentDetails.js";
@@ -43,6 +49,7 @@ export { ThemeToggle } from "./ThemeToggle.js";
43
49
  export { ThinkingBlock, type ThinkingBlockProps, } from "./ThinkingBlock.js";
44
50
  export { TodoList, type TodoListProps } from "./TodoList.js";
45
51
  export { type TodoItem, TodoListItem, type TodoListItemProps, } from "./TodoListItem.js";
46
- export { ToolCall, type ToolCallProps } from "./ToolCall.js";
47
52
  export { ToolCallList, type ToolCallListProps } from "./ToolCallList.js";
53
+ export { ToolOperation, type ToolOperationProps } from "./ToolOperation.js";
48
54
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./Tooltip.js";
55
+ export { WorkProgress, type WorkProgressProps } from "./WorkProgress.js";
@@ -2,6 +2,8 @@
2
2
  export { toast } from "sonner";
3
3
  export { MockFileSystemProvider, mockFileSystemData, } from "../data/mockFileSystemData.js";
4
4
  export { mockSourceData } from "../data/mockSourceData.js";
5
+ // Sidebar components
6
+ export { AppSidebar, } from "./AppSidebar.js";
5
7
  export { Button, buttonVariants } from "./Button.js";
6
8
  export { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "./Card.js";
7
9
  export { ChatEmptyState } from "./ChatEmptyState.js";
@@ -37,6 +39,11 @@ export { PanelTabsHeader, } from "./PanelTabsHeader.js";
37
39
  export { Reasoning } from "./Reasoning.js";
38
40
  export { Response } from "./Response.js";
39
41
  export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, } from "./Select.js";
42
+ export { SessionHistory, } from "./SessionHistory.js";
43
+ export { SessionHistoryItem, } from "./SessionHistoryItem.js";
44
+ export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetOverlay, SheetPortal, SheetTitle, SheetTrigger, } from "./Sheet.js";
45
+ export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, useSidebar, } from "./Sidebar.js";
46
+ export { SidebarToggle } from "./SidebarToggle.js";
40
47
  export { Toaster } from "./Sonner.js";
41
48
  // Toast components
42
49
  export { SourceListItem, } from "./SourceListItem.js";
@@ -50,7 +57,8 @@ export { ThemeToggle } from "./ThemeToggle.js";
50
57
  export { ThinkingBlock, } from "./ThinkingBlock.js";
51
58
  export { TodoList } from "./TodoList.js";
52
59
  export { TodoListItem, } from "./TodoListItem.js";
53
- export { ToolCall } from "./ToolCall.js";
54
60
  export { ToolCallList } from "./ToolCallList.js";
61
+ export { ToolOperation } from "./ToolOperation.js";
55
62
  // Tooltip components
56
63
  export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "./Tooltip.js";
64
+ export { WorkProgress } from "./WorkProgress.js";
@@ -0,0 +1 @@
1
+ export { useIsMobile } from "./use-mobile.js";
@@ -0,0 +1 @@
1
+ export { useIsMobile } from "./use-mobile.js";
@@ -0,0 +1 @@
1
+ export declare function useIsMobile(): boolean;
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ const MOBILE_BREAKPOINT = 768;
3
+ export function useIsMobile() {
4
+ const [isMobile, setIsMobile] = React.useState(undefined);
5
+ React.useEffect(() => {
6
+ const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
7
+ const onChange = () => {
8
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
9
+ };
10
+ mql.addEventListener("change", onChange);
11
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
12
+ return () => mql.removeEventListener("change", onChange);
13
+ }, []);
14
+ return !!isMobile;
15
+ }
@@ -1,2 +1,3 @@
1
1
  export * from "./components/index.js";
2
+ export { useIsMobile } from "./hooks/index.js";
2
3
  export { cn } from "./lib/utils.js";
package/dist/gui/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  // Re-export all components
2
2
  export * from "./components/index.js";
3
+ // Re-export hooks
4
+ export { useIsMobile } from "./hooks/index.js";
3
5
  // Re-export utilities
4
6
  export { cn } from "./lib/utils.js";
@@ -0,0 +1,55 @@
1
+ import type { Transition, Variants } from "framer-motion";
2
+ /**
3
+ * Motion utilities and reusable animation variants for the UI package
4
+ * Provides consistent animation behavior across components
5
+ */
6
+ export declare const motionDuration: {
7
+ readonly fast: 0.15;
8
+ readonly normal: 0.25;
9
+ readonly slow: 0.4;
10
+ };
11
+ export declare const motionEasing: {
12
+ readonly smooth: readonly [0.25, 0.1, 0.25, 1];
13
+ readonly bounce: readonly [0.68, -0.55, 0.265, 1.55];
14
+ readonly sharp: readonly [0.4, 0, 0.2, 1];
15
+ readonly gentle: readonly [0.25, 0.46, 0.45, 0.94];
16
+ };
17
+ export declare const fadeInVariants: Variants;
18
+ export declare const fadeInUpVariants: Variants;
19
+ export declare const expandCollapseVariants: Variants;
20
+ export declare const slideDownVariants: Variants;
21
+ export declare const shimmerTransition: Transition;
22
+ export declare const shimmerVariants: Variants;
23
+ export declare const scaleInVariants: Variants;
24
+ export declare const pulseVariants: Variants;
25
+ export declare const rotateVariants: Variants;
26
+ export declare const standardTransition: Transition;
27
+ export declare const fastTransition: Transition;
28
+ export declare const slowTransition: Transition;
29
+ export declare const springTransition: Transition;
30
+ export declare const gentleSpringTransition: Transition;
31
+ export declare const layoutTransition: Transition;
32
+ export declare const staggerChildren: {
33
+ visible: {
34
+ transition: {
35
+ staggerChildren: number;
36
+ };
37
+ };
38
+ };
39
+ export declare const staggerChildrenFast: {
40
+ visible: {
41
+ transition: {
42
+ staggerChildren: number;
43
+ };
44
+ };
45
+ };
46
+ export declare const slideInFromRightVariants: Variants;
47
+ export declare const slideOutToRightVariants: Variants;
48
+ /**
49
+ * Get transition with reduced motion consideration
50
+ */
51
+ export declare function getTransition(shouldReduceMotion: boolean, transition?: Transition): Transition;
52
+ /**
53
+ * Get duration with reduced motion consideration
54
+ */
55
+ export declare function getDuration(shouldReduceMotion: boolean, duration?: number): number;