@townco/ui 0.1.67 → 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 +5 -0
  2. package/dist/core/hooks/use-tool-calls.d.ts +5 -0
  3. package/dist/core/hooks/use-tool-calls.js +2 -1
  4. package/dist/core/schemas/chat.d.ts +10 -0
  5. package/dist/core/schemas/tool-call.d.ts +96 -0
  6. package/dist/core/schemas/tool-call.js +12 -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/ChatLayout.d.ts +5 -0
  16. package/dist/gui/components/ChatLayout.js +239 -132
  17. package/dist/gui/components/ChatView.js +42 -118
  18. package/dist/gui/components/MessageContent.js +199 -49
  19. package/dist/gui/components/SessionHistory.d.ts +10 -0
  20. package/dist/gui/components/SessionHistory.js +101 -0
  21. package/dist/gui/components/SessionHistoryItem.d.ts +11 -0
  22. package/dist/gui/components/SessionHistoryItem.js +24 -0
  23. package/dist/gui/components/Sheet.d.ts +25 -0
  24. package/dist/gui/components/Sheet.js +36 -0
  25. package/dist/gui/components/Sidebar.d.ts +65 -0
  26. package/dist/gui/components/Sidebar.js +231 -0
  27. package/dist/gui/components/SidebarToggle.d.ts +3 -0
  28. package/dist/gui/components/SidebarToggle.js +9 -0
  29. package/dist/gui/components/SubAgentDetails.d.ts +13 -6
  30. package/dist/gui/components/SubAgentDetails.js +29 -14
  31. package/dist/gui/components/ToolCallList.js +3 -3
  32. package/dist/gui/components/ToolOperation.d.ts +11 -0
  33. package/dist/gui/components/ToolOperation.js +289 -0
  34. package/dist/gui/components/WorkProgress.d.ts +20 -0
  35. package/dist/gui/components/WorkProgress.js +79 -0
  36. package/dist/gui/components/index.d.ts +8 -1
  37. package/dist/gui/components/index.js +9 -1
  38. package/dist/gui/hooks/index.d.ts +1 -0
  39. package/dist/gui/hooks/index.js +1 -0
  40. package/dist/gui/hooks/use-mobile.d.ts +1 -0
  41. package/dist/gui/hooks/use-mobile.js +15 -0
  42. package/dist/gui/index.d.ts +1 -0
  43. package/dist/gui/index.js +2 -0
  44. package/dist/gui/lib/motion.d.ts +55 -0
  45. package/dist/gui/lib/motion.js +217 -0
  46. package/dist/sdk/schemas/session.d.ts +102 -6
  47. package/dist/sdk/transports/http.js +105 -37
  48. package/dist/sdk/transports/types.d.ts +5 -0
  49. package/package.json +8 -7
  50. package/src/styles/global.css +128 -1
  51. package/dist/gui/components/InvokingGroup.d.ts +0 -9
  52. package/dist/gui/components/InvokingGroup.js +0 -16
  53. package/dist/gui/components/ToolCall.d.ts +0 -8
  54. package/dist/gui/components/ToolCall.js +0 -226
  55. package/dist/gui/components/ToolCallGroup.d.ts +0 -8
  56. package/dist/gui/components/ToolCallGroup.js +0 -29
@@ -1,17 +1,20 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ChevronDown, CircleDot, Loader2 } from "lucide-react";
2
+ import { ChevronDown, Loader2 } from "lucide-react";
3
3
  import React, { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { useSubagentStream } from "../../core/hooks/use-subagent-stream.js";
5
+ import { MarkdownRenderer } from "./MarkdownRenderer.js";
5
6
  const SCROLL_THRESHOLD = 50; // px from bottom to consider "at bottom"
6
7
  /**
7
8
  * SubAgentDetails component - displays streaming content from a sub-agent.
8
9
  *
9
10
  * This component:
10
- * - Connects directly to the sub-agent's SSE endpoint
11
+ * - Connects directly to the sub-agent's SSE endpoint (live mode)
12
+ * - Or displays stored messages (replay mode)
11
13
  * - Displays streaming text and tool calls
14
+ * - Renders content as markdown
12
15
  * - Renders in a collapsible section (collapsed by default)
13
16
  */
14
- export function SubAgentDetails({ port, sessionId, host, parentStatus, agentName, query, isExpanded: controlledIsExpanded, onExpandChange, }) {
17
+ export function SubAgentDetails({ port, sessionId, host, parentStatus, agentName, query, isExpanded: controlledIsExpanded, onExpandChange, storedMessages, isReplay = false, }) {
15
18
  const [internalIsExpanded, setInternalIsExpanded] = useState(false);
16
19
  // Use controlled state if provided, otherwise use internal state
17
20
  const isExpanded = controlledIsExpanded ?? internalIsExpanded;
@@ -19,16 +22,28 @@ export function SubAgentDetails({ port, sessionId, host, parentStatus, agentName
19
22
  const [isThinkingExpanded, setIsThinkingExpanded] = useState(false);
20
23
  const [isNearBottom, setIsNearBottom] = useState(true);
21
24
  const thinkingContainerRef = useRef(null);
22
- const { messages, isStreaming: hookIsStreaming, error, } = useSubagentStream({
23
- port,
24
- sessionId,
25
- ...(host !== undefined ? { host } : {}),
26
- });
25
+ // Only use SSE streaming if not in replay mode and port/sessionId provided
26
+ const shouldStream = !isReplay && port !== undefined && sessionId !== undefined;
27
+ const streamOptions = shouldStream
28
+ ? {
29
+ port: port,
30
+ sessionId: sessionId,
31
+ ...(host !== undefined ? { host } : {}),
32
+ }
33
+ : null;
34
+ const { messages: streamedMessages, isStreaming: hookIsStreaming, error, } = useSubagentStream(streamOptions);
35
+ // Use stored messages if available, otherwise use streamed messages
36
+ const messages = storedMessages && storedMessages.length > 0
37
+ ? storedMessages
38
+ : streamedMessages;
27
39
  // Use parent status as primary indicator, fall back to hook's streaming state
28
40
  // Parent is "in_progress" means sub-agent is definitely still running
29
- const isRunning = parentStatus === "in_progress" ||
30
- parentStatus === "pending" ||
31
- hookIsStreaming;
41
+ // In replay mode, we're never running
42
+ const isRunning = isReplay
43
+ ? false
44
+ : parentStatus === "in_progress" ||
45
+ parentStatus === "pending" ||
46
+ hookIsStreaming;
32
47
  // Get the current/latest message
33
48
  const currentMessage = messages[messages.length - 1];
34
49
  const hasContent = currentMessage &&
@@ -97,9 +112,9 @@ export function SubAgentDetails({ port, sessionId, host, parentStatus, agentName
97
112
  return (_jsxs("div", { children: [!isExpanded && (_jsx("div", { className: "w-full max-w-md", children: previewText ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/70 truncate", children: previewText })) : queryFirstLine ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/50 truncate", children: queryFirstLine })) : null })), isExpanded && (_jsxs("div", { className: "space-y-3", children: [(agentName || query) && (_jsxs("div", { children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Input" }), _jsxs("div", { className: "text-[11px] font-mono space-y-1", children: [agentName && (_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "agentName: " }), _jsx("span", { className: "text-foreground", children: agentName })] })), query && (_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "query: " }), _jsx("span", { className: "text-foreground", children: query })] }))] })] })), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setIsThinkingExpanded(!isThinkingExpanded), className: "flex items-center gap-2 cursor-pointer bg-transparent border-none p-0 text-left group", children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider font-sans", children: "Thinking" }), _jsx(ChevronDown, { className: `h-3 w-3 text-muted-foreground/70 transition-transform duration-200 ${isThinkingExpanded ? "rotate-180" : ""}` })] }), isThinkingExpanded && (_jsxs("div", { ref: thinkingContainerRef, className: "mt-2 rounded-md overflow-hidden bg-muted/30 border border-border/50 max-h-[200px] overflow-y-auto", children: [error && (_jsxs("div", { className: "px-2 py-2 text-[11px] text-destructive", children: ["Error: ", error] })), !error && !hasContent && isRunning && (_jsx("div", { className: "px-2 py-2 text-[11px] text-muted-foreground", children: "Waiting for sub-agent response..." })), currentMessage && (_jsxs("div", { className: "px-2 py-2 space-y-2", children: [currentMessage.contentBlocks &&
98
113
  currentMessage.contentBlocks.length > 0
99
114
  ? // Render interleaved content blocks
100
- currentMessage.contentBlocks.map((block, idx) => block.type === "text" ? (_jsx("div", { className: "text-[11px] text-foreground whitespace-pre-wrap font-mono", children: block.text }, `text-${idx}`)) : (_jsx(SubagentToolCallItem, { toolCall: block.toolCall }, block.toolCall.id)))
101
- : // Fallback to legacy content
102
- currentMessage.content && (_jsx("div", { className: "text-[11px] text-foreground whitespace-pre-wrap font-mono", children: currentMessage.content })), currentMessage.isStreaming && (_jsx("span", { className: "inline-block w-1.5 h-3 bg-primary/70 ml-0.5 animate-pulse" }))] }))] }))] }), !isRunning && currentMessage?.content && (_jsxs("div", { children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Output" }), _jsx("div", { className: "text-[11px] text-foreground whitespace-pre-wrap font-mono max-h-[200px] overflow-y-auto rounded-md bg-muted/30 border border-border/50 px-2 py-2", children: currentMessage.content })] }))] }))] }));
115
+ currentMessage.contentBlocks.map((block, idx) => block.type === "text" ? (_jsx("div", { className: "text-[11px] text-foreground prose prose-sm dark:prose-invert max-w-none prose-p:my-1 prose-pre:my-1 prose-code:text-[10px]", children: _jsx(MarkdownRenderer, { content: block.text }) }, `text-${idx}`)) : (_jsx(SubagentToolCallItem, { toolCall: block.toolCall }, block.toolCall.id)))
116
+ : // Fallback to legacy content with markdown
117
+ currentMessage.content && (_jsx("div", { className: "text-[11px] text-foreground prose prose-sm dark:prose-invert max-w-none prose-p:my-1 prose-pre:my-1 prose-code:text-[10px]", children: _jsx(MarkdownRenderer, { content: currentMessage.content }) })), currentMessage.isStreaming && (_jsx("span", { className: "inline-block w-1.5 h-3 bg-primary/70 ml-0.5 animate-pulse" }))] }))] }))] }), !isRunning && currentMessage?.content && (_jsxs("div", { children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Output" }), _jsx("div", { className: "text-[11px] text-foreground max-h-[200px] overflow-y-auto rounded-md bg-muted/30 border border-border/50 px-2 py-2 prose prose-sm dark:prose-invert max-w-none prose-p:my-1 prose-pre:my-1 prose-code:text-[10px]", children: _jsx(MarkdownRenderer, { content: currentMessage.content }) })] }))] }))] }));
103
118
  }
104
119
  /**
105
120
  * Simple tool call display for sub-agent tool calls
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ToolCall } from "./ToolCall.js";
2
+ import { ToolOperation } from "./ToolOperation.js";
3
3
  /**
4
4
  * ToolCallList component - renders a list of tool calls, optionally grouped
5
5
  */
@@ -15,8 +15,8 @@ export function ToolCallList({ toolCalls, groupBy = "chronological", }) {
15
15
  completed: toolCalls.filter((tc) => tc.status === "completed"),
16
16
  failed: toolCalls.filter((tc) => tc.status === "failed"),
17
17
  };
18
- return (_jsxs("div", { className: "space-y-4", children: [grouped.in_progress.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-2 pl-1", children: "In Progress" }), grouped.in_progress.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), grouped.pending.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-2 pl-1", children: "Pending" }), grouped.pending.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), grouped.completed.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-2 pl-1", children: "Completed" }), grouped.completed.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), grouped.failed.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-zinc-400 uppercase tracking-wider mb-2 pl-1", children: "Failed" }), grouped.failed.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] }))] }));
18
+ return (_jsxs("div", { className: "space-y-4", children: [grouped.in_progress.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-2 pl-1", children: "In Progress" }), grouped.in_progress.map((tc) => (_jsx(ToolOperation, { toolCalls: [tc], isGrouped: false }, tc.id)))] })), grouped.pending.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-2 pl-1", children: "Pending" }), grouped.pending.map((tc) => (_jsx(ToolOperation, { toolCalls: [tc], isGrouped: false }, tc.id)))] })), grouped.completed.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-2 pl-1", children: "Completed" }), grouped.completed.map((tc) => (_jsx(ToolOperation, { toolCalls: [tc], isGrouped: false }, tc.id)))] })), grouped.failed.length > 0 && (_jsxs("div", { children: [_jsx("h4", { className: "text-[10px] font-bold text-zinc-400 uppercase tracking-wider mb-2 pl-1", children: "Failed" }), grouped.failed.map((tc) => (_jsx(ToolOperation, { toolCalls: [tc], isGrouped: false }, tc.id)))] }))] }));
19
19
  }
20
20
  // Default: chronological order
21
- return (_jsx("div", { className: "space-y-2", children: toolCalls.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id))) }));
21
+ return (_jsx("div", { className: "space-y-2", children: toolCalls.map((tc) => (_jsx(ToolOperation, { toolCalls: [tc], isGrouped: false }, tc.id))) }));
22
22
  }
@@ -0,0 +1,11 @@
1
+ import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
+ export interface ToolOperationProps {
3
+ toolCalls: ToolCallType[];
4
+ isGrouped?: boolean;
5
+ autoMinimize?: boolean;
6
+ }
7
+ /**
8
+ * ToolOperation component - unified display for tool calls
9
+ * Handles both individual and grouped tool calls with smooth transitions
10
+ */
11
+ export declare function ToolOperation({ toolCalls, isGrouped, autoMinimize, }: ToolOperationProps): import("react/jsx-runtime").JSX.Element;
@@ -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";