@townco/ui 0.1.68 → 0.1.69

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/core/hooks/use-chat-messages.d.ts +6 -1
  2. package/dist/core/hooks/use-chat-session.d.ts +1 -1
  3. package/dist/core/hooks/use-tool-calls.d.ts +6 -1
  4. package/dist/core/schemas/chat.d.ts +10 -0
  5. package/dist/core/schemas/tool-call.d.ts +5 -0
  6. package/dist/core/schemas/tool-call.js +8 -0
  7. package/dist/core/utils/tool-call-state.d.ts +30 -0
  8. package/dist/core/utils/tool-call-state.js +73 -0
  9. package/dist/core/utils/tool-summary.d.ts +13 -0
  10. package/dist/core/utils/tool-summary.js +172 -0
  11. package/dist/core/utils/tool-verbiage.d.ts +28 -0
  12. package/dist/core/utils/tool-verbiage.js +185 -0
  13. package/dist/gui/components/AppSidebar.d.ts +22 -0
  14. package/dist/gui/components/AppSidebar.js +22 -0
  15. package/dist/gui/components/Button.d.ts +1 -1
  16. package/dist/gui/components/ChatLayout.d.ts +5 -0
  17. package/dist/gui/components/ChatLayout.js +239 -132
  18. package/dist/gui/components/ChatView.js +42 -118
  19. package/dist/gui/components/MessageContent.js +151 -39
  20. package/dist/gui/components/SessionHistory.d.ts +10 -0
  21. package/dist/gui/components/SessionHistory.js +101 -0
  22. package/dist/gui/components/SessionHistoryItem.d.ts +11 -0
  23. package/dist/gui/components/SessionHistoryItem.js +24 -0
  24. package/dist/gui/components/Sheet.d.ts +25 -0
  25. package/dist/gui/components/Sheet.js +36 -0
  26. package/dist/gui/components/Sidebar.d.ts +65 -0
  27. package/dist/gui/components/Sidebar.js +231 -0
  28. package/dist/gui/components/SidebarToggle.d.ts +3 -0
  29. package/dist/gui/components/SidebarToggle.js +9 -0
  30. package/dist/gui/components/ToolCallList.js +3 -3
  31. package/dist/gui/components/ToolOperation.d.ts +11 -0
  32. package/dist/gui/components/ToolOperation.js +289 -0
  33. package/dist/gui/components/WorkProgress.d.ts +20 -0
  34. package/dist/gui/components/WorkProgress.js +79 -0
  35. package/dist/gui/components/index.d.ts +8 -1
  36. package/dist/gui/components/index.js +9 -1
  37. package/dist/gui/hooks/index.d.ts +1 -0
  38. package/dist/gui/hooks/index.js +1 -0
  39. package/dist/gui/hooks/use-mobile.d.ts +1 -0
  40. package/dist/gui/hooks/use-mobile.js +15 -0
  41. package/dist/gui/index.d.ts +1 -0
  42. package/dist/gui/index.js +2 -0
  43. package/dist/gui/lib/motion.d.ts +55 -0
  44. package/dist/gui/lib/motion.js +217 -0
  45. package/dist/sdk/schemas/session.d.ts +11 -6
  46. package/dist/sdk/transports/types.d.ts +5 -0
  47. package/package.json +8 -7
  48. package/src/styles/global.css +128 -1
  49. package/dist/gui/components/InvokingGroup.d.ts +0 -9
  50. package/dist/gui/components/InvokingGroup.js +0 -16
  51. package/dist/gui/components/SubagentStream.d.ts +0 -23
  52. package/dist/gui/components/SubagentStream.js +0 -98
  53. package/dist/gui/components/ToolCall.d.ts +0 -8
  54. package/dist/gui/components/ToolCall.js +0 -234
  55. package/dist/gui/components/ToolCallGroup.d.ts +0 -8
  56. package/dist/gui/components/ToolCallGroup.js +0 -29
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Motion utilities and reusable animation variants for the UI package
3
+ * Provides consistent animation behavior across components
4
+ */
5
+ // ============================================================================
6
+ // Standard Durations
7
+ // ============================================================================
8
+ export const motionDuration = {
9
+ fast: 0.15,
10
+ normal: 0.25,
11
+ slow: 0.4,
12
+ };
13
+ // ============================================================================
14
+ // Standard Easings
15
+ // ============================================================================
16
+ export const motionEasing = {
17
+ // Smooth, natural easing
18
+ smooth: [0.25, 0.1, 0.25, 1],
19
+ // Bouncy, playful easing
20
+ bounce: [0.68, -0.55, 0.265, 1.55],
21
+ // Sharp, snappy easing
22
+ sharp: [0.4, 0, 0.2, 1],
23
+ // Gentle, subtle easing
24
+ gentle: [0.25, 0.46, 0.45, 0.94],
25
+ };
26
+ // ============================================================================
27
+ // Fade Variants
28
+ // ============================================================================
29
+ export const fadeInVariants = {
30
+ hidden: {
31
+ opacity: 0,
32
+ },
33
+ visible: {
34
+ opacity: 1,
35
+ },
36
+ };
37
+ export const fadeInUpVariants = {
38
+ hidden: {
39
+ opacity: 0,
40
+ y: 8,
41
+ },
42
+ visible: {
43
+ opacity: 1,
44
+ y: 0,
45
+ },
46
+ };
47
+ // ============================================================================
48
+ // Expand/Collapse Variants
49
+ // ============================================================================
50
+ export const expandCollapseVariants = {
51
+ collapsed: {
52
+ height: 0,
53
+ opacity: 0,
54
+ overflow: "hidden",
55
+ },
56
+ expanded: {
57
+ height: "auto",
58
+ opacity: 1,
59
+ overflow: "visible",
60
+ },
61
+ };
62
+ export const slideDownVariants = {
63
+ hidden: {
64
+ opacity: 0,
65
+ height: 0,
66
+ y: -4,
67
+ },
68
+ visible: {
69
+ opacity: 1,
70
+ height: "auto",
71
+ y: 0,
72
+ },
73
+ };
74
+ // ============================================================================
75
+ // Shimmer Effect
76
+ // ============================================================================
77
+ export const shimmerTransition = {
78
+ duration: 2,
79
+ ease: "linear",
80
+ repeat: Infinity,
81
+ repeatType: "loop",
82
+ };
83
+ // For shimmer, we'll use a background gradient that shifts
84
+ export const shimmerVariants = {
85
+ idle: {
86
+ backgroundPosition: "200% 0",
87
+ },
88
+ active: {
89
+ backgroundPosition: "-200% 0",
90
+ },
91
+ };
92
+ // ============================================================================
93
+ // Scale Variants
94
+ // ============================================================================
95
+ export const scaleInVariants = {
96
+ hidden: {
97
+ scale: 0.95,
98
+ opacity: 0,
99
+ },
100
+ visible: {
101
+ scale: 1,
102
+ opacity: 1,
103
+ },
104
+ };
105
+ export const pulseVariants = {
106
+ idle: {
107
+ scale: 1,
108
+ },
109
+ pulse: {
110
+ scale: [1, 1.05, 1],
111
+ },
112
+ };
113
+ // ============================================================================
114
+ // Rotation Variants
115
+ // ============================================================================
116
+ export const rotateVariants = {
117
+ collapsed: {
118
+ rotate: 0,
119
+ },
120
+ expanded: {
121
+ rotate: 180,
122
+ },
123
+ };
124
+ // ============================================================================
125
+ // Standard Transitions
126
+ // ============================================================================
127
+ export const standardTransition = {
128
+ duration: motionDuration.normal,
129
+ ease: motionEasing.smooth,
130
+ };
131
+ export const fastTransition = {
132
+ duration: motionDuration.fast,
133
+ ease: motionEasing.sharp,
134
+ };
135
+ export const slowTransition = {
136
+ duration: motionDuration.slow,
137
+ ease: motionEasing.gentle,
138
+ };
139
+ export const springTransition = {
140
+ type: "spring",
141
+ stiffness: 300,
142
+ damping: 30,
143
+ };
144
+ export const gentleSpringTransition = {
145
+ type: "spring",
146
+ stiffness: 200,
147
+ damping: 25,
148
+ };
149
+ // ============================================================================
150
+ // Layout Transition (for shared layout animations)
151
+ // ============================================================================
152
+ export const layoutTransition = {
153
+ layout: {
154
+ duration: motionDuration.normal,
155
+ ease: motionEasing.smooth,
156
+ },
157
+ };
158
+ // ============================================================================
159
+ // Stagger Configurations
160
+ // ============================================================================
161
+ export const staggerChildren = {
162
+ visible: {
163
+ transition: {
164
+ staggerChildren: 0.05,
165
+ },
166
+ },
167
+ };
168
+ export const staggerChildrenFast = {
169
+ visible: {
170
+ transition: {
171
+ staggerChildren: 0.03,
172
+ },
173
+ },
174
+ };
175
+ // ============================================================================
176
+ // Slide Variants (for panels and drawers)
177
+ // ============================================================================
178
+ export const slideInFromRightVariants = {
179
+ hidden: {
180
+ x: "100%",
181
+ opacity: 0,
182
+ },
183
+ visible: {
184
+ x: 0,
185
+ opacity: 1,
186
+ },
187
+ };
188
+ export const slideOutToRightVariants = {
189
+ visible: {
190
+ x: 0,
191
+ opacity: 1,
192
+ },
193
+ hidden: {
194
+ x: "100%",
195
+ opacity: 0,
196
+ },
197
+ };
198
+ // ============================================================================
199
+ // Helper Functions
200
+ // ============================================================================
201
+ /**
202
+ * Get transition with reduced motion consideration
203
+ */
204
+ export function getTransition(shouldReduceMotion, transition = standardTransition) {
205
+ if (shouldReduceMotion) {
206
+ return {
207
+ duration: 0.01,
208
+ };
209
+ }
210
+ return transition;
211
+ }
212
+ /**
213
+ * Get duration with reduced motion consideration
214
+ */
215
+ export function getDuration(shouldReduceMotion, duration = motionDuration.normal) {
216
+ return shouldReduceMotion ? 0.01 : duration;
217
+ }
@@ -4,10 +4,10 @@ import { z } from "zod";
4
4
  */
5
5
  export declare const SessionStatus: z.ZodEnum<{
6
6
  error: "error";
7
+ active: "active";
7
8
  idle: "idle";
8
9
  connecting: "connecting";
9
10
  connected: "connected";
10
- active: "active";
11
11
  streaming: "streaming";
12
12
  disconnected: "disconnected";
13
13
  }>;
@@ -41,10 +41,10 @@ export declare const Session: z.ZodObject<{
41
41
  id: z.ZodString;
42
42
  status: z.ZodEnum<{
43
43
  error: "error";
44
+ active: "active";
44
45
  idle: "idle";
45
46
  connecting: "connecting";
46
47
  connected: "connected";
47
- active: "active";
48
48
  streaming: "streaming";
49
49
  disconnected: "disconnected";
50
50
  }>;
@@ -117,10 +117,10 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
117
117
  sessionId: z.ZodString;
118
118
  status: z.ZodOptional<z.ZodEnum<{
119
119
  error: "error";
120
+ active: "active";
120
121
  idle: "idle";
121
122
  connecting: "connecting";
122
123
  connected: "connected";
123
- active: "active";
124
124
  streaming: "streaming";
125
125
  disconnected: "disconnected";
126
126
  }>>;
@@ -178,6 +178,11 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
178
178
  title: z.ZodString;
179
179
  prettyName: z.ZodOptional<z.ZodString>;
180
180
  icon: z.ZodOptional<z.ZodString>;
181
+ verbiage: z.ZodOptional<z.ZodObject<{
182
+ active: z.ZodString;
183
+ past: z.ZodString;
184
+ paramKey: z.ZodOptional<z.ZodString>;
185
+ }, z.core.$strip>>;
181
186
  subline: z.ZodOptional<z.ZodString>;
182
187
  kind: z.ZodEnum<{
183
188
  read: "read";
@@ -349,10 +354,10 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
349
354
  sessionId: z.ZodString;
350
355
  status: z.ZodOptional<z.ZodEnum<{
351
356
  error: "error";
357
+ active: "active";
352
358
  idle: "idle";
353
359
  connecting: "connecting";
354
360
  connected: "connected";
355
- active: "active";
356
361
  streaming: "streaming";
357
362
  disconnected: "disconnected";
358
363
  }>>;
@@ -556,10 +561,10 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
556
561
  sessionId: z.ZodString;
557
562
  status: z.ZodOptional<z.ZodEnum<{
558
563
  error: "error";
564
+ active: "active";
559
565
  idle: "idle";
560
566
  connecting: "connecting";
561
567
  connected: "connected";
562
- active: "active";
563
568
  streaming: "streaming";
564
569
  disconnected: "disconnected";
565
570
  }>>;
@@ -621,10 +626,10 @@ export declare const SessionUpdate: z.ZodUnion<readonly [z.ZodObject<{
621
626
  sessionId: z.ZodString;
622
627
  status: z.ZodOptional<z.ZodEnum<{
623
628
  error: "error";
629
+ active: "active";
624
630
  idle: "idle";
625
631
  connecting: "connecting";
626
632
  connected: "connected";
627
- active: "active";
628
633
  streaming: "streaming";
629
634
  disconnected: "disconnected";
630
635
  }>>;
@@ -7,6 +7,11 @@ export interface AgentToolInfo {
7
7
  description?: string;
8
8
  prettyName?: string;
9
9
  icon?: string;
10
+ verbiage?: {
11
+ active: string;
12
+ past: string;
13
+ paramKey?: string;
14
+ };
10
15
  }
11
16
  /**
12
17
  * Agent MCP (Model Context Protocol) server information
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@townco/ui",
3
- "version": "0.1.68",
3
+ "version": "0.1.69",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -49,11 +49,12 @@
49
49
  "@radix-ui/react-slot": "^1.2.4",
50
50
  "@radix-ui/react-tabs": "^1.1.13",
51
51
  "@radix-ui/react-tooltip": "^1.2.8",
52
- "@townco/core": "0.0.46",
52
+ "@townco/core": "0.0.47",
53
53
  "@uiw/react-json-view": "^2.0.0-alpha.39",
54
54
  "bun": "^1.3.1",
55
55
  "class-variance-authority": "^0.7.1",
56
56
  "clsx": "^2.1.1",
57
+ "framer-motion": "^12.23.25",
57
58
  "lucide-react": "^0.552.0",
58
59
  "react-markdown": "^10.1.0",
59
60
  "react-resizable-panels": "^3.0.6",
@@ -66,18 +67,18 @@
66
67
  },
67
68
  "devDependencies": {
68
69
  "@tailwindcss/postcss": "^4.1.17",
69
- "@townco/tsconfig": "0.1.65",
70
+ "@townco/tsconfig": "0.1.66",
70
71
  "@types/node": "^24.10.0",
71
72
  "@types/react": "^19.2.2",
72
73
  "ink": "^6.4.0",
73
- "react": "^19.2.0",
74
+ "react": "19.2.1",
74
75
  "tailwindcss": "^4.1.17",
75
76
  "typescript": "^5.9.3"
76
77
  },
77
78
  "peerDependencies": {
78
- "ink": "^5.1.0",
79
- "react": "^19.2.0",
80
- "react-dom": "^19.2.0"
79
+ "ink": "^6.4.0",
80
+ "react": "^19.2.1",
81
+ "react-dom": "^19.2.1"
81
82
  },
82
83
  "peerDependenciesMeta": {
83
84
  "react": {
@@ -47,6 +47,14 @@
47
47
  --color-text-primary: var(--text-primary);
48
48
  --color-text-secondary: var(--text-secondary);
49
49
  --color-text-tertiary: var(--text-tertiary);
50
+
51
+ /* Sidebar colors */
52
+ --color-sidebar: var(--sidebar);
53
+ --color-sidebar-foreground: var(--sidebar-foreground);
54
+ --color-sidebar-accent: var(--sidebar-accent);
55
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
56
+ --color-sidebar-border: var(--sidebar-border);
57
+ --color-sidebar-ring: var(--sidebar-ring);
50
58
 
51
59
  /* Layout widths - max-width utilities */
52
60
  --max-width-chat: 720px;
@@ -149,8 +157,16 @@
149
157
 
150
158
  /* Text colors from design system */
151
159
  --text-primary: var(--color-neutral-900);
152
- --text-secondary: var(--color-neutral-600);
160
+ --text-secondary: var(--color-neutral-700);
153
161
  --text-tertiary: var(--color-neutral-500);
162
+
163
+ /* Sidebar colors */
164
+ --sidebar: var(--color-neutral-50);
165
+ --sidebar-foreground: var(--color-neutral-950);
166
+ --sidebar-accent: var(--color-neutral-100);
167
+ --sidebar-accent-foreground: var(--color-neutral-900);
168
+ --sidebar-border: var(--color-neutral-200);
169
+ --sidebar-ring: var(--color-neutral-900);
154
170
  }
155
171
 
156
172
  .dark {
@@ -194,6 +210,14 @@
194
210
  --text-primary: var(--color-neutral-50);
195
211
  --text-secondary: var(--color-neutral-400);
196
212
  --text-tertiary: var(--color-neutral-500);
213
+
214
+ /* Sidebar colors (dark mode) */
215
+ --sidebar: var(--color-neutral-900);
216
+ --sidebar-foreground: var(--color-neutral-50);
217
+ --sidebar-accent: var(--color-neutral-800);
218
+ --sidebar-accent-foreground: var(--color-neutral-50);
219
+ --sidebar-border: var(--color-neutral-800);
220
+ --sidebar-ring: var(--color-neutral-300);
197
221
  }
198
222
 
199
223
  @layer base {
@@ -417,3 +441,106 @@
417
441
  .animate-pulse-scale {
418
442
  animation: pulse-scale 1s ease-in-out infinite;
419
443
  }
444
+
445
+ @keyframes fadeIn {
446
+ from {
447
+ opacity: 0;
448
+ }
449
+ to {
450
+ opacity: 1;
451
+ }
452
+ }
453
+
454
+ .animate-fadeIn {
455
+ animation: fadeIn 200ms ease-out;
456
+ }
457
+
458
+ @keyframes slideDown {
459
+ from {
460
+ opacity: 0;
461
+ max-height: 0;
462
+ transform: translateY(-4px);
463
+ }
464
+ to {
465
+ opacity: 1;
466
+ max-height: 500px;
467
+ transform: translateY(0);
468
+ }
469
+ }
470
+
471
+ .animate-slideDown {
472
+ animation: slideDown 250ms ease-out;
473
+ }
474
+
475
+ @keyframes collapseUp {
476
+ from {
477
+ opacity: 1;
478
+ max-height: 500px;
479
+ transform: translateY(0);
480
+ }
481
+ to {
482
+ opacity: 0;
483
+ max-height: 0;
484
+ transform: translateY(-4px);
485
+ }
486
+ }
487
+
488
+ .animate-collapseUp {
489
+ animation: collapseUp 200ms ease-in;
490
+ }
491
+
492
+ @keyframes pulse-subtle {
493
+ 0%, 100% {
494
+ opacity: 1;
495
+ }
496
+ 50% {
497
+ opacity: 0.8;
498
+ }
499
+ }
500
+
501
+ .animate-pulse-subtle {
502
+ animation: pulse-subtle 2s ease-in-out infinite;
503
+ }
504
+
505
+ @keyframes typing {
506
+ 0%, 100% {
507
+ opacity: 0;
508
+ }
509
+ 50% {
510
+ opacity: 1;
511
+ }
512
+ }
513
+
514
+ .animate-typing {
515
+ animation: typing 1.4s ease-in-out infinite;
516
+ }
517
+
518
+ @keyframes shimmer {
519
+ 0% {
520
+ background-position: -200% 0;
521
+ }
522
+ 100% {
523
+ background-position: 200% 0;
524
+ }
525
+ }
526
+
527
+ .animate-shimmer {
528
+ background: linear-gradient(
529
+ 90deg,
530
+ transparent 0%,
531
+ rgba(255, 255, 255, 0.5) 50%,
532
+ transparent 100%
533
+ );
534
+ background-size: 200% 100%;
535
+ animation: shimmer 2s ease-in-out infinite;
536
+ }
537
+
538
+ .dark .animate-shimmer {
539
+ background: linear-gradient(
540
+ 90deg,
541
+ transparent 0%,
542
+ rgba(255, 255, 255, 0.05) 50%,
543
+ transparent 100%
544
+ );
545
+ background-size: 200% 100%;
546
+ }
@@ -1,9 +0,0 @@
1
- import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
- export interface InvokingGroupProps {
3
- toolCalls: ToolCallType[];
4
- }
5
- /**
6
- * InvokingGroup component - displays a group of preliminary (invoking) tool calls
7
- * Shows as "Invoking parallel operation (N)" with a summary of unique tool names
8
- */
9
- export declare function InvokingGroup({ toolCalls }: InvokingGroupProps): import("react/jsx-runtime").JSX.Element;
@@ -1,16 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ListVideo } from "lucide-react";
3
- import React from "react";
4
- /**
5
- * InvokingGroup component - displays a group of preliminary (invoking) tool calls
6
- * Shows as "Invoking parallel operation (N)" with a summary of unique tool names
7
- */
8
- export function InvokingGroup({ toolCalls }) {
9
- // Get unique display names for the summary
10
- const displayNames = toolCalls.map((tc) => tc.prettyName || tc.title);
11
- const uniqueNames = [...new Set(displayNames)];
12
- const summary = uniqueNames.length <= 2
13
- ? uniqueNames.join(", ")
14
- : `${uniqueNames.slice(0, 2).join(", ")} +${uniqueNames.length - 2} more`;
15
- return (_jsxs("div", { className: "flex flex-col my-4", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-paragraph-sm text-muted-foreground/50", children: [_jsx(ListVideo, { className: "h-3 w-3" }), _jsx("span", { children: "Invoking parallel operation" }), _jsx("span", { className: "text-[10px] bg-muted px-1.5 py-0.5 rounded text-muted-foreground/50", children: toolCalls.length })] }), _jsx("span", { className: "text-paragraph-sm text-muted-foreground/50 pl-4.5", children: summary })] }));
16
- }
@@ -1,23 +0,0 @@
1
- export interface SubagentStreamProps {
2
- /** Sub-agent HTTP port */
3
- port: number;
4
- /** Sub-agent session ID */
5
- sessionId: string;
6
- /** Optional host (defaults to localhost) */
7
- host?: string;
8
- /** Parent tool call status - use this to determine if sub-agent is running */
9
- parentStatus?: "pending" | "in_progress" | "completed" | "failed";
10
- /** Sub-agent name (for display) */
11
- agentName?: string | undefined;
12
- /** Query sent to the sub-agent */
13
- query?: string | undefined;
14
- }
15
- /**
16
- * SubagentStream component - displays streaming content from a sub-agent.
17
- *
18
- * This component:
19
- * - Connects directly to the sub-agent's SSE endpoint
20
- * - Displays streaming text and tool calls
21
- * - Renders in a collapsible section (collapsed by default)
22
- */
23
- export declare function SubagentStream({ port, sessionId, host, parentStatus, agentName, query, }: SubagentStreamProps): import("react/jsx-runtime").JSX.Element;
@@ -1,98 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ChevronDown, CircleDot, Loader2 } from "lucide-react";
3
- import React, { useCallback, useEffect, useRef, useState } from "react";
4
- import { useSubagentStream } from "../../core/hooks/use-subagent-stream.js";
5
- const SCROLL_THRESHOLD = 50; // px from bottom to consider "at bottom"
6
- /**
7
- * SubagentStream component - displays streaming content from a sub-agent.
8
- *
9
- * This component:
10
- * - Connects directly to the sub-agent's SSE endpoint
11
- * - Displays streaming text and tool calls
12
- * - Renders in a collapsible section (collapsed by default)
13
- */
14
- export function SubagentStream({ port, sessionId, host, parentStatus, agentName, query, }) {
15
- const [isExpanded, setIsExpanded] = useState(false); // Start collapsed for parallel ops
16
- const [isThinkingExpanded, setIsThinkingExpanded] = useState(true);
17
- const [isNearBottom, setIsNearBottom] = useState(true);
18
- const thinkingContainerRef = useRef(null);
19
- const { messages, isStreaming: hookIsStreaming, error } = useSubagentStream({
20
- port,
21
- sessionId,
22
- ...(host !== undefined ? { host } : {}),
23
- });
24
- // Use parent status as primary indicator, fall back to hook's streaming state
25
- // Parent is "in_progress" means sub-agent is definitely still running
26
- const isRunning = parentStatus === "in_progress" || parentStatus === "pending" || hookIsStreaming;
27
- // Get the current/latest message
28
- const currentMessage = messages[messages.length - 1];
29
- const hasContent = currentMessage &&
30
- (currentMessage.content ||
31
- (currentMessage.toolCalls && currentMessage.toolCalls.length > 0));
32
- // Auto-collapse Thinking when completed (so Output is the primary view)
33
- const prevIsRunningRef = useRef(isRunning);
34
- useEffect(() => {
35
- if (prevIsRunningRef.current && !isRunning) {
36
- // Just completed - collapse thinking to show output
37
- setIsThinkingExpanded(false);
38
- }
39
- prevIsRunningRef.current = isRunning;
40
- }, [isRunning]);
41
- // Check if user is near bottom of scroll area
42
- const checkScrollPosition = useCallback(() => {
43
- const container = thinkingContainerRef.current;
44
- if (!container)
45
- return;
46
- const { scrollTop, scrollHeight, clientHeight } = container;
47
- const distanceFromBottom = scrollHeight - scrollTop - clientHeight;
48
- setIsNearBottom(distanceFromBottom < SCROLL_THRESHOLD);
49
- }, []);
50
- // Scroll to bottom
51
- const scrollToBottom = useCallback(() => {
52
- const container = thinkingContainerRef.current;
53
- if (!container)
54
- return;
55
- container.scrollTop = container.scrollHeight;
56
- }, []);
57
- // Auto-scroll when content changes and user is near bottom
58
- useEffect(() => {
59
- if (isNearBottom && (isRunning || hasContent)) {
60
- scrollToBottom();
61
- }
62
- }, [currentMessage?.content, currentMessage?.toolCalls, isNearBottom, isRunning, hasContent, scrollToBottom]);
63
- // Set up scroll listener
64
- useEffect(() => {
65
- const container = thinkingContainerRef.current;
66
- if (!container)
67
- return;
68
- const handleScroll = () => checkScrollPosition();
69
- container.addEventListener("scroll", handleScroll, { passive: true });
70
- checkScrollPosition(); // Check initial position
71
- return () => container.removeEventListener("scroll", handleScroll);
72
- }, [checkScrollPosition, isThinkingExpanded, isExpanded]);
73
- // Get last line of streaming content for preview
74
- const lastLine = currentMessage?.content
75
- ? currentMessage.content.split("\n").filter(Boolean).pop() || ""
76
- : "";
77
- const previewText = lastLine.length > 100 ? `${lastLine.slice(0, 100)}...` : lastLine;
78
- return (_jsxs("div", { children: [!isExpanded && (_jsx("button", { type: "button", onClick: () => setIsExpanded(true), className: "w-full max-w-md text-left cursor-pointer bg-transparent border-none p-0", children: previewText ? (_jsx("p", { className: `text-paragraph-sm text-muted-foreground truncate ${isRunning ? "animate-pulse" : ""}`, children: previewText })) : isRunning ? (_jsx("p", { className: "text-paragraph-sm text-muted-foreground/50 italic animate-pulse", children: "Waiting for response..." })) : null })), isExpanded && (_jsxs("div", { className: "space-y-3", children: [(agentName || query) && (_jsxs("div", { children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Input" }), _jsxs("div", { className: "text-[11px] font-mono space-y-1", children: [agentName && (_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "agentName: " }), _jsx("span", { className: "text-foreground", children: agentName })] })), query && (_jsxs("div", { children: [_jsx("span", { className: "text-muted-foreground", children: "query: " }), _jsx("span", { className: "text-foreground", children: query })] }))] })] })), _jsxs("div", { children: [_jsxs("button", { type: "button", onClick: () => setIsThinkingExpanded(!isThinkingExpanded), className: "flex items-center gap-2 cursor-pointer bg-transparent border-none p-0 text-left group", children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider font-sans", children: "Thinking" }), _jsx(ChevronDown, { className: `h-3 w-3 text-muted-foreground/70 transition-transform duration-200 ${isThinkingExpanded ? "rotate-180" : ""}` })] }), isThinkingExpanded && (_jsxs("div", { ref: thinkingContainerRef, className: "mt-2 rounded-md overflow-hidden bg-muted/30 border border-border/50 max-h-[200px] overflow-y-auto", children: [error && (_jsxs("div", { className: "px-2 py-2 text-[11px] text-destructive", children: ["Error: ", error] })), !error && !hasContent && isRunning && (_jsx("div", { className: "px-2 py-2 text-[11px] text-muted-foreground", children: "Waiting for sub-agent response..." })), currentMessage && (_jsxs("div", { className: "px-2 py-2 space-y-2", children: [currentMessage.toolCalls &&
79
- currentMessage.toolCalls.length > 0 && (_jsx("div", { className: "space-y-1", children: currentMessage.toolCalls.map((tc) => (_jsx(SubagentToolCallItem, { toolCall: tc }, tc.id))) })), currentMessage.content && (_jsxs("div", { className: "text-[11px] text-foreground whitespace-pre-wrap font-mono", children: [currentMessage.content, currentMessage.isStreaming && (_jsx("span", { className: "inline-block w-1.5 h-3 bg-primary/70 ml-0.5 animate-pulse" }))] }))] }))] }))] }), !isRunning && currentMessage?.content && (_jsxs("div", { children: [_jsx("div", { className: "text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1.5 font-sans", children: "Output" }), _jsx("div", { className: "text-[11px] text-foreground whitespace-pre-wrap font-mono max-h-[200px] overflow-y-auto rounded-md bg-muted/30 border border-border/50 px-2 py-2", children: currentMessage.content })] }))] }))] }));
80
- }
81
- /**
82
- * Simple tool call display for sub-agent tool calls
83
- */
84
- function SubagentToolCallItem({ toolCall }) {
85
- const statusIcon = {
86
- pending: "...",
87
- in_progress: "",
88
- completed: "",
89
- failed: "",
90
- }[toolCall.status];
91
- const statusColor = {
92
- pending: "text-muted-foreground",
93
- in_progress: "text-blue-500",
94
- completed: "text-green-500",
95
- failed: "text-destructive",
96
- }[toolCall.status];
97
- return (_jsxs("div", { className: "flex items-center gap-2 text-[10px] text-muted-foreground", children: [_jsx("span", { className: statusColor, children: statusIcon }), _jsx("span", { className: "font-medium", children: toolCall.prettyName || toolCall.title }), toolCall.status === "in_progress" && (_jsx(Loader2, { className: "h-2.5 w-2.5 animate-spin" }))] }));
98
- }
@@ -1,8 +0,0 @@
1
- import type { ToolCall as ToolCallType } from "../../core/schemas/tool-call.js";
2
- export interface ToolCallProps {
3
- toolCall: ToolCallType;
4
- }
5
- /**
6
- * ToolCall component - displays a single tool call with collapsible details
7
- */
8
- export declare function ToolCall({ toolCall }: ToolCallProps): import("react/jsx-runtime").JSX.Element;