@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.
Files changed (58) hide show
  1. package/dist/core/hooks/use-chat-messages.d.ts +4 -4
  2. package/dist/core/hooks/use-chat-messages.js +4 -1
  3. package/dist/core/hooks/use-chat-session.d.ts +1 -1
  4. package/dist/core/hooks/use-chat-session.js +5 -1
  5. package/dist/core/hooks/use-subagent-stream.js +6 -6
  6. package/dist/core/hooks/use-tool-calls.d.ts +3 -3
  7. package/dist/core/hooks/use-tool-calls.js +1 -1
  8. package/dist/core/schemas/chat.d.ts +10 -10
  9. package/dist/core/schemas/tool-call.d.ts +8 -8
  10. package/dist/core/store/chat-store.js +1 -0
  11. package/dist/core/utils/tool-summary.js +8 -3
  12. package/dist/core/utils/tool-verbiage.js +1 -1
  13. package/dist/gui/components/AppSidebar.d.ts +1 -1
  14. package/dist/gui/components/AppSidebar.js +4 -3
  15. package/dist/gui/components/Button.d.ts +1 -1
  16. package/dist/gui/components/ChatEmptyState.js +1 -1
  17. package/dist/gui/components/ChatHeader.d.ts +1 -28
  18. package/dist/gui/components/ChatHeader.js +4 -71
  19. package/dist/gui/components/ChatLayout.d.ts +6 -2
  20. package/dist/gui/components/ChatLayout.js +82 -33
  21. package/dist/gui/components/ChatView.js +28 -45
  22. package/dist/gui/components/ContextUsageButton.d.ts +0 -1
  23. package/dist/gui/components/ContextUsageButton.js +10 -3
  24. package/dist/gui/components/HookNotification.js +2 -1
  25. package/dist/gui/components/MessageContent.js +24 -160
  26. package/dist/gui/components/SessionHistory.js +1 -2
  27. package/dist/gui/components/SessionHistoryItem.js +1 -1
  28. package/dist/gui/components/Sidebar.js +27 -42
  29. package/dist/gui/components/SubAgentDetails.js +10 -14
  30. package/dist/gui/components/TodoSubline.js +1 -0
  31. package/dist/gui/components/ToolOperation.js +16 -75
  32. package/dist/gui/components/WorkProgress.js +5 -3
  33. package/dist/gui/components/index.d.ts +0 -1
  34. package/dist/gui/components/resizable.d.ts +1 -1
  35. package/dist/gui/constants.d.ts +6 -0
  36. package/dist/gui/constants.js +8 -0
  37. package/dist/gui/hooks/index.d.ts +1 -0
  38. package/dist/gui/hooks/index.js +1 -0
  39. package/dist/gui/hooks/use-lock-body-scroll.d.ts +7 -0
  40. package/dist/gui/hooks/use-lock-body-scroll.js +29 -0
  41. package/dist/gui/lib/motion.d.ts +12 -0
  42. package/dist/gui/lib/motion.js +69 -0
  43. package/dist/sdk/schemas/message.d.ts +2 -2
  44. package/dist/sdk/schemas/session.d.ts +18 -18
  45. package/dist/sdk/transports/http.d.ts +1 -1
  46. package/dist/sdk/transports/http.js +9 -0
  47. package/dist/sdk/transports/stdio.js +2 -2
  48. package/dist/sdk/transports/types.d.ts +11 -0
  49. package/dist/sdk/transports/types.js +28 -1
  50. package/package.json +3 -5
  51. package/dist/gui/components/InvokingGroup.d.ts +0 -9
  52. package/dist/gui/components/InvokingGroup.js +0 -16
  53. package/dist/gui/components/SubagentStream.d.ts +0 -23
  54. package/dist/gui/components/SubagentStream.js +0 -98
  55. package/dist/gui/components/ToolCall.d.ts +0 -8
  56. package/dist/gui/components/ToolCall.js +0 -234
  57. package/dist/gui/components/ToolCallGroup.d.ts +0 -8
  58. 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: "completed" | "pending" | "in_progress" | "failed";
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: "completed" | "pending" | "in_progress" | "failed";
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: "completed" | "pending" | "in_progress" | "failed";
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" | "triggered" | "completed";
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 = chunkMeta?.context_size || chunk.context_size;
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" | "disconnected" | "connecting" | "connected";
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 = updateMeta?.context_size || update.context_size;
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 [isStreaming, setIsStreaming] = useState(!!options);
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
- }, [options?.port, options?.sessionId, options?.host, connectToSubagent]);
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: "completed" | "pending" | "in_progress" | "failed";
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: "completed" | "pending" | "in_progress" | "failed";
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: "completed" | "pending" | "in_progress" | "failed";
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 logger = createLogger("use-tool-calls", "debug");
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(", ") + " and " + items[items.length - 1];
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(", ") + ` and ${remaining} more`;
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, footer, className, }: AppSidebarProps): import("react/jsx-runtime").JSX.Element;
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
- export function AppSidebar({ client, currentSessionId, onSessionSelect, onNewSession, onRenameSession, onArchiveSession, onDeleteSession, footer, className, }) {
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 pl-6 pr-4", children: _jsxs("div", { className: "flex flex-row items-center justify-between w-full", children: [_jsx("span", { className: "font-semibold text-lg", children: "Sessions" }), _jsx(IconButton, { onClick: handleNewSession, "aria-label": "New Chat", children: _jsx(Plus, { className: "size-4 text-muted-foreground" }) })] }) }), _jsx(SidebarContent, { children: _jsx(SessionHistory, { client: client, currentSessionId: currentSessionId, onSessionSelect: onSessionSelect, onRenameSession: onRenameSession, onArchiveSession: onArchiveSession, onDeleteSession: onDeleteSession }) }), footer && _jsx(SidebarFooter, { children: footer })] }));
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?: "link" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
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-label 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("-")))) })] }))] }));
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 type ConnectionStatus = "connected" | "connecting" | "error" | "disconnected";
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, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "../lib/utils.js";
4
- const ChatHeaderContext = React.createContext(undefined);
5
- const useChatHeaderContext = () => {
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, ChatHeaderStatusIndicator as StatusIndicator, ChatHeaderToggle as Toggle, ChatHeaderExpandablePanel as ExpandablePanel, useChatHeaderContext, };
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: lg and above) */
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, };