@neuronection/assistant-ui 0.23.0 → 0.25.0
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/CHANGELOG.md +92 -0
- package/README.md +9 -0
- package/dist/about.js +1 -1
- package/dist/ai-magic-fill.js +1 -1
- package/dist/chat-branch-tree.d.ts +45 -0
- package/dist/chat-branch-tree.js +3 -0
- package/dist/chat-branch-tree.js.map +1 -0
- package/dist/chat-composer.d.ts +50 -0
- package/dist/chat-composer.js +3 -0
- package/dist/chat-composer.js.map +1 -0
- package/dist/chat-core.d.ts +154 -0
- package/dist/chat-core.js +4 -0
- package/dist/chat-core.js.map +1 -0
- package/dist/chat-drawer.d.ts +32 -0
- package/dist/chat-drawer.js +3 -0
- package/dist/chat-drawer.js.map +1 -0
- package/dist/chat-launcher.d.ts +28 -0
- package/dist/chat-launcher.js +3 -0
- package/dist/chat-launcher.js.map +1 -0
- package/dist/chat-markdown.d.ts +64 -0
- package/dist/chat-markdown.js +3 -0
- package/dist/chat-markdown.js.map +1 -0
- package/dist/chat-message.d.ts +108 -0
- package/dist/chat-message.js +3 -0
- package/dist/chat-message.js.map +1 -0
- package/dist/chat-panel.d.ts +28 -0
- package/dist/chat-panel.js +3 -0
- package/dist/chat-panel.js.map +1 -0
- package/dist/chat-reasoning.d.ts +27 -0
- package/dist/chat-reasoning.js +3 -0
- package/dist/chat-reasoning.js.map +1 -0
- package/dist/chat-session-list.d.ts +51 -0
- package/dist/chat-session-list.js +3 -0
- package/dist/chat-session-list.js.map +1 -0
- package/dist/chat-tool-card.d.ts +43 -0
- package/dist/chat-tool-card.js +3 -0
- package/dist/chat-tool-card.js.map +1 -0
- package/dist/chat-transcript.d.ts +33 -0
- package/dist/chat-transcript.js +3 -0
- package/dist/chat-transcript.js.map +1 -0
- package/dist/chat-turn-status.d.ts +26 -0
- package/dist/chat-turn-status.js +3 -0
- package/dist/chat-turn-status.js.map +1 -0
- package/dist/{chunk-CZ2BL2ZL.js → chunk-3JJ2WCPN.js} +2 -2
- package/dist/{chunk-CZ2BL2ZL.js.map → chunk-3JJ2WCPN.js.map} +1 -1
- package/dist/chunk-5IOLDMDY.js +3 -0
- package/dist/chunk-5IOLDMDY.js.map +1 -0
- package/dist/{chunk-EODV6JK7.js → chunk-5QTHDB3D.js} +3 -3
- package/dist/{chunk-EODV6JK7.js.map → chunk-5QTHDB3D.js.map} +1 -1
- package/dist/chunk-6E6RRLKU.js +3 -0
- package/dist/chunk-6E6RRLKU.js.map +1 -0
- package/dist/chunk-6GNLY2EB.js +272 -0
- package/dist/chunk-6GNLY2EB.js.map +1 -0
- package/dist/chunk-743FJDRA.js +184 -0
- package/dist/chunk-743FJDRA.js.map +1 -0
- package/dist/chunk-BSIWLVDX.js +217 -0
- package/dist/chunk-BSIWLVDX.js.map +1 -0
- package/dist/chunk-BVJT67B5.js +134 -0
- package/dist/chunk-BVJT67B5.js.map +1 -0
- package/dist/chunk-DZBTJZZV.js +201 -0
- package/dist/chunk-DZBTJZZV.js.map +1 -0
- package/dist/chunk-HQIYSW2D.js +131 -0
- package/dist/chunk-HQIYSW2D.js.map +1 -0
- package/dist/chunk-LSCWBNG3.js +3 -0
- package/dist/chunk-LSCWBNG3.js.map +1 -0
- package/dist/chunk-NBT7NLNU.js +120 -0
- package/dist/chunk-NBT7NLNU.js.map +1 -0
- package/dist/chunk-NKYPNEOX.js +198 -0
- package/dist/chunk-NKYPNEOX.js.map +1 -0
- package/dist/{chunk-INMAUGBK.js → chunk-OH7AJ3WY.js} +3 -3
- package/dist/{chunk-INMAUGBK.js.map → chunk-OH7AJ3WY.js.map} +1 -1
- package/dist/chunk-P75QKU5J.js +57 -0
- package/dist/chunk-P75QKU5J.js.map +1 -0
- package/dist/chunk-PAQ6YQ7Y.js +3 -0
- package/dist/chunk-PAQ6YQ7Y.js.map +1 -0
- package/dist/chunk-QVDCFZSE.js +72 -0
- package/dist/chunk-QVDCFZSE.js.map +1 -0
- package/dist/chunk-RPLEI6LQ.js +102 -0
- package/dist/chunk-RPLEI6LQ.js.map +1 -0
- package/dist/chunk-S62VJVWB.js +36 -0
- package/dist/chunk-S62VJVWB.js.map +1 -0
- package/dist/chunk-X3C2FBMR.js +141 -0
- package/dist/chunk-X3C2FBMR.js.map +1 -0
- package/dist/{chunk-7YLR4AGA.js → chunk-XZWLTTHA.js} +2 -2
- package/dist/{chunk-7YLR4AGA.js.map → chunk-XZWLTTHA.js.map} +1 -1
- package/dist/chunk-Z4IBQ53C.js +75 -0
- package/dist/chunk-Z4IBQ53C.js.map +1 -0
- package/dist/{chunk-7KD4MG75.js → chunk-ZK2V2LPA.js} +3 -3
- package/dist/chunk-ZK2V2LPA.js.map +1 -0
- package/dist/copy-button.js +1 -1
- package/dist/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/dist/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff +0 -0
- package/dist/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/dist/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff +0 -0
- package/dist/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/dist/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/dist/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +37 -23
- package/dist/index.js.map +1 -1
- package/dist/portal.js +1 -1
- package/dist/settings-shell.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tree-YaIz73vo.d.ts +54 -0
- package/dist/types-DZwzmyKE.d.ts +44 -0
- package/package.json +60 -2
- package/dist/chunk-7KD4MG75.js.map +0 -1
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import { useState, useRef, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/components/chat-core/liveTurnReducer.ts
|
|
4
|
+
var initialLiveTurnState = {
|
|
5
|
+
status: "idle",
|
|
6
|
+
text: null,
|
|
7
|
+
reasoning: null,
|
|
8
|
+
toolCalls: [],
|
|
9
|
+
nodes: [],
|
|
10
|
+
error: null,
|
|
11
|
+
interruptPayload: null,
|
|
12
|
+
runId: null,
|
|
13
|
+
stopped: false,
|
|
14
|
+
startedAt: null,
|
|
15
|
+
finishedAt: null
|
|
16
|
+
};
|
|
17
|
+
function isTerminal(state) {
|
|
18
|
+
return state.status === "done" || state.status === "error" || state.stopped;
|
|
19
|
+
}
|
|
20
|
+
function upsertNode(nodes, id, label, status) {
|
|
21
|
+
const index = nodes.findIndex((node) => node.id === id);
|
|
22
|
+
if (index === -1) {
|
|
23
|
+
return [...nodes, { id, label, status }];
|
|
24
|
+
}
|
|
25
|
+
const next = nodes.slice();
|
|
26
|
+
const current = next[index];
|
|
27
|
+
next[index] = { ...current, label: label ?? current.label, status };
|
|
28
|
+
return next;
|
|
29
|
+
}
|
|
30
|
+
function upsertToolCall(state, event) {
|
|
31
|
+
const index = state.toolCalls.findIndex((call) => call.id === event.id);
|
|
32
|
+
const merged = {
|
|
33
|
+
id: event.id,
|
|
34
|
+
name: event.name,
|
|
35
|
+
title: event.title,
|
|
36
|
+
status: event.status,
|
|
37
|
+
args: event.args,
|
|
38
|
+
result: event.result,
|
|
39
|
+
durationMs: event.durationMs
|
|
40
|
+
};
|
|
41
|
+
if (index === -1) {
|
|
42
|
+
return [...state.toolCalls, merged];
|
|
43
|
+
}
|
|
44
|
+
const next = state.toolCalls.slice();
|
|
45
|
+
const current = next[index];
|
|
46
|
+
next[index] = {
|
|
47
|
+
...current,
|
|
48
|
+
...merged,
|
|
49
|
+
title: merged.title ?? current.title,
|
|
50
|
+
args: merged.args ?? current.args,
|
|
51
|
+
result: merged.result ?? current.result,
|
|
52
|
+
durationMs: merged.durationMs ?? current.durationMs
|
|
53
|
+
};
|
|
54
|
+
return next;
|
|
55
|
+
}
|
|
56
|
+
function liveTurnReducer(state, action) {
|
|
57
|
+
switch (action.type) {
|
|
58
|
+
case "send":
|
|
59
|
+
return { ...initialLiveTurnState, status: "pending", startedAt: action.at };
|
|
60
|
+
case "stop": {
|
|
61
|
+
if (state.status !== "pending" && state.status !== "streaming") {
|
|
62
|
+
return state;
|
|
63
|
+
}
|
|
64
|
+
return { ...state, status: "interrupted", stopped: true, finishedAt: action.at };
|
|
65
|
+
}
|
|
66
|
+
case "reset":
|
|
67
|
+
return initialLiveTurnState;
|
|
68
|
+
case "event": {
|
|
69
|
+
const { event, at = Date.now() } = action;
|
|
70
|
+
if (state.status === "idle" || isTerminal(state)) {
|
|
71
|
+
return state;
|
|
72
|
+
}
|
|
73
|
+
if (event.run_id !== void 0 && state.runId !== null && event.run_id !== state.runId) {
|
|
74
|
+
return state;
|
|
75
|
+
}
|
|
76
|
+
switch (event.event) {
|
|
77
|
+
case "flow_started":
|
|
78
|
+
return {
|
|
79
|
+
...state,
|
|
80
|
+
runId: event.run_id ?? state.runId,
|
|
81
|
+
nodes: event.steps !== void 0 && event.steps.length > 0 ? event.steps.map((step) => ({ id: step.id, label: step.label, status: "pending" })) : state.nodes
|
|
82
|
+
};
|
|
83
|
+
case "node_started":
|
|
84
|
+
return { ...state, nodes: upsertNode(state.nodes, event.node, event.label, "running") };
|
|
85
|
+
case "node_finished":
|
|
86
|
+
return { ...state, nodes: upsertNode(state.nodes, event.node, event.label, event.outcome) };
|
|
87
|
+
case "delta": {
|
|
88
|
+
const streaming = { ...state, status: "streaming" };
|
|
89
|
+
if (event.kind === "reasoning") {
|
|
90
|
+
return { ...streaming, reasoning: (state.reasoning ?? "") + event.text };
|
|
91
|
+
}
|
|
92
|
+
return { ...streaming, text: (state.text ?? "") + event.text };
|
|
93
|
+
}
|
|
94
|
+
case "tool_call":
|
|
95
|
+
return { ...state, toolCalls: upsertToolCall(state, event) };
|
|
96
|
+
case "interrupt":
|
|
97
|
+
return { ...state, status: "interrupted", interruptPayload: event.payload, finishedAt: at };
|
|
98
|
+
case "flow_finished":
|
|
99
|
+
return { ...state, status: "done", finishedAt: at };
|
|
100
|
+
case "flow_failed":
|
|
101
|
+
return {
|
|
102
|
+
...state,
|
|
103
|
+
status: "error",
|
|
104
|
+
error: { code: event.code, message: event.message, retryable: event.retryable },
|
|
105
|
+
finishedAt: at
|
|
106
|
+
};
|
|
107
|
+
default:
|
|
108
|
+
return state;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
default:
|
|
112
|
+
return state;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function useChatStream(options) {
|
|
116
|
+
const { transport, flushMs = 33, timeoutMs = 9e4 } = options;
|
|
117
|
+
const [state, setState] = useState(initialLiveTurnState);
|
|
118
|
+
const stateRef = useRef(state);
|
|
119
|
+
const dispatch = useCallback((action) => {
|
|
120
|
+
const next = liveTurnReducer(stateRef.current, action);
|
|
121
|
+
stateRef.current = next;
|
|
122
|
+
setState(next);
|
|
123
|
+
}, []);
|
|
124
|
+
const transportRef = useRef(transport);
|
|
125
|
+
transportRef.current = transport;
|
|
126
|
+
const textBufferRef = useRef("");
|
|
127
|
+
const reasoningBufferRef = useRef("");
|
|
128
|
+
const flushTimerRef = useRef(null);
|
|
129
|
+
const timeoutTimerRef = useRef(null);
|
|
130
|
+
const terminalRef = useRef(false);
|
|
131
|
+
const cancelFlush = useCallback(() => {
|
|
132
|
+
if (flushTimerRef.current !== null) {
|
|
133
|
+
window.clearTimeout(flushTimerRef.current);
|
|
134
|
+
flushTimerRef.current = null;
|
|
135
|
+
}
|
|
136
|
+
}, []);
|
|
137
|
+
const flush = useCallback(() => {
|
|
138
|
+
cancelFlush();
|
|
139
|
+
const text = textBufferRef.current;
|
|
140
|
+
const reasoning = reasoningBufferRef.current;
|
|
141
|
+
textBufferRef.current = "";
|
|
142
|
+
reasoningBufferRef.current = "";
|
|
143
|
+
if (text !== "") {
|
|
144
|
+
dispatch({ type: "event", event: { event: "delta", text, kind: "text" } });
|
|
145
|
+
}
|
|
146
|
+
if (reasoning !== "") {
|
|
147
|
+
dispatch({ type: "event", event: { event: "delta", text: reasoning, kind: "reasoning" } });
|
|
148
|
+
}
|
|
149
|
+
}, [cancelFlush, dispatch]);
|
|
150
|
+
const scheduleFlush = useCallback(() => {
|
|
151
|
+
if (flushTimerRef.current !== null) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
flushTimerRef.current = window.setTimeout(() => {
|
|
155
|
+
flushTimerRef.current = null;
|
|
156
|
+
flush();
|
|
157
|
+
}, flushMs);
|
|
158
|
+
}, [flush, flushMs]);
|
|
159
|
+
const clearTimeoutTimer = useCallback(() => {
|
|
160
|
+
if (timeoutTimerRef.current !== null) {
|
|
161
|
+
window.clearTimeout(timeoutTimerRef.current);
|
|
162
|
+
timeoutTimerRef.current = null;
|
|
163
|
+
}
|
|
164
|
+
}, []);
|
|
165
|
+
useEffect(() => {
|
|
166
|
+
const unsubscribe = transportRef.current.subscribe({
|
|
167
|
+
onEvent: (event) => {
|
|
168
|
+
if (terminalRef.current) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (event.event === "delta") {
|
|
172
|
+
if (event.kind === "reasoning") {
|
|
173
|
+
reasoningBufferRef.current += event.text;
|
|
174
|
+
} else {
|
|
175
|
+
textBufferRef.current += event.text;
|
|
176
|
+
}
|
|
177
|
+
scheduleFlush();
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
flush();
|
|
181
|
+
if (event.event === "flow_finished" || event.event === "flow_failed") {
|
|
182
|
+
terminalRef.current = true;
|
|
183
|
+
clearTimeoutTimer();
|
|
184
|
+
}
|
|
185
|
+
dispatch({ type: "event", event });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
return unsubscribe;
|
|
189
|
+
}, [scheduleFlush, flush, clearTimeoutTimer, dispatch]);
|
|
190
|
+
useEffect(
|
|
191
|
+
() => () => {
|
|
192
|
+
cancelFlush();
|
|
193
|
+
clearTimeoutTimer();
|
|
194
|
+
},
|
|
195
|
+
[cancelFlush, clearTimeoutTimer]
|
|
196
|
+
);
|
|
197
|
+
const send = useCallback(
|
|
198
|
+
async (text) => {
|
|
199
|
+
const trimmed = text.trim();
|
|
200
|
+
const active = stateRef.current.status === "pending" || stateRef.current.status === "streaming";
|
|
201
|
+
if (trimmed === "" || active) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
terminalRef.current = false;
|
|
205
|
+
textBufferRef.current = "";
|
|
206
|
+
reasoningBufferRef.current = "";
|
|
207
|
+
cancelFlush();
|
|
208
|
+
clearTimeoutTimer();
|
|
209
|
+
dispatch({ type: "send", at: Date.now() });
|
|
210
|
+
timeoutTimerRef.current = window.setTimeout(() => {
|
|
211
|
+
if (terminalRef.current) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
terminalRef.current = true;
|
|
215
|
+
flush();
|
|
216
|
+
dispatch({
|
|
217
|
+
type: "event",
|
|
218
|
+
event: { event: "flow_failed", code: "timeout", message: "Chat turn timed out", retryable: true }
|
|
219
|
+
});
|
|
220
|
+
}, timeoutMs);
|
|
221
|
+
try {
|
|
222
|
+
await transportRef.current.send({ text: trimmed });
|
|
223
|
+
} catch (sendError) {
|
|
224
|
+
if (terminalRef.current) {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
terminalRef.current = true;
|
|
228
|
+
clearTimeoutTimer();
|
|
229
|
+
flush();
|
|
230
|
+
dispatch({
|
|
231
|
+
type: "event",
|
|
232
|
+
event: {
|
|
233
|
+
event: "flow_failed",
|
|
234
|
+
code: "send_failed",
|
|
235
|
+
message: sendError instanceof Error ? sendError.message : String(sendError),
|
|
236
|
+
retryable: true
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
return true;
|
|
241
|
+
},
|
|
242
|
+
[cancelFlush, clearTimeoutTimer, dispatch, flush, timeoutMs]
|
|
243
|
+
);
|
|
244
|
+
const stop = useCallback(async () => {
|
|
245
|
+
const current = stateRef.current;
|
|
246
|
+
if (current.status !== "pending" && current.status !== "streaming") {
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
terminalRef.current = true;
|
|
250
|
+
clearTimeoutTimer();
|
|
251
|
+
flush();
|
|
252
|
+
dispatch({ type: "stop", at: Date.now() });
|
|
253
|
+
await transportRef.current.stop?.().catch(() => void 0);
|
|
254
|
+
}, [clearTimeoutTimer, dispatch, flush]);
|
|
255
|
+
const reset = useCallback(() => {
|
|
256
|
+
terminalRef.current = false;
|
|
257
|
+
cancelFlush();
|
|
258
|
+
clearTimeoutTimer();
|
|
259
|
+
dispatch({ type: "reset" });
|
|
260
|
+
}, [cancelFlush, clearTimeoutTimer, dispatch]);
|
|
261
|
+
return {
|
|
262
|
+
...state,
|
|
263
|
+
live: state.status === "idle" ? null : state,
|
|
264
|
+
send,
|
|
265
|
+
stop,
|
|
266
|
+
reset
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export { initialLiveTurnState, liveTurnReducer, useChatStream };
|
|
271
|
+
//# sourceMappingURL=chunk-6GNLY2EB.js.map
|
|
272
|
+
//# sourceMappingURL=chunk-6GNLY2EB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-core/liveTurnReducer.ts","../src/components/chat-core/useChatStream.ts"],"names":[],"mappings":"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","file":"chunk-6GNLY2EB.js","sourcesContent":["import type { ChatStreamEvent } from './events'\nimport type { ChatError } from './types'\n\nexport type LiveTurnStatus = 'idle' | 'pending' | 'streaming' | 'interrupted' | 'done' | 'error'\n\nexport interface LiveNodeState {\n id: string\n label?: string\n status: 'pending' | 'running' | 'done' | 'failed' | 'interrupted'\n}\n\nexport interface LiveToolCall {\n id: string\n name: string\n title?: string\n status: 'running' | 'done' | 'failed'\n args?: string\n result?: string\n durationMs?: number\n}\n\n/**\n * Ephemeral state of the in-flight assistant turn. `text`/`reasoning` are\n * `null` until the first delta (study's \"not streaming yet\" sentinel);\n * `interrupted` is a resumable pause (HITL) unless `stopped` marks a user\n * cancel; `stopped`/`done`/`error` are terminal for event intake.\n */\nexport interface LiveTurnState {\n status: LiveTurnStatus\n text: string | null\n reasoning: string | null\n toolCalls: LiveToolCall[]\n nodes: LiveNodeState[]\n error: ChatError | null\n interruptPayload: unknown | null\n runId: string | null\n stopped: boolean\n startedAt: number | null\n finishedAt: number | null\n}\n\nexport type LiveTurnAction =\n | { type: 'send'; at: number }\n | { type: 'event'; event: ChatStreamEvent; at?: number }\n | { type: 'stop'; at: number }\n | { type: 'reset' }\n\nexport const initialLiveTurnState: LiveTurnState = {\n status: 'idle',\n text: null,\n reasoning: null,\n toolCalls: [],\n nodes: [],\n error: null,\n interruptPayload: null,\n runId: null,\n stopped: false,\n startedAt: null,\n finishedAt: null,\n}\n\nfunction isTerminal(state: LiveTurnState): boolean {\n return state.status === 'done' || state.status === 'error' || state.stopped\n}\n\nfunction upsertNode(nodes: LiveNodeState[], id: string, label: string | undefined, status: LiveNodeState['status']): LiveNodeState[] {\n const index = nodes.findIndex((node) => node.id === id)\n if (index === -1) {\n return [...nodes, { id, label, status }]\n }\n const next = nodes.slice()\n const current = next[index]!\n next[index] = { ...current, label: label ?? current.label, status }\n return next\n}\n\nfunction upsertToolCall(state: LiveTurnState, event: Extract<ChatStreamEvent, { event: 'tool_call' }>): LiveToolCall[] {\n const index = state.toolCalls.findIndex((call) => call.id === event.id)\n const merged = {\n id: event.id,\n name: event.name,\n title: event.title,\n status: event.status,\n args: event.args,\n result: event.result,\n durationMs: event.durationMs,\n }\n if (index === -1) {\n return [...state.toolCalls, merged]\n }\n const next = state.toolCalls.slice()\n const current = next[index]!\n next[index] = {\n ...current,\n ...merged,\n title: merged.title ?? current.title,\n args: merged.args ?? current.args,\n result: merged.result ?? current.result,\n durationMs: merged.durationMs ?? current.durationMs,\n }\n return next\n}\n\n/**\n * Pure state machine for one streamed assistant turn — the fixture-testable\n * core behind `useChatStream` (same role career's `chatFlow.ts` plays for\n * `FlowStatusCard`). Unknown event names are ignored (additive contract).\n */\nexport function liveTurnReducer(state: LiveTurnState, action: LiveTurnAction): LiveTurnState {\n switch (action.type) {\n case 'send':\n return { ...initialLiveTurnState, status: 'pending', startedAt: action.at }\n case 'stop': {\n if (state.status !== 'pending' && state.status !== 'streaming') {\n return state\n }\n return { ...state, status: 'interrupted', stopped: true, finishedAt: action.at }\n }\n case 'reset':\n return initialLiveTurnState\n case 'event': {\n const { event, at = Date.now() } = action\n if (state.status === 'idle' || isTerminal(state)) {\n return state\n }\n if (event.run_id !== undefined && state.runId !== null && event.run_id !== state.runId) {\n return state\n }\n switch (event.event) {\n case 'flow_started':\n return {\n ...state,\n runId: event.run_id ?? state.runId,\n nodes:\n event.steps !== undefined && event.steps.length > 0\n ? event.steps.map((step): LiveNodeState => ({ id: step.id, label: step.label, status: 'pending' }))\n : state.nodes,\n }\n case 'node_started':\n return { ...state, nodes: upsertNode(state.nodes, event.node, event.label, 'running') }\n case 'node_finished':\n return { ...state, nodes: upsertNode(state.nodes, event.node, event.label, event.outcome) }\n case 'delta': {\n const streaming = { ...state, status: 'streaming' as const }\n if (event.kind === 'reasoning') {\n return { ...streaming, reasoning: (state.reasoning ?? '') + event.text }\n }\n return { ...streaming, text: (state.text ?? '') + event.text }\n }\n case 'tool_call':\n return { ...state, toolCalls: upsertToolCall(state, event) }\n case 'interrupt':\n return { ...state, status: 'interrupted', interruptPayload: event.payload, finishedAt: at }\n case 'flow_finished':\n return { ...state, status: 'done', finishedAt: at }\n case 'flow_failed':\n return {\n ...state,\n status: 'error',\n error: { code: event.code, message: event.message, retryable: event.retryable },\n finishedAt: at,\n }\n default:\n return state\n }\n }\n default:\n return state\n }\n}\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\nimport type { ChatStreamEvent } from './events'\nimport { initialLiveTurnState, liveTurnReducer, type LiveTurnAction, type LiveTurnState } from './liveTurnReducer'\n\nexport interface ChatStreamTransport {\n /** Enqueue/send a user turn. Resolves when accepted; errors surface as a failed turn. */\n send(input: { text: string }): Promise<void>\n /** Subscribe to the session's stream events; returns an unsubscribe function. */\n subscribe(handlers: { onEvent: (event: ChatStreamEvent) => void }): () => void\n /** Best-effort cancel of the in-flight turn (backend stop endpoint). */\n stop?(): Promise<void>\n}\n\nexport interface UseChatStreamOptions {\n transport: ChatStreamTransport\n /** Delta coalescing window. Default 33 ms (study's rAF-scale budget). */\n flushMs?: number\n /** Watchdog for a turn that never terminates. Default 90 000 ms. */\n timeoutMs?: number\n}\n\nexport interface UseChatStreamResult extends LiveTurnState {\n /** The live turn, or `null` when idle — hand to the transcript's `live` slot. */\n live: LiveTurnState | null\n /** Returns false and does nothing for blank input or while a turn is active. */\n send(text: string): Promise<boolean>\n stop(): Promise<void>\n reset(): void\n}\n\n/**\n * Transport-injected live-turn state machine for chat surfaces: send →\n * pending → streaming (coalesced text/reasoning deltas, tool calls, node\n * progress) → done / error / interrupted. Persists nothing — the app owns\n * sessions, messages and the branching endpoints; after a terminal state\n * the app refetches and calls `reset()`.\n */\nexport function useChatStream(options: UseChatStreamOptions): UseChatStreamResult {\n const { transport, flushMs = 33, timeoutMs = 90_000 } = options\n const [state, setState] = useState<LiveTurnState>(initialLiveTurnState)\n const stateRef = useRef(state)\n const dispatch = useCallback((action: LiveTurnAction) => {\n const next = liveTurnReducer(stateRef.current, action)\n stateRef.current = next\n setState(next)\n }, [])\n\n const transportRef = useRef(transport)\n transportRef.current = transport\n\n const textBufferRef = useRef('')\n const reasoningBufferRef = useRef('')\n const flushTimerRef = useRef<number | null>(null)\n const timeoutTimerRef = useRef<number | null>(null)\n const terminalRef = useRef(false)\n\n const cancelFlush = useCallback(() => {\n if (flushTimerRef.current !== null) {\n window.clearTimeout(flushTimerRef.current)\n flushTimerRef.current = null\n }\n }, [])\n\n const flush = useCallback(() => {\n cancelFlush()\n const text = textBufferRef.current\n const reasoning = reasoningBufferRef.current\n textBufferRef.current = ''\n reasoningBufferRef.current = ''\n if (text !== '') {\n dispatch({ type: 'event', event: { event: 'delta', text, kind: 'text' } })\n }\n if (reasoning !== '') {\n dispatch({ type: 'event', event: { event: 'delta', text: reasoning, kind: 'reasoning' } })\n }\n }, [cancelFlush, dispatch])\n\n const scheduleFlush = useCallback(() => {\n if (flushTimerRef.current !== null) {\n return\n }\n flushTimerRef.current = window.setTimeout(() => {\n flushTimerRef.current = null\n flush()\n }, flushMs)\n }, [flush, flushMs])\n\n const clearTimeoutTimer = useCallback(() => {\n if (timeoutTimerRef.current !== null) {\n window.clearTimeout(timeoutTimerRef.current)\n timeoutTimerRef.current = null\n }\n }, [])\n\n useEffect(() => {\n const unsubscribe = transportRef.current.subscribe({\n onEvent: (event) => {\n if (terminalRef.current) {\n return\n }\n if (event.event === 'delta') {\n if (event.kind === 'reasoning') {\n reasoningBufferRef.current += event.text\n } else {\n textBufferRef.current += event.text\n }\n scheduleFlush()\n return\n }\n flush()\n if (event.event === 'flow_finished' || event.event === 'flow_failed') {\n terminalRef.current = true\n clearTimeoutTimer()\n }\n dispatch({ type: 'event', event })\n },\n })\n return unsubscribe\n }, [scheduleFlush, flush, clearTimeoutTimer, dispatch])\n\n useEffect(\n () => () => {\n cancelFlush()\n clearTimeoutTimer()\n },\n [cancelFlush, clearTimeoutTimer],\n )\n\n const send = useCallback(\n async (text: string) => {\n const trimmed = text.trim()\n const active = stateRef.current.status === 'pending' || stateRef.current.status === 'streaming'\n if (trimmed === '' || active) {\n return false\n }\n terminalRef.current = false\n textBufferRef.current = ''\n reasoningBufferRef.current = ''\n cancelFlush()\n clearTimeoutTimer()\n dispatch({ type: 'send', at: Date.now() })\n timeoutTimerRef.current = window.setTimeout(() => {\n if (terminalRef.current) {\n return\n }\n terminalRef.current = true\n flush()\n dispatch({\n type: 'event',\n event: { event: 'flow_failed', code: 'timeout', message: 'Chat turn timed out', retryable: true },\n })\n }, timeoutMs)\n try {\n await transportRef.current.send({ text: trimmed })\n } catch (sendError) {\n if (terminalRef.current) {\n return true\n }\n terminalRef.current = true\n clearTimeoutTimer()\n flush()\n dispatch({\n type: 'event',\n event: {\n event: 'flow_failed',\n code: 'send_failed',\n message: sendError instanceof Error ? sendError.message : String(sendError),\n retryable: true,\n },\n })\n }\n return true\n },\n [cancelFlush, clearTimeoutTimer, dispatch, flush, timeoutMs],\n )\n\n const stop = useCallback(async () => {\n const current = stateRef.current\n if (current.status !== 'pending' && current.status !== 'streaming') {\n return\n }\n terminalRef.current = true\n clearTimeoutTimer()\n flush()\n dispatch({ type: 'stop', at: Date.now() })\n await transportRef.current.stop?.().catch(() => undefined)\n }, [clearTimeoutTimer, dispatch, flush])\n\n const reset = useCallback(() => {\n terminalRef.current = false\n cancelFlush()\n clearTimeoutTimer()\n dispatch({ type: 'reset' })\n }, [cancelFlush, clearTimeoutTimer, dispatch])\n\n return {\n ...state,\n live: state.status === 'idle' ? null : state,\n send,\n stop,\n reset,\n }\n}\n"]}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { cn } from './chunk-BLJWR4ZV.js';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { Square, ArrowUp } from 'lucide-react';
|
|
4
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function isIMEComposition(event) {
|
|
7
|
+
return event.nativeEvent.isComposing || event.keyCode === 229;
|
|
8
|
+
}
|
|
9
|
+
var ChatComposer = React.forwardRef(
|
|
10
|
+
function ChatComposer2({
|
|
11
|
+
value,
|
|
12
|
+
onValueChange,
|
|
13
|
+
onSubmit,
|
|
14
|
+
sending = false,
|
|
15
|
+
onStop,
|
|
16
|
+
disabled = false,
|
|
17
|
+
placeholder,
|
|
18
|
+
ariaLabel,
|
|
19
|
+
maxRows = 8,
|
|
20
|
+
toolbarStart,
|
|
21
|
+
toolbarEnd,
|
|
22
|
+
attachments,
|
|
23
|
+
suggestions,
|
|
24
|
+
onAttachFiles,
|
|
25
|
+
labels,
|
|
26
|
+
icons,
|
|
27
|
+
textareaRef,
|
|
28
|
+
className
|
|
29
|
+
}, ref) {
|
|
30
|
+
const internalTextareaRef = React.useRef(null);
|
|
31
|
+
const [dragging, setDragging] = React.useState(false);
|
|
32
|
+
const dragDepth = React.useRef(0);
|
|
33
|
+
const setTextarea = (element) => {
|
|
34
|
+
internalTextareaRef.current = element;
|
|
35
|
+
if (typeof textareaRef === "function") {
|
|
36
|
+
textareaRef(element);
|
|
37
|
+
} else if (textareaRef && typeof textareaRef === "object") {
|
|
38
|
+
textareaRef.current = element;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
React.useLayoutEffect(() => {
|
|
42
|
+
const element = internalTextareaRef.current;
|
|
43
|
+
if (element === null) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
element.style.height = "auto";
|
|
47
|
+
const cap = maxRows * 22;
|
|
48
|
+
element.style.height = element.scrollHeight > 0 ? `${Math.min(element.scrollHeight, cap)}px` : "auto";
|
|
49
|
+
element.style.overflowY = element.scrollHeight > cap ? "auto" : "hidden";
|
|
50
|
+
}, [value, maxRows]);
|
|
51
|
+
const submit = () => {
|
|
52
|
+
if (disabled || sending) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
onSubmit();
|
|
56
|
+
};
|
|
57
|
+
const onKeyDown = (event) => {
|
|
58
|
+
if (event.key !== "Enter" || event.shiftKey) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (isIMEComposition(event)) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
submit();
|
|
66
|
+
};
|
|
67
|
+
const emitFiles = (fileList) => {
|
|
68
|
+
const files = fileList ? Array.from(fileList) : [];
|
|
69
|
+
if (files.length > 0) {
|
|
70
|
+
onAttachFiles?.(files);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const dragCounterHandlers = {
|
|
74
|
+
onDragEnter: (event) => {
|
|
75
|
+
if (onAttachFiles === void 0 || !event.dataTransfer.types.includes("Files")) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
dragDepth.current += 1;
|
|
80
|
+
setDragging(true);
|
|
81
|
+
},
|
|
82
|
+
onDragOver: (event) => {
|
|
83
|
+
if (onAttachFiles === void 0 || !event.dataTransfer.types.includes("Files")) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
},
|
|
88
|
+
onDragLeave: () => {
|
|
89
|
+
if (dragDepth.current > 0) {
|
|
90
|
+
dragDepth.current -= 1;
|
|
91
|
+
if (dragDepth.current === 0) {
|
|
92
|
+
setDragging(false);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
onDrop: (event) => {
|
|
97
|
+
if (onAttachFiles === void 0) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
event.preventDefault();
|
|
101
|
+
dragDepth.current = 0;
|
|
102
|
+
setDragging(false);
|
|
103
|
+
emitFiles(event.dataTransfer.files);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
const SendIcon = icons?.send ?? ArrowUp;
|
|
107
|
+
const StopIcon = icons?.stop ?? Square;
|
|
108
|
+
const showStop = sending && onStop !== void 0;
|
|
109
|
+
return /* @__PURE__ */ jsxs(
|
|
110
|
+
"form",
|
|
111
|
+
{
|
|
112
|
+
ref,
|
|
113
|
+
"data-as": "chat-composer",
|
|
114
|
+
"data-sending": sending,
|
|
115
|
+
onSubmit: (event) => {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
submit();
|
|
118
|
+
},
|
|
119
|
+
className: cn("flex w-full flex-col gap-2", className),
|
|
120
|
+
...dragCounterHandlers,
|
|
121
|
+
children: [
|
|
122
|
+
suggestions ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: suggestions }) : null,
|
|
123
|
+
attachments ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: attachments }) : null,
|
|
124
|
+
/* @__PURE__ */ jsxs(
|
|
125
|
+
"div",
|
|
126
|
+
{
|
|
127
|
+
"data-dragging": dragging || void 0,
|
|
128
|
+
className: cn(
|
|
129
|
+
"flex items-end gap-1.5 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 transition-colors focus-within:border-[var(--as-primary)]",
|
|
130
|
+
dragging && "border-dashed border-[var(--as-primary)] bg-[var(--as-primary)]/5",
|
|
131
|
+
disabled && "opacity-50"
|
|
132
|
+
),
|
|
133
|
+
children: [
|
|
134
|
+
toolbarStart ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5 pb-0.5", children: toolbarStart }) : null,
|
|
135
|
+
/* @__PURE__ */ jsx(
|
|
136
|
+
"textarea",
|
|
137
|
+
{
|
|
138
|
+
ref: setTextarea,
|
|
139
|
+
value,
|
|
140
|
+
rows: 1,
|
|
141
|
+
disabled,
|
|
142
|
+
placeholder: placeholder ?? labels?.placeholder ?? "Send a message\u2026",
|
|
143
|
+
"aria-label": ariaLabel ?? labels?.ariaLabel ?? "Message",
|
|
144
|
+
onChange: (event) => onValueChange(event.target.value),
|
|
145
|
+
onKeyDown,
|
|
146
|
+
onPaste: (event) => emitFiles(event.clipboardData.files),
|
|
147
|
+
className: "max-h-40 min-h-[1.5rem] w-full flex-1 resize-none bg-transparent px-1 py-1 text-sm leading-relaxed text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-none disabled:cursor-not-allowed"
|
|
148
|
+
}
|
|
149
|
+
),
|
|
150
|
+
toolbarEnd ? /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-0.5 pb-0.5", children: toolbarEnd }) : null,
|
|
151
|
+
showStop ? /* @__PURE__ */ jsx(
|
|
152
|
+
"button",
|
|
153
|
+
{
|
|
154
|
+
type: "button",
|
|
155
|
+
"aria-label": labels?.stop ?? "Stop generating",
|
|
156
|
+
title: labels?.stop ?? "Stop generating",
|
|
157
|
+
onClick: onStop,
|
|
158
|
+
className: "mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
159
|
+
children: /* @__PURE__ */ jsx(StopIcon, { className: "size-3.5", "aria-hidden": true })
|
|
160
|
+
}
|
|
161
|
+
) : /* @__PURE__ */ jsx(
|
|
162
|
+
"button",
|
|
163
|
+
{
|
|
164
|
+
type: "submit",
|
|
165
|
+
"aria-label": labels?.send ?? "Send message",
|
|
166
|
+
title: labels?.send ?? "Send message",
|
|
167
|
+
disabled: disabled || value.trim() === "" || sending,
|
|
168
|
+
className: "mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] transition-opacity hover:opacity-90 disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]",
|
|
169
|
+
children: /* @__PURE__ */ jsx(SendIcon, { className: "size-4", "aria-hidden": true })
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
]
|
|
173
|
+
}
|
|
174
|
+
),
|
|
175
|
+
dragging && onAttachFiles ? /* @__PURE__ */ jsx("p", { className: "text-xs text-[var(--as-muted-fg)]", role: "status", children: labels?.dropFiles ?? "Drop files to attach" }) : null
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
export { ChatComposer };
|
|
183
|
+
//# sourceMappingURL=chunk-743FJDRA.js.map
|
|
184
|
+
//# sourceMappingURL=chunk-743FJDRA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/chat-composer/ChatComposer.tsx"],"names":["ChatComposer"],"mappings":"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","file":"chunk-743FJDRA.js","sourcesContent":["import * as React from 'react'\nimport { ArrowUp, Square } from 'lucide-react'\nimport type { LucideIcon } from 'lucide-react'\n\nimport { cn } from '../../lib/utils'\n\nexport interface ChatComposerLabels {\n send: string\n stop: string\n placeholder: string\n ariaLabel: string\n dropFiles: string\n}\n\nexport interface ChatComposerIcons {\n send: LucideIcon\n stop: LucideIcon\n}\n\nexport interface ChatComposerProps {\n value: string\n onValueChange: (value: string) => void\n /** Enter / Send — IME-safe (composition and keyCode 229 are ignored). */\n onSubmit: () => void\n /** A turn is in flight — renders the stop control when `onStop` is provided. */\n sending?: boolean\n onStop?: () => void\n disabled?: boolean\n placeholder?: string\n ariaLabel?: string\n /** Auto-grow cap before the textarea scrolls. Default 8. */\n maxRows?: number\n /** Leading slot inside the input row (attach menu, equation, draw…). */\n toolbarStart?: React.ReactNode\n /** Trailing slot inside the input row (dictation, tools…). */\n toolbarEnd?: React.ReactNode\n /** Attachment rail above the input (usually `FileQueue` / chips). */\n attachments?: React.ReactNode\n /** Suggestion chips / context strip above everything. */\n suggestions?: React.ReactNode\n /** Drag-drop + paste wiring for file attachments. */\n onAttachFiles?: (files: File[]) => void\n labels?: Partial<ChatComposerLabels>\n icons?: Partial<ChatComposerIcons>\n textareaRef?: React.Ref<HTMLTextAreaElement>\n className?: string\n}\n\nfunction isIMEComposition(event: React.KeyboardEvent<HTMLTextAreaElement>): boolean {\n return event.nativeEvent.isComposing || event.keyCode === 229\n}\n\n/**\n * The family chat input: auto-growing IME-safe textarea, send/stop\n * states, attachment/dictation/toolbar slots, drag-drop + paste file\n * wiring. Presentational + controlled; transports stay app-side.\n */\nexport const ChatComposer = React.forwardRef<HTMLFormElement, ChatComposerProps>(\n function ChatComposer(\n {\n value,\n onValueChange,\n onSubmit,\n sending = false,\n onStop,\n disabled = false,\n placeholder,\n ariaLabel,\n maxRows = 8,\n toolbarStart,\n toolbarEnd,\n attachments,\n suggestions,\n onAttachFiles,\n labels,\n icons,\n textareaRef,\n className,\n },\n ref,\n ) {\n const internalTextareaRef = React.useRef<HTMLTextAreaElement | null>(null)\n const [dragging, setDragging] = React.useState(false)\n const dragDepth = React.useRef(0)\n\n const setTextarea = (element: HTMLTextAreaElement | null) => {\n internalTextareaRef.current = element\n if (typeof textareaRef === 'function') {\n textareaRef(element)\n } else if (textareaRef && typeof textareaRef === 'object') {\n ;(textareaRef as React.MutableRefObject<HTMLTextAreaElement | null>).current = element\n }\n }\n\n React.useLayoutEffect(() => {\n const element = internalTextareaRef.current\n if (element === null) {\n return\n }\n element.style.height = 'auto'\n const cap = maxRows * 22\n element.style.height = element.scrollHeight > 0 ? `${Math.min(element.scrollHeight, cap)}px` : 'auto'\n element.style.overflowY = element.scrollHeight > cap ? 'auto' : 'hidden'\n }, [value, maxRows])\n\n const submit = () => {\n if (disabled || sending) {\n return\n }\n onSubmit()\n }\n\n const onKeyDown: React.KeyboardEventHandler<HTMLTextAreaElement> = (event) => {\n if (event.key !== 'Enter' || event.shiftKey) {\n return\n }\n if (isIMEComposition(event)) {\n return\n }\n event.preventDefault()\n submit()\n }\n\n const emitFiles = (fileList: FileList | null | undefined) => {\n const files = fileList ? Array.from(fileList) : []\n if (files.length > 0) {\n onAttachFiles?.(files)\n }\n }\n\n const dragCounterHandlers = {\n onDragEnter: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n dragDepth.current += 1\n setDragging(true)\n },\n onDragOver: (event: React.DragEvent) => {\n if (onAttachFiles === undefined || !event.dataTransfer.types.includes('Files')) {\n return\n }\n event.preventDefault()\n },\n onDragLeave: () => {\n if (dragDepth.current > 0) {\n dragDepth.current -= 1\n if (dragDepth.current === 0) {\n setDragging(false)\n }\n }\n },\n onDrop: (event: React.DragEvent) => {\n if (onAttachFiles === undefined) {\n return\n }\n event.preventDefault()\n dragDepth.current = 0\n setDragging(false)\n emitFiles(event.dataTransfer.files)\n },\n }\n\n const SendIcon = icons?.send ?? ArrowUp\n const StopIcon = icons?.stop ?? Square\n const showStop = sending && onStop !== undefined\n\n return (\n <form\n ref={ref}\n data-as=\"chat-composer\"\n data-sending={sending}\n onSubmit={(event) => {\n event.preventDefault()\n submit()\n }}\n className={cn('flex w-full flex-col gap-2', className)}\n {...dragCounterHandlers}\n >\n {suggestions ? <div className=\"flex flex-wrap gap-1.5\">{suggestions}</div> : null}\n {attachments ? <div className=\"flex flex-wrap gap-1.5\">{attachments}</div> : null}\n <div\n data-dragging={dragging || undefined}\n className={cn(\n 'flex items-end gap-1.5 rounded-[var(--as-radius-lg)] border border-[var(--as-border)] bg-[var(--as-surface)] px-2 py-1.5 transition-colors focus-within:border-[var(--as-primary)]',\n dragging && 'border-dashed border-[var(--as-primary)] bg-[var(--as-primary)]/5',\n disabled && 'opacity-50',\n )}\n >\n {toolbarStart ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarStart}</div> : null}\n <textarea\n ref={setTextarea}\n value={value}\n rows={1}\n disabled={disabled}\n placeholder={placeholder ?? labels?.placeholder ?? 'Send a message…'}\n aria-label={ariaLabel ?? labels?.ariaLabel ?? 'Message'}\n onChange={(event) => onValueChange(event.target.value)}\n onKeyDown={onKeyDown}\n onPaste={(event) => emitFiles(event.clipboardData.files)}\n className=\"max-h-40 min-h-[1.5rem] w-full flex-1 resize-none bg-transparent px-1 py-1 text-sm leading-relaxed text-[var(--as-fg)] placeholder:text-[var(--as-muted-fg)] focus-visible:outline-none disabled:cursor-not-allowed\"\n />\n {toolbarEnd ? <div className=\"flex shrink-0 items-center gap-0.5 pb-0.5\">{toolbarEnd}</div> : null}\n {showStop ? (\n <button\n type=\"button\"\n aria-label={labels?.stop ?? 'Stop generating'}\n title={labels?.stop ?? 'Stop generating'}\n onClick={onStop}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full border border-[var(--as-border)] bg-[var(--as-surface-raised)] text-[var(--as-fg)] transition-colors hover:bg-[var(--as-secondary)] focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <StopIcon className=\"size-3.5\" aria-hidden />\n </button>\n ) : (\n <button\n type=\"submit\"\n aria-label={labels?.send ?? 'Send message'}\n title={labels?.send ?? 'Send message'}\n disabled={disabled || value.trim() === '' || sending}\n className=\"mb-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-[var(--as-primary)] text-[var(--as-primary-fg)] transition-opacity hover:opacity-90 disabled:pointer-events-none disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-[var(--as-focus-ring)]\"\n >\n <SendIcon className=\"size-4\" aria-hidden />\n </button>\n )}\n </div>\n {dragging && onAttachFiles ? (\n <p className=\"text-xs text-[var(--as-muted-fg)]\" role=\"status\">\n {labels?.dropFiles ?? 'Drop files to attach'}\n </p>\n ) : null}\n </form>\n )\n },\n)\n"]}
|