@townco/ui 0.1.77 → 0.1.78
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/use-chat-messages.d.ts +4 -4
- package/dist/core/hooks/use-chat-messages.js +4 -1
- package/dist/core/hooks/use-chat-session.d.ts +1 -1
- package/dist/core/hooks/use-chat-session.js +5 -1
- package/dist/core/hooks/use-subagent-stream.js +6 -6
- package/dist/core/hooks/use-tool-calls.d.ts +3 -3
- package/dist/core/hooks/use-tool-calls.js +1 -1
- package/dist/core/schemas/chat.d.ts +10 -10
- package/dist/core/schemas/tool-call.d.ts +8 -8
- package/dist/core/store/chat-store.js +1 -0
- package/dist/core/utils/tool-summary.js +8 -3
- package/dist/core/utils/tool-verbiage.js +1 -1
- package/dist/gui/components/AppSidebar.d.ts +1 -1
- package/dist/gui/components/AppSidebar.js +4 -3
- package/dist/gui/components/Button.d.ts +1 -1
- package/dist/gui/components/ChatEmptyState.js +1 -1
- package/dist/gui/components/ChatHeader.d.ts +1 -28
- package/dist/gui/components/ChatHeader.js +4 -71
- package/dist/gui/components/ChatLayout.d.ts +6 -2
- package/dist/gui/components/ChatLayout.js +82 -33
- package/dist/gui/components/ChatView.js +28 -45
- package/dist/gui/components/ContextUsageButton.d.ts +0 -1
- package/dist/gui/components/ContextUsageButton.js +10 -3
- package/dist/gui/components/HookNotification.js +2 -1
- package/dist/gui/components/MessageContent.js +24 -160
- package/dist/gui/components/SessionHistory.js +1 -2
- package/dist/gui/components/SessionHistoryItem.js +1 -1
- package/dist/gui/components/Sidebar.js +27 -42
- package/dist/gui/components/SubAgentDetails.js +10 -14
- package/dist/gui/components/TodoSubline.js +1 -0
- package/dist/gui/components/ToolOperation.js +16 -75
- package/dist/gui/components/WorkProgress.js +5 -3
- package/dist/gui/components/index.d.ts +0 -1
- package/dist/gui/components/resizable.d.ts +1 -1
- package/dist/gui/constants.d.ts +6 -0
- package/dist/gui/constants.js +8 -0
- package/dist/gui/hooks/index.d.ts +1 -0
- package/dist/gui/hooks/index.js +1 -0
- package/dist/gui/hooks/use-lock-body-scroll.d.ts +7 -0
- package/dist/gui/hooks/use-lock-body-scroll.js +29 -0
- package/dist/gui/lib/motion.d.ts +12 -0
- package/dist/gui/lib/motion.js +69 -0
- package/dist/sdk/schemas/message.d.ts +2 -2
- package/dist/sdk/schemas/session.d.ts +18 -18
- package/dist/sdk/transports/http.d.ts +1 -1
- package/dist/sdk/transports/http.js +9 -0
- package/dist/sdk/transports/stdio.js +2 -2
- package/dist/sdk/transports/types.d.ts +11 -0
- package/dist/sdk/transports/types.js +28 -1
- package/package.json +3 -5
- package/dist/gui/components/InvokingGroup.d.ts +0 -9
- package/dist/gui/components/InvokingGroup.js +0 -16
- package/dist/gui/components/SubagentStream.d.ts +0 -23
- package/dist/gui/components/SubagentStream.js +0 -98
- package/dist/gui/components/ToolCall.d.ts +0 -8
- package/dist/gui/components/ToolCall.js +0 -234
- package/dist/gui/components/ToolCallGroup.d.ts +0 -8
- package/dist/gui/components/ToolCallGroup.js +0 -29
|
@@ -15,7 +15,7 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
15
15
|
id: string;
|
|
16
16
|
title: string;
|
|
17
17
|
kind: "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "switch_mode" | "other";
|
|
18
|
-
status: "
|
|
18
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
19
19
|
batchId?: string | undefined;
|
|
20
20
|
prettyName?: string | undefined;
|
|
21
21
|
icon?: string | undefined;
|
|
@@ -82,7 +82,7 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
82
82
|
toolCalls?: {
|
|
83
83
|
id: string;
|
|
84
84
|
title: string;
|
|
85
|
-
status: "
|
|
85
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
86
86
|
prettyName?: string | undefined;
|
|
87
87
|
icon?: string | undefined;
|
|
88
88
|
content?: ({
|
|
@@ -122,7 +122,7 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
122
122
|
toolCall: {
|
|
123
123
|
id: string;
|
|
124
124
|
title: string;
|
|
125
|
-
status: "
|
|
125
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
126
126
|
prettyName?: string | undefined;
|
|
127
127
|
icon?: string | undefined;
|
|
128
128
|
content?: ({
|
|
@@ -163,7 +163,7 @@ export declare function useChatMessages(client: AcpClient | null, startSession:
|
|
|
163
163
|
id: string;
|
|
164
164
|
hookType: "context_size" | "tool_response";
|
|
165
165
|
callback: string;
|
|
166
|
-
status: "error" | "
|
|
166
|
+
status: "error" | "completed" | "triggered";
|
|
167
167
|
threshold?: number | undefined;
|
|
168
168
|
currentPercentage?: number | undefined;
|
|
169
169
|
metadata?: {
|
|
@@ -103,8 +103,11 @@ export function useChatMessages(client, startSession) {
|
|
|
103
103
|
if (chunk.type === "content") {
|
|
104
104
|
// Content chunk - text streaming
|
|
105
105
|
// Update context size if provided (check both _meta.context_size and direct context_size)
|
|
106
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing dynamic properties from streaming chunks
|
|
106
107
|
const chunkMeta = chunk._meta;
|
|
107
|
-
const contextSizeData =
|
|
108
|
+
const contextSizeData =
|
|
109
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing dynamic properties from streaming chunks
|
|
110
|
+
chunkMeta?.context_size || chunk.context_size;
|
|
108
111
|
if (contextSizeData != null) {
|
|
109
112
|
const contextSize = contextSizeData;
|
|
110
113
|
logger.info("✅ Received context_size from backend", {
|
|
@@ -3,7 +3,7 @@ import type { AcpClient } from "../../sdk/client/index.js";
|
|
|
3
3
|
* Hook for managing chat session lifecycle
|
|
4
4
|
*/
|
|
5
5
|
export declare function useChatSession(client: AcpClient | null, initialSessionId?: string | null): {
|
|
6
|
-
connectionStatus: "error" | "
|
|
6
|
+
connectionStatus: "error" | "connecting" | "connected" | "disconnected";
|
|
7
7
|
sessionId: string | null;
|
|
8
8
|
connect: () => Promise<void>;
|
|
9
9
|
loadSession: (sessionIdToLoad: string) => Promise<void>;
|
|
@@ -21,8 +21,11 @@ export function useChatSession(client, initialSessionId) {
|
|
|
21
21
|
return;
|
|
22
22
|
const unsubscribe = client.onSessionUpdate((update) => {
|
|
23
23
|
// Extract context size from update metadata if available
|
|
24
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing dynamic metadata properties from session updates
|
|
24
25
|
const updateMeta = update._meta;
|
|
25
|
-
const contextSizeData =
|
|
26
|
+
const contextSizeData =
|
|
27
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing dynamic metadata properties from session updates
|
|
28
|
+
updateMeta?.context_size || update.context_size;
|
|
26
29
|
if (contextSizeData != null) {
|
|
27
30
|
const contextSize = contextSizeData;
|
|
28
31
|
logger.info("✅ Received context_size from session update", {
|
|
@@ -52,6 +55,7 @@ export function useChatSession(client, initialSessionId) {
|
|
|
52
55
|
const imageBlocks = [];
|
|
53
56
|
for (const c of update.message.content) {
|
|
54
57
|
if (c.type === "image") {
|
|
58
|
+
// biome-ignore lint/suspicious/noExplicitAny: Image blocks can have various formats (source object or direct properties)
|
|
55
59
|
const imgBlock = c;
|
|
56
60
|
// Handle both formats: direct data/mimeType or source object
|
|
57
61
|
if (imgBlock.source?.data) {
|
|
@@ -14,7 +14,7 @@ const logger = createLogger("subagent-stream");
|
|
|
14
14
|
export function useSubagentStream(options) {
|
|
15
15
|
const [messages, setMessages] = useState([]);
|
|
16
16
|
// Start as streaming=true if options provided, since we're about to connect
|
|
17
|
-
const [
|
|
17
|
+
const [_isStreaming, setIsStreaming] = useState(!!options);
|
|
18
18
|
const [hasCompleted, setHasCompleted] = useState(false);
|
|
19
19
|
const [error, setError] = useState(null);
|
|
20
20
|
const abortControllerRef = useRef(null);
|
|
@@ -221,12 +221,12 @@ export function useSubagentStream(options) {
|
|
|
221
221
|
logger.debug("Sub-agent stream completed");
|
|
222
222
|
}
|
|
223
223
|
}, [processSSEMessage]);
|
|
224
|
+
// Extract values from options (memoized to avoid dependency issues)
|
|
225
|
+
const port = options?.port;
|
|
226
|
+
const sessionId = options?.sessionId;
|
|
227
|
+
const host = options?.host ?? "localhost";
|
|
224
228
|
// Connect when options change
|
|
225
229
|
useEffect(() => {
|
|
226
|
-
if (!options) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const { port, sessionId, host = "localhost" } = options;
|
|
230
230
|
if (!port || !sessionId) {
|
|
231
231
|
return;
|
|
232
232
|
}
|
|
@@ -247,7 +247,7 @@ export function useSubagentStream(options) {
|
|
|
247
247
|
updateTimeoutRef.current = null;
|
|
248
248
|
}
|
|
249
249
|
};
|
|
250
|
-
}, [
|
|
250
|
+
}, [port, sessionId, host, connectToSubagent]);
|
|
251
251
|
// Derive streaming status: streaming if we haven't completed yet
|
|
252
252
|
const effectiveIsStreaming = !hasCompleted;
|
|
253
253
|
return { messages, isStreaming: effectiveIsStreaming, error };
|
|
@@ -13,7 +13,7 @@ export declare function useToolCalls(client: AcpClient | null): {
|
|
|
13
13
|
id: string;
|
|
14
14
|
title: string;
|
|
15
15
|
kind: "read" | "edit" | "delete" | "move" | "search" | "execute" | "think" | "fetch" | "switch_mode" | "other";
|
|
16
|
-
status: "
|
|
16
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
17
17
|
batchId?: string | undefined;
|
|
18
18
|
prettyName?: string | undefined;
|
|
19
19
|
icon?: string | undefined;
|
|
@@ -80,7 +80,7 @@ export declare function useToolCalls(client: AcpClient | null): {
|
|
|
80
80
|
toolCalls?: {
|
|
81
81
|
id: string;
|
|
82
82
|
title: string;
|
|
83
|
-
status: "
|
|
83
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
84
84
|
prettyName?: string | undefined;
|
|
85
85
|
icon?: string | undefined;
|
|
86
86
|
content?: ({
|
|
@@ -120,7 +120,7 @@ export declare function useToolCalls(client: AcpClient | null): {
|
|
|
120
120
|
toolCall: {
|
|
121
121
|
id: string;
|
|
122
122
|
title: string;
|
|
123
|
-
status: "
|
|
123
|
+
status: "pending" | "in_progress" | "completed" | "failed";
|
|
124
124
|
prettyName?: string | undefined;
|
|
125
125
|
icon?: string | undefined;
|
|
126
126
|
content?: ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createLogger } from "@townco/core";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
3
|
import { useChatStore } from "../store/chat-store.js";
|
|
4
|
-
const
|
|
4
|
+
const _logger = createLogger("use-tool-calls", "debug");
|
|
5
5
|
/**
|
|
6
6
|
* Hook to track and manage tool calls from ACP sessions
|
|
7
7
|
*
|
|
@@ -23,8 +23,8 @@ export declare const HookNotificationDisplay: z.ZodObject<{
|
|
|
23
23
|
callback: z.ZodString;
|
|
24
24
|
status: z.ZodEnum<{
|
|
25
25
|
error: "error";
|
|
26
|
-
triggered: "triggered";
|
|
27
26
|
completed: "completed";
|
|
27
|
+
triggered: "triggered";
|
|
28
28
|
}>;
|
|
29
29
|
threshold: z.ZodOptional<z.ZodNumber>;
|
|
30
30
|
currentPercentage: z.ZodOptional<z.ZodNumber>;
|
|
@@ -86,9 +86,9 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
86
86
|
other: "other";
|
|
87
87
|
}>;
|
|
88
88
|
status: z.ZodEnum<{
|
|
89
|
-
completed: "completed";
|
|
90
89
|
pending: "pending";
|
|
91
90
|
in_progress: "in_progress";
|
|
91
|
+
completed: "completed";
|
|
92
92
|
failed: "failed";
|
|
93
93
|
}>;
|
|
94
94
|
contentPosition: z.ZodOptional<z.ZodNumber>;
|
|
@@ -154,9 +154,9 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
154
154
|
prettyName: z.ZodOptional<z.ZodString>;
|
|
155
155
|
icon: z.ZodOptional<z.ZodString>;
|
|
156
156
|
status: z.ZodEnum<{
|
|
157
|
-
completed: "completed";
|
|
158
157
|
pending: "pending";
|
|
159
158
|
in_progress: "in_progress";
|
|
159
|
+
completed: "completed";
|
|
160
160
|
failed: "failed";
|
|
161
161
|
}>;
|
|
162
162
|
content: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -199,9 +199,9 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
199
199
|
prettyName: z.ZodOptional<z.ZodString>;
|
|
200
200
|
icon: z.ZodOptional<z.ZodString>;
|
|
201
201
|
status: z.ZodEnum<{
|
|
202
|
-
completed: "completed";
|
|
203
202
|
pending: "pending";
|
|
204
203
|
in_progress: "in_progress";
|
|
204
|
+
completed: "completed";
|
|
205
205
|
failed: "failed";
|
|
206
206
|
}>;
|
|
207
207
|
content: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -247,8 +247,8 @@ export declare const DisplayMessage: z.ZodObject<{
|
|
|
247
247
|
callback: z.ZodString;
|
|
248
248
|
status: z.ZodEnum<{
|
|
249
249
|
error: "error";
|
|
250
|
-
triggered: "triggered";
|
|
251
250
|
completed: "completed";
|
|
251
|
+
triggered: "triggered";
|
|
252
252
|
}>;
|
|
253
253
|
threshold: z.ZodOptional<z.ZodNumber>;
|
|
254
254
|
currentPercentage: z.ZodOptional<z.ZodNumber>;
|
|
@@ -338,9 +338,9 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
338
338
|
other: "other";
|
|
339
339
|
}>;
|
|
340
340
|
status: z.ZodEnum<{
|
|
341
|
-
completed: "completed";
|
|
342
341
|
pending: "pending";
|
|
343
342
|
in_progress: "in_progress";
|
|
343
|
+
completed: "completed";
|
|
344
344
|
failed: "failed";
|
|
345
345
|
}>;
|
|
346
346
|
contentPosition: z.ZodOptional<z.ZodNumber>;
|
|
@@ -406,9 +406,9 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
406
406
|
prettyName: z.ZodOptional<z.ZodString>;
|
|
407
407
|
icon: z.ZodOptional<z.ZodString>;
|
|
408
408
|
status: z.ZodEnum<{
|
|
409
|
-
completed: "completed";
|
|
410
409
|
pending: "pending";
|
|
411
410
|
in_progress: "in_progress";
|
|
411
|
+
completed: "completed";
|
|
412
412
|
failed: "failed";
|
|
413
413
|
}>;
|
|
414
414
|
content: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -451,9 +451,9 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
451
451
|
prettyName: z.ZodOptional<z.ZodString>;
|
|
452
452
|
icon: z.ZodOptional<z.ZodString>;
|
|
453
453
|
status: z.ZodEnum<{
|
|
454
|
-
completed: "completed";
|
|
455
454
|
pending: "pending";
|
|
456
455
|
in_progress: "in_progress";
|
|
456
|
+
completed: "completed";
|
|
457
457
|
failed: "failed";
|
|
458
458
|
}>;
|
|
459
459
|
content: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -499,8 +499,8 @@ export declare const ChatSessionState: z.ZodObject<{
|
|
|
499
499
|
callback: z.ZodString;
|
|
500
500
|
status: z.ZodEnum<{
|
|
501
501
|
error: "error";
|
|
502
|
-
triggered: "triggered";
|
|
503
502
|
completed: "completed";
|
|
503
|
+
triggered: "triggered";
|
|
504
504
|
}>;
|
|
505
505
|
threshold: z.ZodOptional<z.ZodNumber>;
|
|
506
506
|
currentPercentage: z.ZodOptional<z.ZodNumber>;
|
|
@@ -549,8 +549,8 @@ export type ChatSessionState = z.infer<typeof ChatSessionState>;
|
|
|
549
549
|
*/
|
|
550
550
|
export declare const ConnectionStatus: z.ZodEnum<{
|
|
551
551
|
error: "error";
|
|
552
|
-
disconnected: "disconnected";
|
|
553
552
|
connecting: "connecting";
|
|
554
553
|
connected: "connected";
|
|
554
|
+
disconnected: "disconnected";
|
|
555
555
|
}>;
|
|
556
556
|
export type ConnectionStatus = z.infer<typeof ConnectionStatus>;
|
|
@@ -13,16 +13,16 @@ export type ToolCallStatus = z.infer<typeof ToolCallStatusSchema>;
|
|
|
13
13
|
* Tool call categories for UI presentation
|
|
14
14
|
*/
|
|
15
15
|
export declare const ToolCallKindSchema: z.ZodEnum<{
|
|
16
|
-
search: "search";
|
|
17
|
-
execute: "execute";
|
|
18
|
-
move: "move";
|
|
19
|
-
other: "other";
|
|
20
16
|
read: "read";
|
|
21
17
|
edit: "edit";
|
|
22
18
|
delete: "delete";
|
|
19
|
+
move: "move";
|
|
20
|
+
search: "search";
|
|
21
|
+
execute: "execute";
|
|
23
22
|
think: "think";
|
|
24
23
|
fetch: "fetch";
|
|
25
24
|
switch_mode: "switch_mode";
|
|
25
|
+
other: "other";
|
|
26
26
|
}>;
|
|
27
27
|
export type ToolCallKind = z.infer<typeof ToolCallKindSchema>;
|
|
28
28
|
/**
|
|
@@ -280,16 +280,16 @@ export declare const ToolCallSchema: z.ZodObject<{
|
|
|
280
280
|
}, z.core.$strip>>;
|
|
281
281
|
subline: z.ZodOptional<z.ZodString>;
|
|
282
282
|
kind: z.ZodEnum<{
|
|
283
|
-
search: "search";
|
|
284
|
-
execute: "execute";
|
|
285
|
-
move: "move";
|
|
286
|
-
other: "other";
|
|
287
283
|
read: "read";
|
|
288
284
|
edit: "edit";
|
|
289
285
|
delete: "delete";
|
|
286
|
+
move: "move";
|
|
287
|
+
search: "search";
|
|
288
|
+
execute: "execute";
|
|
290
289
|
think: "think";
|
|
291
290
|
fetch: "fetch";
|
|
292
291
|
switch_mode: "switch_mode";
|
|
292
|
+
other: "other";
|
|
293
293
|
}>;
|
|
294
294
|
status: z.ZodEnum<{
|
|
295
295
|
pending: "pending";
|
|
@@ -359,6 +359,7 @@ export const useChatStore = create((set) => ({
|
|
|
359
359
|
if (existingIndex !== -1) {
|
|
360
360
|
// Merge: preserve triggered data (threshold, currentPercentage, triggeredAt),
|
|
361
361
|
// overlay completion data
|
|
362
|
+
// biome-ignore lint/style/noNonNullAssertion: existingIndex !== -1 ensures element exists
|
|
362
363
|
const existing = existingNotifications[existingIndex];
|
|
363
364
|
updatedNotifications = [...existingNotifications];
|
|
364
365
|
// Use backend timestamp if available, fallback to Date.now()
|
|
@@ -33,14 +33,15 @@ function extractKeyParameter(toolCall) {
|
|
|
33
33
|
function formatItemList(items, maxItems = 3) {
|
|
34
34
|
if (items.length === 0)
|
|
35
35
|
return "";
|
|
36
|
+
// biome-ignore lint/style/noNonNullAssertion: Length check ensures element exists
|
|
36
37
|
if (items.length === 1)
|
|
37
38
|
return items[0];
|
|
38
39
|
if (items.length <= maxItems) {
|
|
39
|
-
return items.slice(0, -1).join(", ")
|
|
40
|
+
return `${items.slice(0, -1).join(", ")} and ${items[items.length - 1]}`;
|
|
40
41
|
}
|
|
41
42
|
const shown = items.slice(0, maxItems);
|
|
42
43
|
const remaining = items.length - maxItems;
|
|
43
|
-
return shown.join(", ")
|
|
44
|
+
return `${shown.join(", ")} and ${remaining} more`;
|
|
44
45
|
}
|
|
45
46
|
/**
|
|
46
47
|
* Detect common patterns in file paths
|
|
@@ -49,13 +50,14 @@ function detectFilePattern(paths) {
|
|
|
49
50
|
if (paths.length === 0)
|
|
50
51
|
return null;
|
|
51
52
|
// Extract directory from first path
|
|
53
|
+
// biome-ignore lint/style/noNonNullAssertion: Length check ensures element exists
|
|
52
54
|
const firstPath = paths[0];
|
|
53
55
|
const lastSlash = firstPath.lastIndexOf("/");
|
|
54
56
|
if (lastSlash === -1)
|
|
55
57
|
return null;
|
|
56
58
|
const directory = firstPath.substring(0, lastSlash);
|
|
57
59
|
// Check if all paths are in the same directory
|
|
58
|
-
const allInSameDir = paths.every((p) => p.startsWith(directory
|
|
60
|
+
const allInSameDir = paths.every((p) => p.startsWith(`${directory}/`));
|
|
59
61
|
if (allInSameDir) {
|
|
60
62
|
return directory;
|
|
61
63
|
}
|
|
@@ -68,8 +70,10 @@ function generateSameToolSummary(toolCalls, tense) {
|
|
|
68
70
|
if (toolCalls.length === 0)
|
|
69
71
|
return "";
|
|
70
72
|
if (toolCalls.length === 1) {
|
|
73
|
+
// biome-ignore lint/style/noNonNullAssertion: Length check ensures element exists
|
|
71
74
|
return getToolCallVerbiage(toolCalls[0], tense);
|
|
72
75
|
}
|
|
76
|
+
// biome-ignore lint/style/noNonNullAssertion: Length check ensures element exists
|
|
73
77
|
const firstTool = toolCalls[0];
|
|
74
78
|
const toolName = firstTool.title;
|
|
75
79
|
// Extract parameters from all tool calls
|
|
@@ -144,6 +148,7 @@ export function generateSmartSummary(toolCalls, tense) {
|
|
|
144
148
|
if (toolCalls.length === 0)
|
|
145
149
|
return "";
|
|
146
150
|
if (toolCalls.length === 1) {
|
|
151
|
+
// biome-ignore lint/style/noNonNullAssertion: Length check ensures element exists
|
|
147
152
|
return getToolCallVerbiage(toolCalls[0], tense);
|
|
148
153
|
}
|
|
149
154
|
// Check if all tool calls are of the same type
|
|
@@ -128,7 +128,7 @@ function formatVerbiage(template, params) {
|
|
|
128
128
|
function truncate(text, maxLength) {
|
|
129
129
|
if (text.length <= maxLength)
|
|
130
130
|
return text;
|
|
131
|
-
return text.substring(0, maxLength - 1)
|
|
131
|
+
return `${text.substring(0, maxLength - 1)}…`;
|
|
132
132
|
}
|
|
133
133
|
/**
|
|
134
134
|
* Get display verbiage for a tool call
|
|
@@ -19,4 +19,4 @@ export interface AppSidebarProps {
|
|
|
19
19
|
/** Additional className for the sidebar */
|
|
20
20
|
className?: string;
|
|
21
21
|
}
|
|
22
|
-
export declare function AppSidebar({ client, currentSessionId, onSessionSelect, onNewSession, onRenameSession, onArchiveSession, onDeleteSession,
|
|
22
|
+
export declare function AppSidebar({ client, currentSessionId, onSessionSelect, onNewSession, onRenameSession, onArchiveSession, onDeleteSession, className, }: AppSidebarProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Plus } from "lucide-react";
|
|
2
|
+
import { Plus, Settings } from "lucide-react";
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
4
|
import { IconButton } from "./IconButton.js";
|
|
5
5
|
import { SessionHistory } from "./SessionHistory.js";
|
|
6
6
|
import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, useSidebar, } from "./Sidebar.js";
|
|
7
|
-
|
|
7
|
+
import { ThemeToggle } from "./ThemeToggle.js";
|
|
8
|
+
export function AppSidebar({ client, currentSessionId, onSessionSelect, onNewSession, onRenameSession, onArchiveSession, onDeleteSession, className, }) {
|
|
8
9
|
const { setOpenMobile } = useSidebar();
|
|
9
10
|
const handleNewSession = () => {
|
|
10
11
|
if (onNewSession) {
|
|
@@ -18,5 +19,5 @@ export function AppSidebar({ client, currentSessionId, onSessionSelect, onNewSes
|
|
|
18
19
|
}
|
|
19
20
|
setOpenMobile(false);
|
|
20
21
|
};
|
|
21
|
-
return (_jsxs(Sidebar, { className: cn("group-data-[side=left]:border-r-0", className), children: [_jsx(SidebarHeader, { className: "h-16 py-5
|
|
22
|
+
return (_jsxs(Sidebar, { className: cn("group-data-[side=left]:border-r-0", className), children: [_jsx(SidebarHeader, { className: "h-16 py-5 px-4 justify-center gap-0", children: _jsxs("div", { className: "flex flex-row items-center justify-between w-full pl-2", children: [_jsx("span", { className: "font-semibold text-xl tracking-tight", children: "Sessions" }), _jsx(IconButton, { onClick: handleNewSession, "aria-label": "New Chat", variant: "default", children: _jsx(Plus, { className: "size-4" }) })] }) }), _jsx(SidebarContent, { className: "gap-6", children: _jsx(SessionHistory, { client: client, currentSessionId: currentSessionId, onSessionSelect: onSessionSelect, onRenameSession: onRenameSession, onArchiveSession: onArchiveSession, onDeleteSession: onDeleteSession }) }), _jsx(SidebarFooter, { className: "p-0", children: _jsxs("div", { className: "border-t border-border pl-6 pr-4 py-5 flex justify-end gap-2", children: [_jsx(ThemeToggle, {}), _jsx(IconButton, { "aria-label": "Settings", children: _jsx(Settings, { className: "size-4 text-muted-foreground" }) })] }) })] }));
|
|
22
23
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type VariantProps } from "class-variance-authority";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
declare const buttonVariants: (props?: ({
|
|
4
|
-
variant?: "
|
|
4
|
+
variant?: "default" | "link" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
|
|
5
5
|
size?: "default" | "icon" | "sm" | "lg" | null | undefined;
|
|
6
6
|
} & import("class-variance-authority/types").ClassProp) | undefined) => string;
|
|
7
7
|
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
|
|
@@ -17,6 +17,6 @@ export const ChatEmptyState = React.forwardRef(({ title, titleElement, descripti
|
|
|
17
17
|
for (let i = 0; i < suggestedPrompts.length; i += 2) {
|
|
18
18
|
promptRows.push(suggestedPrompts.slice(i, i + 2));
|
|
19
19
|
}
|
|
20
|
-
return (_jsxs("div", { ref: ref, className: cn("flex flex-col items-start", className), ...props, children: [titleElement ? (_jsx("div", { className: "text-heading-4 text-text-primary mb-6", children: titleElement })) : (_jsx("h3", { className: "text-heading-4 text-text-primary mb-6", children: title })), _jsx("p", { className: "text-subheading text-text-secondary max-w-prose mb-6", children: description }), (onOpenFiles || onOpenSettings) && (_jsxs("div", { className: "flex items-center gap-1 -ml-3 mb-6", children: [onOpenFiles && (_jsxs("button", { type: "button", onClick: onOpenFiles, className: "inline-flex items-center gap-1 py-1.5 pr-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsx("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: "View Files" }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] })), onOpenSettings && (_jsxs("button", { type: "button", onClick: onOpenSettings, className: "inline-flex items-center gap-1 py-1.5 pr-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsxs("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: ["View Tools & MCPs", toolsAndMcpsCount !== undefined && toolsAndMcpsCount > 0 && (_jsxs("span", { className: "ml-1", children: ["(", toolsAndMcpsCount, ")"] }))] }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] }))] })), (guideUrl || onGuideClick) && (_jsxs("button", { type: "button", onClick: handleGuideClick, className: "inline-flex items-center gap-1 py-1.5 pr-3 -ml-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsx("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: guideText }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] })), suggestedPrompts.length > 0 && (_jsxs("div", { className: "flex flex-col gap-3 w-full max-w-prompt-container", children: [_jsx("p", { className: "text-
|
|
20
|
+
return (_jsxs("div", { ref: ref, className: cn("flex flex-col items-start", className), ...props, children: [titleElement ? (_jsx("div", { className: "text-heading-4 text-text-primary mb-6", children: titleElement })) : (_jsx("h3", { className: "text-heading-4 text-text-primary mb-6", children: title })), _jsx("p", { className: "text-subheading text-text-secondary max-w-prose mb-6", children: description }), (onOpenFiles || onOpenSettings) && (_jsxs("div", { className: "flex items-center gap-1 -ml-3 mb-6", children: [onOpenFiles && (_jsxs("button", { type: "button", onClick: onOpenFiles, className: "inline-flex items-center gap-1 py-1.5 pr-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsx("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: "View Files" }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] })), onOpenSettings && (_jsxs("button", { type: "button", onClick: onOpenSettings, className: "inline-flex items-center gap-1 py-1.5 pr-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsxs("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: ["View Tools & MCPs", toolsAndMcpsCount !== undefined && toolsAndMcpsCount > 0 && (_jsxs("span", { className: "ml-1", children: ["(", toolsAndMcpsCount, ")"] }))] }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] }))] })), (guideUrl || onGuideClick) && (_jsxs("button", { type: "button", onClick: handleGuideClick, className: "inline-flex items-center gap-1 py-1.5 pr-3 -ml-3 pl-3 rounded-lg hover:bg-accent transition-colors", children: [_jsx("span", { className: "text-paragraph-sm-medium text-foreground tracking-wide leading-none", children: guideText }), _jsx(ChevronRight, { className: "size-4 text-foreground shrink-0" })] })), suggestedPrompts.length > 0 && (_jsxs("div", { className: "flex flex-col gap-3 w-full max-w-prompt-container", children: [_jsx("p", { className: "text-text-tertiary", children: "Suggested Prompts" }), _jsx("div", { className: "flex flex-col gap-2.5", children: promptRows.map((row) => (_jsx("div", { className: "flex gap-2.5 items-center", children: row.map((prompt) => (_jsx("button", { type: "button", onClick: () => handlePromptClick(prompt), onMouseEnter: () => onPromptHover?.(prompt), onMouseLeave: () => onPromptLeave?.(), className: "flex-1 flex items-start gap-2 p-3 bg-secondary hover:bg-secondary/80 rounded-2xl transition-colors min-w-0", children: _jsx("span", { className: "text-paragraph font-normal leading-normal text-text-tertiary truncate", children: prompt }) }, prompt))) }, row.join("-")))) })] }))] }));
|
|
21
21
|
});
|
|
22
22
|
ChatEmptyState.displayName = "ChatEmptyState";
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
-
interface ChatHeaderContextValue {
|
|
3
|
-
isExpanded: boolean;
|
|
4
|
-
setIsExpanded: (expanded: boolean) => void;
|
|
5
|
-
}
|
|
6
|
-
declare const useChatHeaderContext: () => ChatHeaderContextValue;
|
|
7
2
|
export interface ChatHeaderRootProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
8
|
-
/** Initial expanded state */
|
|
9
|
-
defaultExpanded?: boolean;
|
|
10
|
-
/** Controlled expanded state */
|
|
11
|
-
expanded?: boolean;
|
|
12
|
-
/** Callback when expanded state changes */
|
|
13
|
-
onExpandedChange?: (expanded: boolean) => void;
|
|
14
3
|
}
|
|
15
4
|
declare const ChatHeaderRoot: React.ForwardRefExoticComponent<ChatHeaderRootProps & React.RefAttributes<HTMLDivElement>>;
|
|
16
5
|
export interface ChatHeaderTitleProps extends React.HTMLAttributes<HTMLHeadingElement> {
|
|
@@ -19,20 +8,4 @@ declare const ChatHeaderTitle: React.ForwardRefExoticComponent<ChatHeaderTitlePr
|
|
|
19
8
|
export interface ChatHeaderActionsProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
20
9
|
}
|
|
21
10
|
declare const ChatHeaderActions: React.ForwardRefExoticComponent<ChatHeaderActionsProps & React.RefAttributes<HTMLDivElement>>;
|
|
22
|
-
export
|
|
23
|
-
export interface ChatHeaderStatusIndicatorProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
24
|
-
/** Connection status */
|
|
25
|
-
status: ConnectionStatus;
|
|
26
|
-
/** Optional status text override */
|
|
27
|
-
statusText?: string;
|
|
28
|
-
}
|
|
29
|
-
declare const ChatHeaderStatusIndicator: React.ForwardRefExoticComponent<ChatHeaderStatusIndicatorProps & React.RefAttributes<HTMLDivElement>>;
|
|
30
|
-
export interface ChatHeaderToggleProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
31
|
-
/** Icon to display (should rotate based on expanded state) */
|
|
32
|
-
icon?: React.ReactNode;
|
|
33
|
-
}
|
|
34
|
-
declare const ChatHeaderToggle: React.ForwardRefExoticComponent<ChatHeaderToggleProps & React.RefAttributes<HTMLButtonElement>>;
|
|
35
|
-
export interface ChatHeaderExpandablePanelProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
36
|
-
}
|
|
37
|
-
declare const ChatHeaderExpandablePanel: React.ForwardRefExoticComponent<ChatHeaderExpandablePanelProps & React.RefAttributes<HTMLDivElement>>;
|
|
38
|
-
export { ChatHeaderRoot as Root, ChatHeaderTitle as Title, ChatHeaderActions as Actions, ChatHeaderStatusIndicator as StatusIndicator, ChatHeaderToggle as Toggle, ChatHeaderExpandablePanel as ExpandablePanel, useChatHeaderContext, };
|
|
11
|
+
export { ChatHeaderRoot as Root, ChatHeaderTitle as Title, ChatHeaderActions as Actions, };
|
|
@@ -1,29 +1,8 @@
|
|
|
1
|
-
import { jsx as _jsx
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import * as React from "react";
|
|
3
3
|
import { cn } from "../lib/utils.js";
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
const context = React.useContext(ChatHeaderContext);
|
|
7
|
-
if (!context) {
|
|
8
|
-
throw new Error("ChatHeader components must be used within ChatHeader.Root");
|
|
9
|
-
}
|
|
10
|
-
return context;
|
|
11
|
-
};
|
|
12
|
-
const ChatHeaderRoot = React.forwardRef(({ defaultExpanded = false, expanded: expandedProp, onExpandedChange, className, children, ...props }, ref) => {
|
|
13
|
-
const [isExpandedInternal, setIsExpandedInternal] = React.useState(defaultExpanded);
|
|
14
|
-
const isExpanded = expandedProp ?? isExpandedInternal;
|
|
15
|
-
const setIsExpanded = React.useCallback((expanded) => {
|
|
16
|
-
setIsExpandedInternal(expanded);
|
|
17
|
-
onExpandedChange?.(expanded);
|
|
18
|
-
}, [onExpandedChange]);
|
|
19
|
-
// Separate children into main content and expandable panel
|
|
20
|
-
const childrenArray = React.Children.toArray(children);
|
|
21
|
-
const expandablePanel = childrenArray.find((child) => React.isValidElement(child) &&
|
|
22
|
-
typeof child.type === "function" &&
|
|
23
|
-
child.type.displayName ===
|
|
24
|
-
"ChatHeader.ExpandablePanel");
|
|
25
|
-
const mainContent = childrenArray.filter((child) => child !== expandablePanel);
|
|
26
|
-
return (_jsxs(ChatHeaderContext.Provider, { value: { isExpanded, setIsExpanded }, children: [_jsx("div", { ref: ref, className: cn("flex items-center justify-between px-6 py-4", className), ...props, children: mainContent }), expandablePanel] }));
|
|
4
|
+
const ChatHeaderRoot = React.forwardRef(({ className, children, ...props }, ref) => {
|
|
5
|
+
return (_jsx("div", { ref: ref, className: cn("flex items-center justify-between", className), ...props, children: children }));
|
|
27
6
|
});
|
|
28
7
|
ChatHeaderRoot.displayName = "ChatHeader.Root";
|
|
29
8
|
const ChatHeaderTitle = React.forwardRef(({ className, children, ...props }, ref) => {
|
|
@@ -34,53 +13,7 @@ const ChatHeaderActions = React.forwardRef(({ className, children, ...props }, r
|
|
|
34
13
|
return (_jsx("div", { ref: ref, className: cn("flex items-center gap-3", className), ...props, children: children }));
|
|
35
14
|
});
|
|
36
15
|
ChatHeaderActions.displayName = "ChatHeader.Actions";
|
|
37
|
-
const getStatusColor = (status) => {
|
|
38
|
-
switch (status) {
|
|
39
|
-
case "connected":
|
|
40
|
-
return "bg-green-500";
|
|
41
|
-
case "connecting":
|
|
42
|
-
return "bg-yellow-500";
|
|
43
|
-
case "error":
|
|
44
|
-
return "bg-red-500";
|
|
45
|
-
default:
|
|
46
|
-
return "bg-gray-500";
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
const getDefaultStatusText = (status) => {
|
|
50
|
-
switch (status) {
|
|
51
|
-
case "connected":
|
|
52
|
-
return "Connected";
|
|
53
|
-
case "connecting":
|
|
54
|
-
return "Connecting...";
|
|
55
|
-
case "error":
|
|
56
|
-
return "Connection Error";
|
|
57
|
-
default:
|
|
58
|
-
return "No Server";
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
const ChatHeaderStatusIndicator = React.forwardRef(({ status, statusText, className, ...props }, ref) => {
|
|
62
|
-
const text = statusText ?? getDefaultStatusText(status);
|
|
63
|
-
const colorClass = getStatusColor(status);
|
|
64
|
-
return (_jsxs("div", { ref: ref, className: cn("flex items-center gap-2", className), ...props, children: [_jsx("div", { className: cn("h-2 w-2 rounded-full", colorClass) }), _jsx("span", { className: "text-paragraph-sm text-muted-foreground", children: text })] }));
|
|
65
|
-
});
|
|
66
|
-
ChatHeaderStatusIndicator.displayName = "ChatHeader.StatusIndicator";
|
|
67
|
-
const ChatHeaderToggle = React.forwardRef(({ icon, className, children, onClick, ...props }, ref) => {
|
|
68
|
-
const { isExpanded, setIsExpanded } = useChatHeaderContext();
|
|
69
|
-
const handleClick = (e) => {
|
|
70
|
-
setIsExpanded(!isExpanded);
|
|
71
|
-
onClick?.(e);
|
|
72
|
-
};
|
|
73
|
-
return (_jsxs("button", { ref: ref, type: "button", onClick: handleClick, className: cn("rounded p-1 transition-colors hover:bg-background lg:hidden", className), "aria-label": isExpanded ? "Collapse header" : "Expand header", ...props, children: [icon && (_jsx("div", { className: cn("transition-transform duration-200", isExpanded && "rotate-180"), children: icon })), children] }));
|
|
74
|
-
});
|
|
75
|
-
ChatHeaderToggle.displayName = "ChatHeader.Toggle";
|
|
76
|
-
const ChatHeaderExpandablePanel = React.forwardRef(({ className, children, ...props }, ref) => {
|
|
77
|
-
const { isExpanded } = useChatHeaderContext();
|
|
78
|
-
if (!isExpanded)
|
|
79
|
-
return null;
|
|
80
|
-
return (_jsx("div", { ref: ref, className: cn("absolute top-full left-0 right-0 z-50 border-b border-border bg-card px-6 py-4 shadow-lg lg:hidden", className), ...props, children: children }));
|
|
81
|
-
});
|
|
82
|
-
ChatHeaderExpandablePanel.displayName = "ChatHeader.ExpandablePanel";
|
|
83
16
|
/* -------------------------------------------------------------------------------------------------
|
|
84
17
|
* Exports
|
|
85
18
|
* -----------------------------------------------------------------------------------------------*/
|
|
86
|
-
export { ChatHeaderRoot as Root, ChatHeaderTitle as Title, ChatHeaderActions as Actions,
|
|
19
|
+
export { ChatHeaderRoot as Root, ChatHeaderTitle as Title, ChatHeaderActions as Actions, };
|
|
@@ -11,6 +11,8 @@ interface ChatLayoutContextValue {
|
|
|
11
11
|
panelOpen: boolean;
|
|
12
12
|
setPanelOpen: (open: boolean) => void;
|
|
13
13
|
togglePanel: () => void;
|
|
14
|
+
isDraggingAside: boolean;
|
|
15
|
+
setIsDraggingAside: (dragging: boolean) => void;
|
|
14
16
|
}
|
|
15
17
|
declare const ChatLayoutContext: React.Context<ChatLayoutContextValue | undefined>;
|
|
16
18
|
declare const useChatLayoutContext: () => ChatLayoutContextValue;
|
|
@@ -50,8 +52,10 @@ export interface ChatLayoutSidebarProps extends React.HTMLAttributes<HTMLDivElem
|
|
|
50
52
|
}
|
|
51
53
|
declare const ChatLayoutSidebar: React.ForwardRefExoticComponent<ChatLayoutSidebarProps & React.RefAttributes<HTMLDivElement>>;
|
|
52
54
|
export interface ChatLayoutAsideProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
53
|
-
/** Show panel on these breakpoints (default:
|
|
54
|
-
breakpoint?: "md" | "lg" | "xl" | "2xl";
|
|
55
|
+
/** Show panel on these breakpoints (default: md and above) */
|
|
56
|
+
breakpoint?: "sm" | "md" | "lg" | "xl" | "2xl";
|
|
57
|
+
/** Optional close button callback (for mobile overlay mode) */
|
|
58
|
+
onClose?: () => void;
|
|
55
59
|
}
|
|
56
60
|
declare const ChatLayoutAside: React.ForwardRefExoticComponent<ChatLayoutAsideProps & React.RefAttributes<HTMLDivElement>>;
|
|
57
61
|
export { ChatLayoutRoot as Root, ChatLayoutHeader as Header, ChatLayoutMain as Main, ChatLayoutBody as Body, ChatLayoutMessages as Messages, ChatLayoutFooter as Footer, ChatLayoutSidebar as Sidebar, ChatLayoutAside as Aside, ChatLayoutContext as Context, useChatLayoutContext, };
|