@townco/ui 0.1.83 → 0.1.96
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-input.js +13 -6
- package/dist/core/hooks/use-chat-messages.d.ts +17 -0
- package/dist/core/hooks/use-chat-messages.js +294 -10
- package/dist/core/schemas/chat.d.ts +20 -0
- package/dist/core/schemas/chat.js +4 -0
- package/dist/core/schemas/index.d.ts +1 -0
- package/dist/core/schemas/index.js +1 -0
- package/dist/core/schemas/source.d.ts +22 -0
- package/dist/core/schemas/source.js +45 -0
- package/dist/core/store/chat-store.d.ts +4 -0
- package/dist/core/store/chat-store.js +54 -0
- package/dist/gui/components/Actions.d.ts +15 -0
- package/dist/gui/components/Actions.js +22 -0
- package/dist/gui/components/ChatInput.d.ts +9 -1
- package/dist/gui/components/ChatInput.js +24 -6
- package/dist/gui/components/ChatInputCommandMenu.d.ts +1 -0
- package/dist/gui/components/ChatInputCommandMenu.js +22 -5
- package/dist/gui/components/ChatInputParameters.d.ts +13 -0
- package/dist/gui/components/ChatInputParameters.js +67 -0
- package/dist/gui/components/ChatLayout.d.ts +2 -0
- package/dist/gui/components/ChatLayout.js +183 -61
- package/dist/gui/components/ChatPanelTabContent.d.ts +7 -0
- package/dist/gui/components/ChatPanelTabContent.js +17 -7
- package/dist/gui/components/ChatView.js +105 -15
- package/dist/gui/components/CitationChip.d.ts +15 -0
- package/dist/gui/components/CitationChip.js +72 -0
- package/dist/gui/components/EditableUserMessage.d.ts +18 -0
- package/dist/gui/components/EditableUserMessage.js +109 -0
- package/dist/gui/components/MessageActions.d.ts +16 -0
- package/dist/gui/components/MessageActions.js +97 -0
- package/dist/gui/components/MessageContent.js +22 -7
- package/dist/gui/components/Response.d.ts +3 -0
- package/dist/gui/components/Response.js +30 -3
- package/dist/gui/components/Sidebar.js +1 -1
- package/dist/gui/components/TodoSubline.js +1 -1
- package/dist/gui/components/WorkProgress.js +7 -0
- package/dist/gui/components/index.d.ts +6 -1
- package/dist/gui/components/index.js +6 -1
- package/dist/gui/hooks/index.d.ts +1 -0
- package/dist/gui/hooks/index.js +1 -0
- package/dist/gui/hooks/use-favicon.d.ts +6 -0
- package/dist/gui/hooks/use-favicon.js +47 -0
- package/dist/gui/hooks/use-scroll-to-bottom.d.ts +14 -0
- package/dist/gui/hooks/use-scroll-to-bottom.js +317 -1
- package/dist/gui/index.d.ts +1 -1
- package/dist/gui/index.js +1 -1
- package/dist/gui/lib/motion.js +6 -6
- package/dist/gui/lib/remark-citations.d.ts +28 -0
- package/dist/gui/lib/remark-citations.js +70 -0
- package/dist/sdk/client/acp-client.d.ts +38 -1
- package/dist/sdk/client/acp-client.js +67 -3
- package/dist/sdk/schemas/message.d.ts +40 -0
- package/dist/sdk/schemas/message.js +20 -0
- package/dist/sdk/transports/http.d.ts +24 -1
- package/dist/sdk/transports/http.js +189 -1
- package/dist/sdk/transports/stdio.d.ts +1 -0
- package/dist/sdk/transports/stdio.js +39 -0
- package/dist/sdk/transports/types.d.ts +46 -1
- package/dist/sdk/transports/websocket.d.ts +1 -0
- package/dist/sdk/transports/websocket.js +4 -0
- package/dist/tui/components/ChatView.js +3 -4
- package/package.json +5 -3
- package/src/styles/global.css +71 -0
|
@@ -21,6 +21,7 @@ export declare class StdioTransport implements Transport {
|
|
|
21
21
|
connect(): Promise<void>;
|
|
22
22
|
disconnect(): Promise<void>;
|
|
23
23
|
send(message: Message): Promise<void>;
|
|
24
|
+
cancel(sessionId: string): Promise<void>;
|
|
24
25
|
receive(): AsyncIterableIterator<MessageChunk>;
|
|
25
26
|
isConnected(): boolean;
|
|
26
27
|
onSessionUpdate(callback: (update: SessionUpdate) => void): () => void;
|
|
@@ -525,6 +525,45 @@ export class StdioTransport {
|
|
|
525
525
|
throw err;
|
|
526
526
|
}
|
|
527
527
|
}
|
|
528
|
+
async cancel(sessionId) {
|
|
529
|
+
if (!this.connected || !this.connection) {
|
|
530
|
+
logger.warn("Cannot cancel: transport not connected");
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const targetSessionId = sessionId || this.currentSessionId;
|
|
534
|
+
if (!targetSessionId) {
|
|
535
|
+
logger.warn("Cannot cancel: no session ID");
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
logger.info("Cancelling session", { sessionId: targetSessionId });
|
|
539
|
+
try {
|
|
540
|
+
// Send cancel request through the ACP connection
|
|
541
|
+
// The ACP SDK connection has a cancel method
|
|
542
|
+
await this.connection.cancel({ sessionId: targetSessionId });
|
|
543
|
+
logger.debug("Cancel request sent successfully");
|
|
544
|
+
}
|
|
545
|
+
catch (error) {
|
|
546
|
+
// Log but don't throw - cancellation should be best-effort
|
|
547
|
+
logger.error("Error sending cancel request", { error });
|
|
548
|
+
}
|
|
549
|
+
// Mark stream as complete to exit receive() loop
|
|
550
|
+
this.streamComplete = true;
|
|
551
|
+
// Resolve any pending chunk resolvers with a completion marker
|
|
552
|
+
const completionChunk = {
|
|
553
|
+
type: "content",
|
|
554
|
+
id: targetSessionId,
|
|
555
|
+
role: "assistant",
|
|
556
|
+
contentDelta: { type: "text", text: "" },
|
|
557
|
+
isComplete: true,
|
|
558
|
+
};
|
|
559
|
+
// Drain all pending resolvers
|
|
560
|
+
while (this.chunkResolvers.length > 0) {
|
|
561
|
+
const resolver = this.chunkResolvers.shift();
|
|
562
|
+
if (resolver) {
|
|
563
|
+
resolver(completionChunk);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
}
|
|
528
567
|
async *receive() {
|
|
529
568
|
// Keep yielding chunks until stream is complete
|
|
530
569
|
while (!this.streamComplete) {
|
|
@@ -44,6 +44,30 @@ export interface AgentUiConfig {
|
|
|
44
44
|
/** Whether to hide the top bar (session switcher, debugger link, settings) */
|
|
45
45
|
hideTopBar?: boolean;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* A single option within a prompt parameter
|
|
49
|
+
*/
|
|
50
|
+
export interface PromptParameterOption {
|
|
51
|
+
/** Unique identifier for this option */
|
|
52
|
+
id: string;
|
|
53
|
+
/** Human-readable label shown in the UI */
|
|
54
|
+
label: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* A configurable prompt parameter that users can select per-message
|
|
58
|
+
*/
|
|
59
|
+
export interface PromptParameter {
|
|
60
|
+
/** Unique identifier for this parameter */
|
|
61
|
+
id: string;
|
|
62
|
+
/** Human-readable label shown in the UI dropdown */
|
|
63
|
+
label: string;
|
|
64
|
+
/** Optional description explaining what this parameter controls */
|
|
65
|
+
description?: string;
|
|
66
|
+
/** Available options for this parameter */
|
|
67
|
+
options: PromptParameterOption[];
|
|
68
|
+
/** The default option ID to use if none is selected */
|
|
69
|
+
defaultOptionId?: string;
|
|
70
|
+
}
|
|
47
71
|
/**
|
|
48
72
|
* Session summary for listing
|
|
49
73
|
*/
|
|
@@ -70,14 +94,33 @@ export interface Transport {
|
|
|
70
94
|
* List available sessions (optional, not all transports support this)
|
|
71
95
|
*/
|
|
72
96
|
listSessions?(): Promise<SessionSummary[]>;
|
|
97
|
+
/**
|
|
98
|
+
* Edit and resend a message from a specific point in the conversation.
|
|
99
|
+
* Truncates history to the specified message index and sends the new content.
|
|
100
|
+
* (optional, not all transports support this)
|
|
101
|
+
*/
|
|
102
|
+
editAndResend?(sessionId: string, messageIndex: number, prompt: Array<{
|
|
103
|
+
type: string;
|
|
104
|
+
text?: string;
|
|
105
|
+
[key: string]: unknown;
|
|
106
|
+
}>): Promise<void>;
|
|
73
107
|
/**
|
|
74
108
|
* Close the transport connection
|
|
75
109
|
*/
|
|
76
110
|
disconnect(): Promise<void>;
|
|
77
111
|
/**
|
|
78
112
|
* Send a message through the transport
|
|
113
|
+
* @param message - The message to send
|
|
114
|
+
* @param options - Optional parameters including promptParameters for per-message configuration
|
|
115
|
+
*/
|
|
116
|
+
send(message: Message, options?: {
|
|
117
|
+
promptParameters?: Record<string, string>;
|
|
118
|
+
}): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Cancel the current agent turn for a session
|
|
121
|
+
* Stops streaming, tool execution, and sub-agent processes
|
|
79
122
|
*/
|
|
80
|
-
|
|
123
|
+
cancel(sessionId: string): Promise<void>;
|
|
81
124
|
/**
|
|
82
125
|
* Receive messages from the transport
|
|
83
126
|
* Returns an async iterator for streaming support
|
|
@@ -104,6 +147,7 @@ export interface Transport {
|
|
|
104
147
|
* - subagents: List of subagents available via Task tool
|
|
105
148
|
* - initialMessage: Configuration for agent's initial message on session start
|
|
106
149
|
* - uiConfig: UI configuration for controlling interface appearance
|
|
150
|
+
* - promptParameters: Configurable parameters users can select per-message
|
|
107
151
|
*/
|
|
108
152
|
getAgentInfo?(): {
|
|
109
153
|
name?: string;
|
|
@@ -116,6 +160,7 @@ export interface Transport {
|
|
|
116
160
|
tools?: AgentToolInfo[];
|
|
117
161
|
mcps?: AgentMcpInfo[];
|
|
118
162
|
subagents?: AgentSubagentInfo[];
|
|
163
|
+
promptParameters?: PromptParameter[];
|
|
119
164
|
};
|
|
120
165
|
}
|
|
121
166
|
/**
|
|
@@ -14,6 +14,7 @@ export declare class WebSocketTransport implements Transport {
|
|
|
14
14
|
disconnect(): Promise<void>;
|
|
15
15
|
send(_message: Message): Promise<void>;
|
|
16
16
|
receive(): AsyncIterableIterator<MessageChunk>;
|
|
17
|
+
cancel(_sessionId: string): Promise<void>;
|
|
17
18
|
isConnected(): boolean;
|
|
18
19
|
onSessionUpdate(callback: (update: SessionUpdate) => void): () => void;
|
|
19
20
|
onError(callback: (error: Error) => void): () => void;
|
|
@@ -34,6 +34,10 @@ export class WebSocketTransport {
|
|
|
34
34
|
// TODO: Implement WebSocket message receiving
|
|
35
35
|
throw new Error("WebSocketTransport not yet implemented. Waiting for HTTP ACP server.");
|
|
36
36
|
}
|
|
37
|
+
async cancel(_sessionId) {
|
|
38
|
+
// TODO: Implement WebSocket cancel
|
|
39
|
+
throw new Error("WebSocketTransport not yet implemented. Waiting for HTTP ACP server.");
|
|
40
|
+
}
|
|
37
41
|
isConnected() {
|
|
38
42
|
return this.connected;
|
|
39
43
|
}
|
|
@@ -5,7 +5,6 @@ import { InputBox } from "./InputBox.js";
|
|
|
5
5
|
import { MessageList } from "./MessageList.js";
|
|
6
6
|
import { StatusBar } from "./StatusBar.js";
|
|
7
7
|
export function ChatView({ client }) {
|
|
8
|
-
const setIsStreaming = useChatStore((state) => state.setIsStreaming);
|
|
9
8
|
const _streamingStartTime = useChatStore((state) => state.streamingStartTime);
|
|
10
9
|
const _totalBilled = useChatStore((state) => state.totalBilled);
|
|
11
10
|
const _currentContext = useChatStore((state) => state.currentContext);
|
|
@@ -13,7 +12,7 @@ export function ChatView({ client }) {
|
|
|
13
12
|
const _tokenDisplayMode = useChatStore((state) => state.tokenDisplayMode);
|
|
14
13
|
// Use headless hooks for business logic
|
|
15
14
|
const { startSession } = useChatSession(client); // Subscribe to session changes
|
|
16
|
-
const { messages, isStreaming } = useChatMessages(client, startSession);
|
|
15
|
+
const { messages, isStreaming, cancel } = useChatMessages(client, startSession);
|
|
17
16
|
useToolCalls(client); // Still need to subscribe to tool call events
|
|
18
17
|
const { value, isSubmitting, attachedFiles, onChange, onSubmit } = useChatInput(client, startSession);
|
|
19
18
|
// Check if we're actively receiving content (hide waiting indicator)
|
|
@@ -21,8 +20,8 @@ export function ChatView({ client }) {
|
|
|
21
20
|
// Callbacks for keyboard shortcuts
|
|
22
21
|
const handleEscape = () => {
|
|
23
22
|
if (isStreaming) {
|
|
24
|
-
//
|
|
25
|
-
|
|
23
|
+
// Cancel the current agent turn
|
|
24
|
+
cancel();
|
|
26
25
|
}
|
|
27
26
|
};
|
|
28
27
|
return (_jsxs(Box, { flexDirection: "column", height: "100%", children: [
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@townco/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.96",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -49,7 +49,8 @@
|
|
|
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.
|
|
52
|
+
"@townco/core": "0.0.74",
|
|
53
|
+
"@types/mdast": "^4.0.4",
|
|
53
54
|
"@uiw/react-json-view": "^2.0.0-alpha.39",
|
|
54
55
|
"class-variance-authority": "^0.7.1",
|
|
55
56
|
"clsx": "^2.1.1",
|
|
@@ -61,11 +62,12 @@
|
|
|
61
62
|
"sonner": "^2.0.7",
|
|
62
63
|
"streamdown": "^1.6.9",
|
|
63
64
|
"tailwind-merge": "^3.3.1",
|
|
65
|
+
"unist-util-visit": "^5.0.0",
|
|
64
66
|
"zod": "^4.1.12",
|
|
65
67
|
"zustand": "^5.0.8"
|
|
66
68
|
},
|
|
67
69
|
"devDependencies": {
|
|
68
|
-
"@townco/tsconfig": "0.1.
|
|
70
|
+
"@townco/tsconfig": "0.1.93",
|
|
69
71
|
"@types/node": "^24.10.0",
|
|
70
72
|
"@types/react": "^19.2.2",
|
|
71
73
|
"ink": "^6.4.0",
|
package/src/styles/global.css
CHANGED
|
@@ -233,6 +233,77 @@
|
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
235
|
|
|
236
|
+
/* Streamdown list styling */
|
|
237
|
+
/* Ordered and unordered lists need explicit styling since Tailwind resets them */
|
|
238
|
+
[data-streamdown] ol,
|
|
239
|
+
[data-streamdown] ul {
|
|
240
|
+
padding-left: 1.5rem;
|
|
241
|
+
margin: 1rem 0;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
[data-streamdown] ol {
|
|
245
|
+
list-style-type: decimal;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
[data-streamdown] ul {
|
|
249
|
+
list-style-type: disc;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
[data-streamdown] li {
|
|
253
|
+
margin: 0.5rem 0;
|
|
254
|
+
padding-left: 0.25rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
[data-streamdown] li > p {
|
|
258
|
+
margin: 0;
|
|
259
|
+
display: inline;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* Nested lists */
|
|
263
|
+
[data-streamdown] ol ol,
|
|
264
|
+
[data-streamdown] ul ul,
|
|
265
|
+
[data-streamdown] ol ul,
|
|
266
|
+
[data-streamdown] ul ol {
|
|
267
|
+
margin: 0.5rem 0;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
[data-streamdown] ol ol {
|
|
271
|
+
list-style-type: lower-alpha;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
[data-streamdown] ol ol ol {
|
|
275
|
+
list-style-type: lower-roman;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/* Table styling */
|
|
279
|
+
[data-streamdown] table {
|
|
280
|
+
width: 100%;
|
|
281
|
+
border-collapse: collapse;
|
|
282
|
+
margin: 1rem 0;
|
|
283
|
+
font-size: 0.875rem;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
[data-streamdown] th,
|
|
287
|
+
[data-streamdown] td {
|
|
288
|
+
border: 1px solid var(--border);
|
|
289
|
+
padding: 0.5rem 0.75rem;
|
|
290
|
+
text-align: left;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
[data-streamdown] th {
|
|
294
|
+
background-color: var(--muted);
|
|
295
|
+
font-weight: 600;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
[data-streamdown] tr:nth-child(even) {
|
|
299
|
+
background-color: var(--muted);
|
|
300
|
+
opacity: 0.5;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
[data-streamdown] table code {
|
|
304
|
+
font-size: 0.8125rem;
|
|
305
|
+
}
|
|
306
|
+
|
|
236
307
|
/* Streamdown code block dark mode styling */
|
|
237
308
|
/* Ensure Shiki dark theme variables are applied in dark mode */
|
|
238
309
|
.dark [data-streamdown="code-block"] {
|