@townco/ui 0.1.15 → 0.1.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/hooks/index.d.ts +1 -0
- package/dist/core/hooks/index.js +1 -0
- package/dist/core/hooks/use-chat-messages.d.ts +50 -11
- package/dist/core/hooks/use-chat-session.d.ts +5 -5
- package/dist/core/hooks/use-tool-calls.d.ts +52 -0
- package/dist/core/hooks/use-tool-calls.js +61 -0
- package/dist/core/schemas/chat.d.ts +166 -83
- package/dist/core/schemas/chat.js +27 -27
- package/dist/core/schemas/index.d.ts +1 -0
- package/dist/core/schemas/index.js +1 -0
- package/dist/core/schemas/tool-call.d.ts +174 -0
- package/dist/core/schemas/tool-call.js +130 -0
- package/dist/core/store/chat-store.d.ts +28 -28
- package/dist/core/store/chat-store.js +123 -59
- package/dist/gui/components/ChatLayout.js +11 -10
- package/dist/gui/components/Dialog.js +8 -84
- package/dist/gui/components/Label.js +2 -12
- package/dist/gui/components/MessageContent.js +4 -1
- package/dist/gui/components/Select.js +12 -118
- package/dist/gui/components/Tabs.js +4 -32
- package/dist/gui/components/ToolCall.d.ts +8 -0
- package/dist/gui/components/ToolCall.js +100 -0
- package/dist/gui/components/ToolCallList.d.ts +9 -0
- package/dist/gui/components/ToolCallList.js +22 -0
- package/dist/gui/components/index.d.ts +2 -0
- package/dist/gui/components/index.js +2 -0
- package/dist/gui/components/resizable.d.ts +7 -0
- package/dist/gui/components/resizable.js +7 -0
- package/dist/sdk/schemas/session.d.ts +390 -220
- package/dist/sdk/schemas/session.js +74 -29
- package/dist/sdk/transports/http.js +705 -472
- package/dist/sdk/transports/stdio.js +187 -32
- package/dist/tui/components/ChatView.js +19 -51
- package/dist/tui/components/MessageList.d.ts +2 -4
- package/dist/tui/components/MessageList.js +13 -37
- package/dist/tui/components/ToolCall.d.ts +9 -0
- package/dist/tui/components/ToolCall.js +41 -0
- package/dist/tui/components/ToolCallList.d.ts +8 -0
- package/dist/tui/components/ToolCallList.js +17 -0
- package/dist/tui/components/index.d.ts +2 -0
- package/dist/tui/components/index.js +2 -0
- package/package.json +4 -2
|
@@ -55,43 +55,198 @@ export class StdioTransport {
|
|
|
55
55
|
// Handle session updates from the agent
|
|
56
56
|
const paramsExtended = params;
|
|
57
57
|
const update = paramsExtended.update;
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
const sessionId = self.currentSessionId || params.sessionId;
|
|
59
|
+
// Handle ACP tool call notifications
|
|
60
|
+
if (update?.sessionUpdate === "tool_call") {
|
|
61
|
+
// Initial tool call notification
|
|
62
|
+
const toolCall = {
|
|
63
|
+
id: update.toolCallId ?? "",
|
|
64
|
+
title: update.title ?? "",
|
|
65
|
+
kind: update.kind || "other",
|
|
66
|
+
status: update.status || "pending",
|
|
67
|
+
locations: update.locations,
|
|
68
|
+
rawInput: update.rawInput,
|
|
69
|
+
tokenUsage: update.tokenUsage,
|
|
70
|
+
content: update.content?.map((c) => {
|
|
71
|
+
// Type guard to safely check properties
|
|
72
|
+
if (typeof c !== "object" || c === null) {
|
|
73
|
+
return { type: "text", text: "" };
|
|
74
|
+
}
|
|
75
|
+
const content = c;
|
|
76
|
+
// Handle ACP nested content format
|
|
77
|
+
if (content.type === "content" &&
|
|
78
|
+
typeof content.content === "object" &&
|
|
79
|
+
content.content !== null) {
|
|
80
|
+
const innerContent = content.content;
|
|
81
|
+
if (innerContent.type === "text") {
|
|
82
|
+
return {
|
|
83
|
+
type: "content",
|
|
84
|
+
content: {
|
|
85
|
+
type: "text",
|
|
86
|
+
text: typeof innerContent.text === "string"
|
|
87
|
+
? innerContent.text
|
|
88
|
+
: "",
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Handle legacy direct formats
|
|
94
|
+
if (content.type === "text")
|
|
95
|
+
return {
|
|
96
|
+
type: "text",
|
|
97
|
+
text: typeof content.text === "string" ? content.text : "",
|
|
98
|
+
};
|
|
99
|
+
if (content.type === "diff")
|
|
100
|
+
return {
|
|
101
|
+
type: "diff",
|
|
102
|
+
path: typeof content.path === "string" ? content.path : "",
|
|
103
|
+
oldText: typeof content.oldText === "string"
|
|
104
|
+
? content.oldText
|
|
105
|
+
: "",
|
|
106
|
+
newText: typeof content.newText === "string"
|
|
107
|
+
? content.newText
|
|
108
|
+
: "",
|
|
109
|
+
line: typeof content.line === "number"
|
|
110
|
+
? content.line
|
|
111
|
+
: null,
|
|
112
|
+
};
|
|
113
|
+
if (content.type === "terminal")
|
|
114
|
+
return {
|
|
115
|
+
type: "terminal",
|
|
116
|
+
terminalId: typeof content.terminalId === "string"
|
|
117
|
+
? content.terminalId
|
|
118
|
+
: "",
|
|
119
|
+
};
|
|
120
|
+
return { type: "text", text: "" };
|
|
121
|
+
}),
|
|
122
|
+
startedAt: Date.now(),
|
|
123
|
+
};
|
|
124
|
+
const sessionUpdate = {
|
|
125
|
+
type: "tool_call",
|
|
126
|
+
sessionId,
|
|
127
|
+
status: "active",
|
|
128
|
+
toolCall: toolCall,
|
|
129
|
+
};
|
|
130
|
+
self.notifySessionUpdate(sessionUpdate);
|
|
131
|
+
}
|
|
132
|
+
else if (update?.sessionUpdate === "tool_call_update") {
|
|
133
|
+
// Tool call update notification
|
|
134
|
+
const toolCallUpdate = {
|
|
135
|
+
id: update.toolCallId ?? "",
|
|
136
|
+
status: update.status,
|
|
137
|
+
locations: update.locations,
|
|
138
|
+
rawOutput: update.rawOutput,
|
|
139
|
+
tokenUsage: update.tokenUsage,
|
|
140
|
+
content: update.content?.map((c) => {
|
|
141
|
+
// Type guard to safely check properties
|
|
142
|
+
if (typeof c !== "object" || c === null) {
|
|
143
|
+
return { type: "text", text: "" };
|
|
144
|
+
}
|
|
145
|
+
const content = c;
|
|
146
|
+
// Handle ACP nested content format
|
|
147
|
+
if (content.type === "content" &&
|
|
148
|
+
typeof content.content === "object" &&
|
|
149
|
+
content.content !== null) {
|
|
150
|
+
const innerContent = content.content;
|
|
151
|
+
if (innerContent.type === "text") {
|
|
152
|
+
return {
|
|
153
|
+
type: "content",
|
|
154
|
+
content: {
|
|
155
|
+
type: "text",
|
|
156
|
+
text: typeof innerContent.text === "string"
|
|
157
|
+
? innerContent.text
|
|
158
|
+
: "",
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Handle legacy direct formats
|
|
164
|
+
if (content.type === "text")
|
|
165
|
+
return {
|
|
166
|
+
type: "text",
|
|
167
|
+
text: typeof content.text === "string" ? content.text : "",
|
|
168
|
+
};
|
|
169
|
+
if (content.type === "diff")
|
|
170
|
+
return {
|
|
171
|
+
type: "diff",
|
|
172
|
+
path: typeof content.path === "string" ? content.path : "",
|
|
173
|
+
oldText: typeof content.oldText === "string"
|
|
174
|
+
? content.oldText
|
|
175
|
+
: "",
|
|
176
|
+
newText: typeof content.newText === "string"
|
|
177
|
+
? content.newText
|
|
178
|
+
: "",
|
|
179
|
+
line: typeof content.line === "number"
|
|
180
|
+
? content.line
|
|
181
|
+
: null,
|
|
182
|
+
};
|
|
183
|
+
if (content.type === "terminal")
|
|
184
|
+
return {
|
|
185
|
+
type: "terminal",
|
|
186
|
+
terminalId: typeof content.terminalId === "string"
|
|
187
|
+
? content.terminalId
|
|
188
|
+
: "",
|
|
189
|
+
};
|
|
190
|
+
return { type: "text", text: "" };
|
|
191
|
+
}),
|
|
192
|
+
error: update.error,
|
|
193
|
+
completedAt: update.status === "completed" || update.status === "failed"
|
|
194
|
+
? Date.now()
|
|
195
|
+
: undefined,
|
|
196
|
+
};
|
|
197
|
+
const sessionUpdate = {
|
|
198
|
+
type: "tool_call_update",
|
|
199
|
+
sessionId,
|
|
200
|
+
status: "active",
|
|
201
|
+
toolCallUpdate: toolCallUpdate,
|
|
202
|
+
};
|
|
203
|
+
self.notifySessionUpdate(sessionUpdate);
|
|
204
|
+
}
|
|
205
|
+
else if (update?.sessionUpdate === "agent_message_chunk") {
|
|
206
|
+
// Handle agent message chunks
|
|
207
|
+
const sessionUpdate = {
|
|
208
|
+
type: "generic",
|
|
209
|
+
sessionId,
|
|
210
|
+
status: "active",
|
|
211
|
+
};
|
|
212
|
+
// Queue message chunks if present
|
|
213
|
+
// For agent_message_chunk, content is an object, not an array
|
|
64
214
|
const content = update.content;
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
chunk =
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
id: params.sessionId,
|
|
77
|
-
role: "assistant",
|
|
78
|
-
contentDelta: content,
|
|
79
|
-
isComplete: false,
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
if (chunk) {
|
|
83
|
-
// Resolve any waiting receive() calls immediately
|
|
84
|
-
const resolver = self.chunkResolvers.shift();
|
|
85
|
-
if (resolver) {
|
|
86
|
-
resolver(chunk);
|
|
215
|
+
if (content && typeof content === "object") {
|
|
216
|
+
const contentObj = content;
|
|
217
|
+
let chunk = null;
|
|
218
|
+
if (contentObj.type === "text" &&
|
|
219
|
+
typeof contentObj.text === "string") {
|
|
220
|
+
chunk = {
|
|
221
|
+
id: params.sessionId,
|
|
222
|
+
role: "assistant",
|
|
223
|
+
contentDelta: { type: "text", text: contentObj.text },
|
|
224
|
+
isComplete: false,
|
|
225
|
+
};
|
|
87
226
|
}
|
|
88
|
-
|
|
89
|
-
//
|
|
90
|
-
self.
|
|
227
|
+
if (chunk) {
|
|
228
|
+
// Resolve any waiting receive() calls immediately
|
|
229
|
+
const resolver = self.chunkResolvers.shift();
|
|
230
|
+
if (resolver) {
|
|
231
|
+
resolver(chunk);
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
// Only queue if no resolver is waiting
|
|
235
|
+
self.messageQueue.push(chunk);
|
|
236
|
+
}
|
|
91
237
|
}
|
|
92
238
|
}
|
|
239
|
+
self.notifySessionUpdate(sessionUpdate);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
// Handle other session updates
|
|
243
|
+
const sessionUpdate = {
|
|
244
|
+
type: "generic",
|
|
245
|
+
sessionId,
|
|
246
|
+
status: "active",
|
|
247
|
+
};
|
|
248
|
+
self.notifySessionUpdate(sessionUpdate);
|
|
93
249
|
}
|
|
94
|
-
self.notifySessionUpdate(sessionUpdate);
|
|
95
250
|
},
|
|
96
251
|
async writeTextFile(params) {
|
|
97
252
|
const fs = await import("node:fs/promises");
|
|
@@ -1,57 +1,25 @@
|
|
|
1
|
-
import { Box } from "ink";
|
|
2
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
useChatMessages,
|
|
6
|
-
useChatSession,
|
|
7
|
-
useChatStore,
|
|
8
|
-
} from "../../core/index.js";
|
|
2
|
+
import { Box } from "ink";
|
|
3
|
+
import { useChatInput, useChatMessages, useChatSession, useChatStore, useToolCalls, } from "../../core/index.js";
|
|
9
4
|
import { InputBox } from "./InputBox.js";
|
|
10
5
|
import { MessageList } from "./MessageList.js";
|
|
11
6
|
import { StatusBar } from "./StatusBar.js";
|
|
12
7
|
export function ChatView({ client }) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
};
|
|
31
|
-
return _jsxs(Box, {
|
|
32
|
-
flexDirection: "column",
|
|
33
|
-
height: "100%",
|
|
34
|
-
children: [
|
|
35
|
-
_jsx(Box, {
|
|
36
|
-
flexGrow: 1,
|
|
37
|
-
flexDirection: "column",
|
|
38
|
-
children: _jsx(MessageList, { messages: messages }),
|
|
39
|
-
}),
|
|
40
|
-
_jsx(InputBox, {
|
|
41
|
-
value: value,
|
|
42
|
-
isSubmitting: isSubmitting,
|
|
43
|
-
attachedFiles: attachedFiles,
|
|
44
|
-
onChange: onChange,
|
|
45
|
-
onSubmit: onSubmit,
|
|
46
|
-
onEscape: handleEscape,
|
|
47
|
-
}),
|
|
48
|
-
_jsx(StatusBar, {
|
|
49
|
-
connectionStatus: connectionStatus,
|
|
50
|
-
sessionId: sessionId,
|
|
51
|
-
isStreaming: isStreaming,
|
|
52
|
-
streamingStartTime: streamingStartTime,
|
|
53
|
-
hasStreamingContent: hasStreamingContent,
|
|
54
|
-
}),
|
|
55
|
-
],
|
|
56
|
-
});
|
|
8
|
+
const setIsStreaming = useChatStore((state) => state.setIsStreaming);
|
|
9
|
+
const streamingStartTime = useChatStore((state) => state.streamingStartTime);
|
|
10
|
+
// Use headless hooks for business logic
|
|
11
|
+
const { connectionStatus, sessionId } = useChatSession(client);
|
|
12
|
+
const { messages, isStreaming } = useChatMessages(client);
|
|
13
|
+
useToolCalls(client); // Still need to subscribe to tool call events
|
|
14
|
+
const { value, isSubmitting, attachedFiles, onChange, onSubmit } = useChatInput(client);
|
|
15
|
+
// Check if we're actively receiving content (hide waiting indicator)
|
|
16
|
+
const hasStreamingContent = messages.some((msg) => msg.isStreaming && msg.content.length > 0);
|
|
17
|
+
// Callbacks for keyboard shortcuts
|
|
18
|
+
const handleEscape = () => {
|
|
19
|
+
if (isStreaming) {
|
|
20
|
+
// TODO: Implement proper cancellation when SDK supports it
|
|
21
|
+
setIsStreaming(false);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [_jsx(Box, { flexGrow: 1, flexDirection: "column", children: _jsx(MessageList, { messages: messages }) }), _jsx(InputBox, { value: value, isSubmitting: isSubmitting, attachedFiles: attachedFiles, onChange: onChange, onSubmit: onSubmit, onEscape: handleEscape }), _jsx(StatusBar, { connectionStatus: connectionStatus, sessionId: sessionId, isStreaming: isStreaming, streamingStartTime: streamingStartTime, hasStreamingContent: hasStreamingContent })] }));
|
|
57
25
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import type { DisplayMessage } from "../../core/index.js";
|
|
2
2
|
export interface MessageListProps {
|
|
3
|
-
|
|
3
|
+
messages: DisplayMessage[];
|
|
4
4
|
}
|
|
5
|
-
export declare function MessageList({
|
|
6
|
-
messages,
|
|
7
|
-
}: MessageListProps): import("react/jsx-runtime").JSX.Element;
|
|
5
|
+
export declare function MessageList({ messages }: MessageListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,43 +1,19 @@
|
|
|
1
|
-
import { Box, Text } from "ink";
|
|
2
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
3
|
import { GameOfLife } from "./GameOfLife.js";
|
|
4
|
+
import { ToolCall } from "./ToolCall.js";
|
|
4
5
|
export function MessageList({ messages }) {
|
|
5
|
-
|
|
6
|
-
flexDirection: "column",
|
|
7
|
-
paddingX: 1,
|
|
8
|
-
paddingY: 1,
|
|
9
|
-
children:
|
|
10
|
-
messages.length === 0
|
|
11
|
-
? _jsx(GameOfLife, {})
|
|
12
|
-
: messages.map((message) =>
|
|
13
|
-
_jsx(Message, { message: message }, message.id),
|
|
14
|
-
),
|
|
15
|
-
});
|
|
6
|
+
return (_jsx(Box, { flexDirection: "column", paddingX: 1, paddingY: 1, children: messages.length === 0 ? (_jsx(GameOfLife, {})) : (messages.map((message) => (_jsx(Message, { message: message }, message.id)))) }));
|
|
16
7
|
}
|
|
17
8
|
function Message({ message }) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
flexDirection: "column",
|
|
29
|
-
marginY: 1,
|
|
30
|
-
children: _jsxs(Box, {
|
|
31
|
-
children: [
|
|
32
|
-
_jsxs(Text, {
|
|
33
|
-
bold: true,
|
|
34
|
-
color: roleColor,
|
|
35
|
-
children: [roleSymbol, " "],
|
|
36
|
-
}),
|
|
37
|
-
message.role === "user"
|
|
38
|
-
? _jsx(Text, { backgroundColor: "gray", children: trimmedContent })
|
|
39
|
-
: _jsx(Text, { children: trimmedContent }),
|
|
40
|
-
],
|
|
41
|
-
}),
|
|
42
|
-
});
|
|
9
|
+
const roleColor = message.role === "user"
|
|
10
|
+
? "blue"
|
|
11
|
+
: message.role === "assistant"
|
|
12
|
+
? "green"
|
|
13
|
+
: "gray";
|
|
14
|
+
const roleSymbol = message.role === "user" ? ">" : message.role === "assistant" ? "⏺" : "•";
|
|
15
|
+
const trimmedContent = message.content?.trim() || "";
|
|
16
|
+
return (_jsxs(Box, { flexDirection: "column", marginY: 1, children: [message.role === "assistant" &&
|
|
17
|
+
message.toolCalls &&
|
|
18
|
+
message.toolCalls.length > 0 && (_jsx(Box, { flexDirection: "column", marginLeft: 2, marginBottom: 1, children: message.toolCalls.map((toolCall) => (_jsx(ToolCall, { toolCall: toolCall }, toolCall.id))) })), _jsxs(Box, { children: [_jsxs(Text, { bold: true, color: roleColor, children: [roleSymbol, " "] }), message.role === "user" ? (_jsx(Text, { backgroundColor: "gray", children: trimmedContent })) : (_jsx(Text, { children: trimmedContent }))] })] }));
|
|
43
19
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
|
|
2
|
+
export interface ToolCallProps {
|
|
3
|
+
toolCall: ToolCallType;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* ToolCall component for TUI - displays a single tool call
|
|
7
|
+
* Always shows: tool name, start time, duration, and cost
|
|
8
|
+
*/
|
|
9
|
+
export declare function ToolCall({ toolCall }: ToolCallProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
/**
|
|
4
|
+
* Tool call status indicators for terminal
|
|
5
|
+
*/
|
|
6
|
+
const statusIndicators = {
|
|
7
|
+
pending: "⏳",
|
|
8
|
+
in_progress: "⚙️ ",
|
|
9
|
+
completed: "✓",
|
|
10
|
+
failed: "✗",
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Tool call status colors
|
|
14
|
+
*/
|
|
15
|
+
const statusColors = {
|
|
16
|
+
pending: "gray",
|
|
17
|
+
in_progress: "blue",
|
|
18
|
+
completed: "green",
|
|
19
|
+
failed: "red",
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* ToolCall component for TUI - displays a single tool call
|
|
23
|
+
* Always shows: tool name, start time, duration, and cost
|
|
24
|
+
*/
|
|
25
|
+
export function ToolCall({ toolCall }) {
|
|
26
|
+
// Calculate duration if completed
|
|
27
|
+
const duration = toolCall.startedAt && toolCall.completedAt
|
|
28
|
+
? ((toolCall.completedAt - toolCall.startedAt) / 1000).toFixed(1)
|
|
29
|
+
: null;
|
|
30
|
+
// Format start time
|
|
31
|
+
const startTime = toolCall.startedAt
|
|
32
|
+
? new Date(toolCall.startedAt).toLocaleTimeString()
|
|
33
|
+
: "";
|
|
34
|
+
// Calculate cost (simplified - input tokens * rate + output tokens * rate)
|
|
35
|
+
// Using approximate rates: $3/M input, $15/M output for Claude
|
|
36
|
+
const cost = toolCall.tokenUsage?.inputTokens && toolCall.tokenUsage?.outputTokens
|
|
37
|
+
? ((toolCall.tokenUsage.inputTokens / 1_000_000) * 3 +
|
|
38
|
+
(toolCall.tokenUsage.outputTokens / 1_000_000) * 15).toFixed(4)
|
|
39
|
+
: null;
|
|
40
|
+
return (_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: statusColors[toolCall.status], children: statusIndicators[toolCall.status] }), _jsx(Text, { color: "cyan", bold: true, children: "[TOOL]" }), _jsx(Text, { children: toolCall.title }), startTime && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| ", startTime] })), duration && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| ", duration, "s"] })), cost && (_jsxs(Text, { color: "gray", dimColor: true, children: ["| $", cost] }))] }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
|
|
2
|
+
export interface ToolCallListProps {
|
|
3
|
+
toolCalls: ToolCallType[];
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* ToolCallList component for TUI - renders a list of tool calls
|
|
7
|
+
*/
|
|
8
|
+
export declare function ToolCallList({ toolCalls }: ToolCallListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from "ink";
|
|
3
|
+
import { ToolCall } from "./ToolCall.js";
|
|
4
|
+
/**
|
|
5
|
+
* ToolCallList component for TUI - renders a list of tool calls
|
|
6
|
+
*/
|
|
7
|
+
export function ToolCallList({ toolCalls }) {
|
|
8
|
+
if (!toolCalls || toolCalls.length === 0) {
|
|
9
|
+
return (_jsx(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", paddingY: 2, children: _jsx(Text, { dimColor: true, children: "No tool calls yet" }) }));
|
|
10
|
+
}
|
|
11
|
+
// Group by status for better organization
|
|
12
|
+
const inProgress = toolCalls.filter((tc) => tc.status === "in_progress");
|
|
13
|
+
const pending = toolCalls.filter((tc) => tc.status === "pending");
|
|
14
|
+
const completed = toolCalls.filter((tc) => tc.status === "completed");
|
|
15
|
+
const failed = toolCalls.filter((tc) => tc.status === "failed");
|
|
16
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [inProgress.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: "blue", children: ["\u25B6 In Progress (", inProgress.length, ")"] }), inProgress.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), pending.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: "gray", children: ["\u23F8 Pending (", pending.length, ")"] }), pending.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] })), completed.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: "green", children: ["\u2713 Completed (", completed.length, ")"] }), completed.slice(-5).map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id))), completed.length > 5 && (_jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["... and ", completed.length - 5, " more"] }) }))] })), failed.length > 0 && (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { bold: true, color: "red", children: ["\u2717 Failed (", failed.length, ")"] }), failed.map((tc) => (_jsx(ToolCall, { toolCall: tc }, tc.id)))] }))] }));
|
|
17
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@townco/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -46,11 +46,13 @@
|
|
|
46
46
|
"@radix-ui/react-select": "^2.2.6",
|
|
47
47
|
"@radix-ui/react-slot": "^1.2.4",
|
|
48
48
|
"@radix-ui/react-tabs": "^1.1.13",
|
|
49
|
+
"@uiw/react-json-view": "^2.0.0-alpha.39",
|
|
49
50
|
"bun": "^1.3.1",
|
|
50
51
|
"class-variance-authority": "^0.7.1",
|
|
51
52
|
"clsx": "^2.1.1",
|
|
52
53
|
"lucide-react": "^0.552.0",
|
|
53
54
|
"react-markdown": "^10.1.0",
|
|
55
|
+
"react-resizable-panels": "^3.0.6",
|
|
54
56
|
"remark-gfm": "^4.0.1",
|
|
55
57
|
"sonner": "^2.0.7",
|
|
56
58
|
"tailwind-merge": "^3.3.1",
|
|
@@ -59,7 +61,7 @@
|
|
|
59
61
|
},
|
|
60
62
|
"devDependencies": {
|
|
61
63
|
"@tailwindcss/postcss": "^4.1.17",
|
|
62
|
-
"@townco/tsconfig": "0.1.
|
|
64
|
+
"@townco/tsconfig": "0.1.14",
|
|
63
65
|
"@types/node": "^24.10.0",
|
|
64
66
|
"@types/react": "^19.2.2",
|
|
65
67
|
"ink": "^6.4.0",
|